// === 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;