864 lines
30 KiB
JavaScript
864 lines
30 KiB
JavaScript
// === GÉNÉRATEURS AUTOMATIQUES D'EXERCICES ===
|
||
|
||
// === GÉNÉRATEUR DE VOCABULAIRE ===
|
||
class VocabularyGenerator {
|
||
async generate(parsedContent, options = {}) {
|
||
console.log('📚 VocabularyGenerator - Génération exercices vocabulaire');
|
||
|
||
const exercises = [];
|
||
const vocabulary = parsedContent.vocabulary || [];
|
||
|
||
vocabulary.forEach((word, index) => {
|
||
const exercise = this.createVocabularyExercise(word, index, options);
|
||
exercises.push(exercise);
|
||
});
|
||
|
||
// Générer des exercices supplémentaires si demandé
|
||
if (options.generateVariations) {
|
||
exercises.push(...this.generateVariations(vocabulary, options));
|
||
}
|
||
|
||
return exercises;
|
||
}
|
||
|
||
createVocabularyExercise(word, index, options) {
|
||
return {
|
||
id: `vocab_${index + 1}`,
|
||
type: 'vocabulary',
|
||
difficulty: this.inferDifficulty(word),
|
||
category: word.category || 'general',
|
||
|
||
content: {
|
||
english: word.english,
|
||
french: word.french,
|
||
phonetic: word.phonetic || this.generatePhonetic(word.english),
|
||
context: word.context || word.category || '',
|
||
tags: this.generateTags(word)
|
||
},
|
||
|
||
media: {
|
||
image: word.image || null,
|
||
audio: word.audio || null,
|
||
icon: this.getIconForWord(word.english, word.category)
|
||
},
|
||
|
||
pedagogy: {
|
||
learningObjective: `Apprendre le mot "${word.english}"`,
|
||
prerequisites: [],
|
||
followUp: this.suggestFollowUp(word),
|
||
grammarFocus: 'vocabulary'
|
||
},
|
||
|
||
interaction: {
|
||
type: options.interactionType || this.selectBestInteraction(word),
|
||
validation: 'exact',
|
||
hints: this.generateHints(word),
|
||
feedback: {
|
||
correct: `Parfait ! "${word.english}" = "${word.french}" ${this.getIconForWord(word.english)}`,
|
||
incorrect: `Non, "${word.english}" signifie "${word.french}"`
|
||
},
|
||
alternatives: this.generateAlternatives(word)
|
||
}
|
||
};
|
||
}
|
||
|
||
inferDifficulty(word) {
|
||
const length = word.english.length;
|
||
const complexity = this.calculateComplexity(word.english);
|
||
|
||
if (length <= 4 && complexity < 2) return 'easy';
|
||
if (length <= 8 && complexity < 3) return 'medium';
|
||
return 'hard';
|
||
}
|
||
|
||
calculateComplexity(word) {
|
||
let complexity = 0;
|
||
if (word.includes('th')) complexity++;
|
||
if (word.includes('gh')) complexity++;
|
||
if (word.match(/[aeiou]{2,}/)) complexity++;
|
||
if (word.split('').some(c => 'xyz'.includes(c))) complexity++;
|
||
return complexity;
|
||
}
|
||
|
||
generateTags(word) {
|
||
const tags = [word.category || 'general'];
|
||
|
||
// Tags basés sur la longueur
|
||
if (word.english.length <= 4) tags.push('short');
|
||
else if (word.english.length >= 8) tags.push('long');
|
||
|
||
// Tags basés sur la catégorie
|
||
if (word.category) {
|
||
tags.push(word.category);
|
||
if (['cat', 'dog', 'bird'].includes(word.english)) tags.push('pet');
|
||
if (['red', 'blue', 'green'].includes(word.english)) tags.push('color');
|
||
}
|
||
|
||
return tags;
|
||
}
|
||
|
||
getIconForWord(word, category) {
|
||
const wordIcons = {
|
||
cat: '🐱', dog: '🐕', bird: '🐦', fish: '🐠',
|
||
apple: '🍎', banana: '🍌', orange: '🍊',
|
||
car: '🚗', house: '🏠', school: '🏫',
|
||
book: '📚', pen: '✏️', pencil: '✏️'
|
||
};
|
||
|
||
if (wordIcons[word.toLowerCase()]) {
|
||
return wordIcons[word.toLowerCase()];
|
||
}
|
||
|
||
const categoryIcons = {
|
||
animals: '🐾', food: '🍎', transport: '🚗',
|
||
family: '👨👩👧👦', colors: '🎨', numbers: '🔢'
|
||
};
|
||
|
||
return categoryIcons[category] || '📝';
|
||
}
|
||
|
||
selectBestInteraction(word) {
|
||
// Sélection intelligente du type d'interaction
|
||
if (word.category === 'actions') return 'gesture';
|
||
if (word.english.length > 8) return 'type';
|
||
if (word.image) return 'drag_drop';
|
||
return 'click';
|
||
}
|
||
|
||
generateHints(word) {
|
||
const hints = [];
|
||
|
||
// Hint basé sur la catégorie
|
||
if (word.category === 'animals') hints.push("C'est un animal");
|
||
if (word.category === 'food') hints.push("On peut le manger");
|
||
if (word.category === 'colors') hints.push("C'est une couleur");
|
||
|
||
// Hint basé sur la longueur
|
||
hints.push(`Ce mot a ${word.english.length} lettres`);
|
||
|
||
// Hint basé sur la première lettre
|
||
hints.push(`Commence par la lettre "${word.english[0].toUpperCase()}"`);
|
||
|
||
return hints.slice(0, 3); // Max 3 hints
|
||
}
|
||
|
||
generateAlternatives(word) {
|
||
// Générer des alternatives plausibles pour QCM
|
||
const alternatives = [];
|
||
|
||
// Pour l'instant, alternatives génériques
|
||
// Dans une vraie implémentation, on utiliserait une base de données
|
||
const commonWords = {
|
||
animals: ['chien', 'oiseau', 'poisson', 'lapin'],
|
||
food: ['pomme', 'banane', 'orange', 'pain'],
|
||
colors: ['rouge', 'bleu', 'vert', 'jaune'],
|
||
family: ['mère', 'père', 'frère', 'sœur']
|
||
};
|
||
|
||
const categoryAlts = commonWords[word.category] || ['mot1', 'mot2', 'mot3'];
|
||
|
||
// Prendre 3 alternatives qui ne sont pas la bonne réponse
|
||
return categoryAlts.filter(alt => alt !== word.french).slice(0, 3);
|
||
}
|
||
|
||
suggestFollowUp(word) {
|
||
const followUp = [];
|
||
|
||
if (word.category === 'animals') {
|
||
followUp.push('pets', 'farm_animals', 'wild_animals');
|
||
} else if (word.category === 'family') {
|
||
followUp.push('family_relationships', 'family_activities');
|
||
}
|
||
|
||
return followUp;
|
||
}
|
||
|
||
generateVariations(vocabulary, options) {
|
||
const variations = [];
|
||
|
||
// Générer des exercices de synonymes/antonymes
|
||
// Générer des exercices de catégorisation
|
||
// Générer des exercices de construction de phrases
|
||
|
||
return variations; // Pour l'instant vide
|
||
}
|
||
|
||
generatePhonetic(word) {
|
||
// Génération basique de phonétique
|
||
// Dans une vraie implémentation, utiliser une API de phonétique
|
||
const basicPhonetic = {
|
||
cat: '/kæt/', dog: '/dɔg/', bird: '/bɜrd/',
|
||
apple: '/ˈæpəl/', house: '/haʊs/', book: '/bʊk/'
|
||
};
|
||
|
||
return basicPhonetic[word.toLowerCase()] || `/${word}/`;
|
||
}
|
||
}
|
||
|
||
// === GÉNÉRATEUR DE PHRASES ===
|
||
class SentenceGenerator {
|
||
async generate(parsedContent, options = {}) {
|
||
console.log('📖 SentenceGenerator - Génération exercices phrases');
|
||
|
||
const exercises = [];
|
||
const sentences = parsedContent.sentences || [];
|
||
|
||
sentences.forEach((sentence, index) => {
|
||
const exercise = this.createSentenceExercise(sentence, index, options);
|
||
exercises.push(exercise);
|
||
});
|
||
|
||
return exercises;
|
||
}
|
||
|
||
createSentenceExercise(sentence, index, options) {
|
||
return {
|
||
id: `sent_${index + 1}`,
|
||
type: 'sentence',
|
||
difficulty: this.inferSentenceDifficulty(sentence),
|
||
category: sentence.category || 'general',
|
||
|
||
content: {
|
||
english: sentence.english,
|
||
french: sentence.french || this.suggestTranslation(sentence.english),
|
||
structure: this.analyzeSentenceStructure(sentence.english),
|
||
context: sentence.context || 'general',
|
||
tags: this.generateSentenceTags(sentence)
|
||
},
|
||
|
||
media: {
|
||
icon: this.getSentenceIcon(sentence)
|
||
},
|
||
|
||
pedagogy: {
|
||
learningObjective: `Comprendre et construire la phrase "${sentence.english}"`,
|
||
prerequisites: this.extractPrerequisites(sentence.english),
|
||
followUp: ['complex_sentences', 'questions'],
|
||
grammarFocus: this.identifyGrammarFocus(sentence.english)
|
||
},
|
||
|
||
interaction: {
|
||
type: 'build_sentence',
|
||
validation: 'word_order',
|
||
hints: this.generateSentenceHints(sentence),
|
||
feedback: {
|
||
correct: `Excellent ! Tu as construit la phrase correctement ! ✨`,
|
||
incorrect: `Essaie de remettre les mots dans le bon ordre`
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
inferSentenceDifficulty(sentence) {
|
||
const wordCount = sentence.english.split(' ').length;
|
||
const structure = sentence.structure || {};
|
||
|
||
if (wordCount <= 4 && !structure.hasQuestion) return 'easy';
|
||
if (wordCount <= 8) return 'medium';
|
||
return 'hard';
|
||
}
|
||
|
||
analyzeSentenceStructure(sentence) {
|
||
return {
|
||
words: sentence.split(' '),
|
||
wordCount: sentence.split(' ').length,
|
||
hasQuestion: sentence.includes('?'),
|
||
hasExclamation: sentence.includes('!'),
|
||
hasComma: sentence.includes(','),
|
||
startsWithCapital: /^[A-Z]/.test(sentence),
|
||
tense: this.detectTense(sentence)
|
||
};
|
||
}
|
||
|
||
detectTense(sentence) {
|
||
if (sentence.includes(' am ') || sentence.includes(' is ') || sentence.includes(' are ')) {
|
||
if (sentence.includes('ing')) return 'present_continuous';
|
||
return 'present_simple';
|
||
}
|
||
if (sentence.includes(' was ') || sentence.includes(' were ')) return 'past_simple';
|
||
if (sentence.includes(' will ')) return 'future_simple';
|
||
return 'present_simple';
|
||
}
|
||
|
||
extractPrerequisites(sentence) {
|
||
const prerequisites = [];
|
||
const words = sentence.toLowerCase().split(' ');
|
||
|
||
// Extraire les mots de vocabulaire importants
|
||
const importantWords = words.filter(word =>
|
||
word.length > 3 &&
|
||
!['this', 'that', 'with', 'from', 'they', 'have', 'been'].includes(word)
|
||
);
|
||
|
||
return importantWords.slice(0, 3);
|
||
}
|
||
|
||
identifyGrammarFocus(sentence) {
|
||
if (sentence.includes('?')) return 'questions';
|
||
if (sentence.includes(' my ') || sentence.includes(' your ')) return 'possessives';
|
||
if (sentence.includes(' is ') || sentence.includes(' are ')) return 'be_verb';
|
||
if (sentence.includes('ing')) return 'present_continuous';
|
||
return 'sentence_structure';
|
||
}
|
||
|
||
generateSentenceTags(sentence) {
|
||
const tags = ['sentence'];
|
||
|
||
if (sentence.english.includes('?')) tags.push('question');
|
||
if (sentence.english.includes('!')) tags.push('exclamation');
|
||
if (sentence.english.toLowerCase().includes('hello')) tags.push('greeting');
|
||
if (sentence.english.toLowerCase().includes('my name')) tags.push('introduction');
|
||
|
||
return tags;
|
||
}
|
||
|
||
getSentenceIcon(sentence) {
|
||
if (sentence.english.includes('?')) return '❓';
|
||
if (sentence.english.includes('!')) return '❗';
|
||
if (sentence.english.toLowerCase().includes('hello')) return '👋';
|
||
return '💬';
|
||
}
|
||
|
||
generateSentenceHints(sentence) {
|
||
const hints = [];
|
||
|
||
hints.push(`La phrase a ${sentence.english.split(' ').length} mots`);
|
||
|
||
if (sentence.english.includes('?')) {
|
||
hints.push("C'est une question");
|
||
}
|
||
|
||
const firstWord = sentence.english.split(' ')[0];
|
||
hints.push(`Commence par "${firstWord}"`);
|
||
|
||
return hints;
|
||
}
|
||
|
||
suggestTranslation(english) {
|
||
// Traduction basique pour exemples
|
||
const basicTranslations = {
|
||
'Hello': 'Bonjour',
|
||
'My name is': 'Je m\'appelle',
|
||
'I am': 'Je suis',
|
||
'How are you?': 'Comment allez-vous ?',
|
||
'Thank you': 'Merci'
|
||
};
|
||
|
||
for (const [en, fr] of Object.entries(basicTranslations)) {
|
||
if (english.includes(en)) {
|
||
return english.replace(en, fr);
|
||
}
|
||
}
|
||
|
||
return ''; // À traduire manuellement
|
||
}
|
||
}
|
||
|
||
// === GÉNÉRATEUR DE DIALOGUES ===
|
||
class DialogueGenerator {
|
||
async generate(parsedContent, options = {}) {
|
||
console.log('💬 DialogueGenerator - Génération exercices dialogues');
|
||
|
||
const exercises = [];
|
||
|
||
if (parsedContent.dialogue) {
|
||
const exercise = this.createDialogueExercise(parsedContent.dialogue, 0, options);
|
||
exercises.push(exercise);
|
||
}
|
||
|
||
if (parsedContent.conversations) {
|
||
parsedContent.conversations.forEach((conversation, index) => {
|
||
const exercise = this.createConversationExercise(conversation, index, options);
|
||
exercises.push(exercise);
|
||
});
|
||
}
|
||
|
||
return exercises;
|
||
}
|
||
|
||
createDialogueExercise(dialogue, index, options) {
|
||
return {
|
||
id: `dial_${index + 1}`,
|
||
type: 'dialogue',
|
||
difficulty: this.inferDialogueDifficulty(dialogue),
|
||
category: dialogue.scenario || 'conversation',
|
||
|
||
content: {
|
||
scenario: dialogue.scenario || 'conversation',
|
||
english: this.extractDialogueTitle(dialogue),
|
||
french: this.translateScenario(dialogue.scenario),
|
||
conversation: dialogue.conversation.map(line => ({
|
||
speaker: line.speaker,
|
||
english: line.english || line.text,
|
||
french: line.french || this.suggestTranslation(line.english || line.text),
|
||
role: this.identifyRole(line)
|
||
})),
|
||
context: dialogue.scenario || 'general',
|
||
tags: this.generateDialogueTags(dialogue)
|
||
},
|
||
|
||
media: {
|
||
icon: this.getDialogueIcon(dialogue.scenario)
|
||
},
|
||
|
||
pedagogy: {
|
||
learningObjective: `Participer à un dialogue : ${dialogue.scenario}`,
|
||
prerequisites: this.extractDialoguePrerequisites(dialogue),
|
||
followUp: ['advanced_dialogue', 'role_play'],
|
||
grammarFocus: 'conversation_patterns'
|
||
},
|
||
|
||
interaction: {
|
||
type: 'role_play',
|
||
validation: 'dialogue_flow',
|
||
userRole: this.selectUserRole(dialogue),
|
||
hints: this.generateDialogueHints(dialogue),
|
||
feedback: {
|
||
correct: `Parfait ! Tu maîtrises ce type de conversation ! 🎭`,
|
||
incorrect: `Essaie de suivre le flow naturel de la conversation`
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
createConversationExercise(conversation, index, options) {
|
||
return {
|
||
id: `conv_${index + 1}`,
|
||
type: 'dialogue',
|
||
difficulty: 'medium',
|
||
category: conversation.scene || 'conversation',
|
||
|
||
content: {
|
||
scenario: conversation.scene || `Conversation ${index + 1}`,
|
||
english: `${conversation.speaker}: ${conversation.english}`,
|
||
french: `${conversation.speaker}: ${conversation.french}`,
|
||
conversation: [{
|
||
speaker: conversation.speaker,
|
||
english: conversation.english,
|
||
french: conversation.french,
|
||
role: 'statement'
|
||
}],
|
||
tags: ['conversation', 'dialogue']
|
||
},
|
||
|
||
interaction: {
|
||
type: 'click',
|
||
validation: 'exact'
|
||
}
|
||
};
|
||
}
|
||
|
||
inferDialogueDifficulty(dialogue) {
|
||
const conversationLength = dialogue.conversation?.length || 1;
|
||
const averageWordsPerLine = dialogue.conversation?.reduce((sum, line) =>
|
||
sum + (line.english || line.text || '').split(' ').length, 0) / conversationLength || 5;
|
||
|
||
if (conversationLength <= 2 && averageWordsPerLine <= 5) return 'easy';
|
||
if (conversationLength <= 4 && averageWordsPerLine <= 8) return 'medium';
|
||
return 'hard';
|
||
}
|
||
|
||
extractDialogueTitle(dialogue) {
|
||
if (dialogue.title) return dialogue.title;
|
||
if (dialogue.scenario) return this.beautifyScenario(dialogue.scenario);
|
||
return 'Dialogue';
|
||
}
|
||
|
||
beautifyScenario(scenario) {
|
||
const scenarios = {
|
||
'greeting': 'Salutations',
|
||
'restaurant': 'Au Restaurant',
|
||
'shopping': 'Faire les Courses',
|
||
'school': 'À l\'École',
|
||
'family': 'En Famille'
|
||
};
|
||
|
||
return scenarios[scenario] || scenario;
|
||
}
|
||
|
||
translateScenario(scenario) {
|
||
const translations = {
|
||
'greeting': 'Se saluer',
|
||
'restaurant': 'Commander au restaurant',
|
||
'shopping': 'Acheter quelque chose',
|
||
'school': 'Parler à l\'école',
|
||
'family': 'Conversation familiale'
|
||
};
|
||
|
||
return translations[scenario] || scenario;
|
||
}
|
||
|
||
identifyRole(line) {
|
||
const text = (line.english || line.text || '').toLowerCase();
|
||
|
||
if (text.includes('?')) return 'question';
|
||
if (text.includes('hello') || text.includes('hi')) return 'greeting';
|
||
if (text.includes('thank')) return 'thanks';
|
||
if (text.includes('goodbye') || text.includes('bye')) return 'farewell';
|
||
return 'statement';
|
||
}
|
||
|
||
generateDialogueTags(dialogue) {
|
||
const tags = ['dialogue', 'conversation'];
|
||
|
||
if (dialogue.scenario) tags.push(dialogue.scenario);
|
||
|
||
const hasQuestion = dialogue.conversation?.some(line =>
|
||
(line.english || line.text || '').includes('?'));
|
||
if (hasQuestion) tags.push('questions');
|
||
|
||
return tags;
|
||
}
|
||
|
||
getDialogueIcon(scenario) {
|
||
const icons = {
|
||
'greeting': '👋',
|
||
'restaurant': '🍽️',
|
||
'shopping': '🛒',
|
||
'school': '🎒',
|
||
'family': '👨👩👧👦',
|
||
'friends': '👫'
|
||
};
|
||
|
||
return icons[scenario] || '💬';
|
||
}
|
||
|
||
selectUserRole(dialogue) {
|
||
// Sélectionner le rôle que l'utilisateur va jouer
|
||
const speakers = dialogue.conversation?.map(line => line.speaker) || [];
|
||
const uniqueSpeakers = [...new Set(speakers)];
|
||
|
||
// Privilégier certains rôles
|
||
if (uniqueSpeakers.includes('student')) return 'student';
|
||
if (uniqueSpeakers.includes('child')) return 'child';
|
||
if (uniqueSpeakers.includes('customer')) return 'customer';
|
||
|
||
// Sinon, prendre le premier
|
||
return uniqueSpeakers[0] || 'person1';
|
||
}
|
||
|
||
extractDialoguePrerequisites(dialogue) {
|
||
const prerequisites = new Set();
|
||
|
||
dialogue.conversation?.forEach(line => {
|
||
const text = line.english || line.text || '';
|
||
const words = text.toLowerCase().split(' ');
|
||
|
||
// Extraire mots importants
|
||
words.forEach(word => {
|
||
if (word.length > 3 && !['this', 'that', 'with', 'they', 'have'].includes(word)) {
|
||
prerequisites.add(word);
|
||
}
|
||
});
|
||
});
|
||
|
||
return Array.from(prerequisites).slice(0, 5);
|
||
}
|
||
|
||
generateDialogueHints(dialogue) {
|
||
const hints = [];
|
||
|
||
hints.push(`Cette conversation a ${dialogue.conversation?.length || 1} répliques`);
|
||
|
||
if (dialogue.scenario) {
|
||
hints.push(`Le contexte est : ${dialogue.scenario}`);
|
||
}
|
||
|
||
const hasQuestions = dialogue.conversation?.some(line =>
|
||
(line.english || line.text || '').includes('?'));
|
||
if (hasQuestions) {
|
||
hints.push('Il y a des questions dans cette conversation');
|
||
}
|
||
|
||
return hints;
|
||
}
|
||
|
||
suggestTranslation(english) {
|
||
// Utiliser le même système que SentenceGenerator
|
||
return new SentenceGenerator().suggestTranslation(english);
|
||
}
|
||
}
|
||
|
||
// === GÉNÉRATEUR DE SÉQUENCES ===
|
||
class SequenceGenerator {
|
||
async generate(parsedContent, options = {}) {
|
||
console.log('📋 SequenceGenerator - Génération exercices séquences');
|
||
|
||
const exercises = [];
|
||
|
||
if (parsedContent.sequence) {
|
||
const exercise = this.createSequenceExercise(parsedContent.sequence, 0, options);
|
||
exercises.push(exercise);
|
||
}
|
||
|
||
return exercises;
|
||
}
|
||
|
||
createSequenceExercise(sequence, index, options) {
|
||
return {
|
||
id: `seq_${index + 1}`,
|
||
type: 'sequence',
|
||
difficulty: this.inferSequenceDifficulty(sequence),
|
||
category: this.inferSequenceCategory(sequence),
|
||
|
||
content: {
|
||
english: sequence.title || 'Sequence',
|
||
french: this.translateSequenceTitle(sequence.title),
|
||
title: sequence.title || 'Sequence',
|
||
steps: sequence.steps.map(step => ({
|
||
order: step.order,
|
||
english: step.english,
|
||
french: step.french || this.suggestStepTranslation(step.english),
|
||
icon: this.getStepIcon(step.english),
|
||
time: this.extractTime(step)
|
||
})),
|
||
context: this.inferContext(sequence),
|
||
tags: this.generateSequenceTags(sequence)
|
||
},
|
||
|
||
media: {
|
||
icon: this.getSequenceIcon(sequence)
|
||
},
|
||
|
||
pedagogy: {
|
||
learningObjective: `Comprendre l'ordre chronologique : ${sequence.title}`,
|
||
prerequisites: this.extractSequencePrerequisites(sequence),
|
||
followUp: ['time_expressions', 'daily_routine'],
|
||
grammarFocus: 'sequence_connectors'
|
||
},
|
||
|
||
interaction: {
|
||
type: 'chronological_order',
|
||
validation: 'sequence_correct',
|
||
hints: this.generateSequenceHints(sequence),
|
||
feedback: {
|
||
correct: `Parfait ! Tu connais l'ordre correct ! ⏰`,
|
||
incorrect: `Réfléchis à l'ordre logique des étapes`
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
inferSequenceDifficulty(sequence) {
|
||
const stepCount = sequence.steps?.length || 1;
|
||
const avgWordsPerStep = sequence.steps?.reduce((sum, step) =>
|
||
sum + step.english.split(' ').length, 0) / stepCount || 3;
|
||
|
||
if (stepCount <= 3 && avgWordsPerStep <= 4) return 'easy';
|
||
if (stepCount <= 5 && avgWordsPerStep <= 6) return 'medium';
|
||
return 'hard';
|
||
}
|
||
|
||
inferSequenceCategory(sequence) {
|
||
const title = sequence.title?.toLowerCase() || '';
|
||
const allText = sequence.steps?.map(s => s.english.toLowerCase()).join(' ') || '';
|
||
|
||
if (title.includes('morning') || allText.includes('wake up') || allText.includes('breakfast')) {
|
||
return 'daily_routine';
|
||
}
|
||
if (title.includes('recipe') || allText.includes('cook') || allText.includes('mix')) {
|
||
return 'cooking';
|
||
}
|
||
if (title.includes('story') || allText.includes('once upon')) {
|
||
return 'story';
|
||
}
|
||
|
||
return 'sequence';
|
||
}
|
||
|
||
translateSequenceTitle(title) {
|
||
const translations = {
|
||
'Morning Routine': 'Routine du Matin',
|
||
'Getting Ready': 'Se Préparer',
|
||
'Cooking Recipe': 'Recette de Cuisine',
|
||
'Story': 'Histoire'
|
||
};
|
||
|
||
return translations[title] || title;
|
||
}
|
||
|
||
suggestStepTranslation(english) {
|
||
const stepTranslations = {
|
||
'Wake up': 'Se réveiller',
|
||
'Get dressed': 'S\'habiller',
|
||
'Eat breakfast': 'Prendre le petit-déjeuner',
|
||
'Go to school': 'Aller à l\'école',
|
||
'Brush teeth': 'Se brosser les dents'
|
||
};
|
||
|
||
return stepTranslations[english] || '';
|
||
}
|
||
|
||
getStepIcon(stepText) {
|
||
const icons = {
|
||
'wake up': '⏰', 'get dressed': '👕', 'breakfast': '🥞',
|
||
'school': '🎒', 'brush': '🪥', 'wash': '🧼',
|
||
'cook': '🍳', 'mix': '🥄', 'bake': '🔥'
|
||
};
|
||
|
||
const lowerText = stepText.toLowerCase();
|
||
|
||
for (const [key, icon] of Object.entries(icons)) {
|
||
if (lowerText.includes(key)) return icon;
|
||
}
|
||
|
||
return '📝';
|
||
}
|
||
|
||
extractTime(step) {
|
||
// Extraire indication de temps du texte
|
||
const timeMatch = step.english.match(/\d{1,2}:\d{2}|\d{1,2}(am|pm)/i);
|
||
return timeMatch ? timeMatch[0] : null;
|
||
}
|
||
|
||
inferContext(sequence) {
|
||
const category = this.inferSequenceCategory(sequence);
|
||
|
||
const contexts = {
|
||
'daily_routine': 'routine quotidienne',
|
||
'cooking': 'cuisine',
|
||
'story': 'histoire',
|
||
'sequence': 'étapes'
|
||
};
|
||
|
||
return contexts[category] || 'séquence';
|
||
}
|
||
|
||
generateSequenceTags(sequence) {
|
||
const tags = ['sequence', 'chronological'];
|
||
|
||
const category = this.inferSequenceCategory(sequence);
|
||
tags.push(category);
|
||
|
||
if (sequence.steps?.some(step => this.extractTime(step))) {
|
||
tags.push('time');
|
||
}
|
||
|
||
return tags;
|
||
}
|
||
|
||
getSequenceIcon(sequence) {
|
||
const category = this.inferSequenceCategory(sequence);
|
||
|
||
const icons = {
|
||
'daily_routine': '🌅',
|
||
'cooking': '👨🍳',
|
||
'story': '📖',
|
||
'sequence': '📋'
|
||
};
|
||
|
||
return icons[category] || '📋';
|
||
}
|
||
|
||
extractSequencePrerequisites(sequence) {
|
||
const prerequisites = new Set();
|
||
|
||
sequence.steps?.forEach(step => {
|
||
const words = step.english.toLowerCase().split(' ');
|
||
|
||
// Verbes d'action importants
|
||
const actionVerbs = words.filter(word =>
|
||
['wake', 'get', 'eat', 'go', 'brush', 'wash', 'cook', 'mix'].some(verb =>
|
||
word.includes(verb)
|
||
)
|
||
);
|
||
|
||
actionVerbs.forEach(verb => prerequisites.add(verb));
|
||
});
|
||
|
||
return Array.from(prerequisites).slice(0, 4);
|
||
}
|
||
|
||
generateSequenceHints(sequence) {
|
||
const hints = [];
|
||
|
||
hints.push(`Cette séquence a ${sequence.steps?.length || 0} étapes`);
|
||
|
||
const category = this.inferSequenceCategory(sequence);
|
||
if (category === 'daily_routine') {
|
||
hints.push('Pense à ton ordre habituel du matin');
|
||
} else if (category === 'cooking') {
|
||
hints.push('Quelle est la logique de la recette ?');
|
||
}
|
||
|
||
const firstStep = sequence.steps?.[0];
|
||
if (firstStep) {
|
||
hints.push(`La première étape est : "${firstStep.english}"`);
|
||
}
|
||
|
||
return hints;
|
||
}
|
||
}
|
||
|
||
// === GÉNÉRATEUR AUTOMATIQUE ===
|
||
class AutoGenerator {
|
||
async generate(parsedContent, options = {}) {
|
||
console.log('🤖 AutoGenerator - Génération automatique intelligente');
|
||
|
||
const exercises = [];
|
||
|
||
// Détecter et générer selon ce qui est disponible
|
||
if (parsedContent.vocabulary?.length > 0) {
|
||
const vocabGen = new VocabularyGenerator();
|
||
const vocabExercises = await vocabGen.generate(parsedContent, options);
|
||
exercises.push(...vocabExercises);
|
||
}
|
||
|
||
if (parsedContent.sentences?.length > 0) {
|
||
const sentGen = new SentenceGenerator();
|
||
const sentExercises = await sentGen.generate(parsedContent, options);
|
||
exercises.push(...sentExercises);
|
||
}
|
||
|
||
if (parsedContent.dialogue) {
|
||
const dialogGen = new DialogueGenerator();
|
||
const dialogExercises = await dialogGen.generate(parsedContent, options);
|
||
exercises.push(...dialogExercises);
|
||
}
|
||
|
||
if (parsedContent.sequence) {
|
||
const seqGen = new SequenceGenerator();
|
||
const seqExercises = await seqGen.generate(parsedContent, options);
|
||
exercises.push(...seqExercises);
|
||
}
|
||
|
||
// Si rien de spécifique n'a été détecté, créer du contenu basique
|
||
if (exercises.length === 0) {
|
||
exercises.push(this.createFallbackExercise(parsedContent, options));
|
||
}
|
||
|
||
return exercises;
|
||
}
|
||
|
||
createFallbackExercise(parsedContent, options) {
|
||
return {
|
||
id: 'auto_001',
|
||
type: 'vocabulary',
|
||
difficulty: 'easy',
|
||
category: 'general',
|
||
|
||
content: {
|
||
english: 'text',
|
||
french: 'texte',
|
||
context: 'Contenu généré automatiquement',
|
||
tags: ['auto-generated']
|
||
},
|
||
|
||
interaction: {
|
||
type: 'click',
|
||
validation: 'exact',
|
||
feedback: {
|
||
correct: 'Bonne réponse !',
|
||
incorrect: 'Essaie encore'
|
||
}
|
||
}
|
||
};
|
||
}
|
||
}
|
||
|
||
// Export global
|
||
window.VocabularyGenerator = VocabularyGenerator;
|
||
window.SentenceGenerator = SentenceGenerator;
|
||
window.DialogueGenerator = DialogueGenerator;
|
||
window.SequenceGenerator = SequenceGenerator;
|
||
window.AutoGenerator = AutoGenerator; |