diff --git a/backend/scripts/create-bisaya-course-content.js b/backend/scripts/create-bisaya-course-content.js index 0e6461d..38863e6 100644 --- a/backend/scripts/create-bisaya-course-content.js +++ b/backend/scripts/create-bisaya-course-content.js @@ -4323,6 +4323,73 @@ const BISAYA_EXERCISES = { }) ], + 'Rollenspiele - echte Situationen': [ + { + exerciseTypeId: 2, + title: 'Szene wählen', + instruction: 'Wähle die Bisaya-Formulierung, die am besten zu einer gemischten Alltagsszene (Weg + Termin + Hilfe) passt.', + questionData: { + type: 'multiple_choice', + question: 'Ihr müsst in die Stadt, habt einen Termin und willst kurz Hilfe anbieten. Was passt am ehesten?', + options: [ + 'Moadto mi sa lungsod. Aduna mi appointment. Tabangan tika.', + 'Asa ang CR?', + 'Nikaon na ka?', + 'Magdula ta.' + ] + }, + answerData: { type: 'multiple_choice', correctAnswer: 0 }, + explanation: 'Rollenspiele verbinden oft Weg, Termin und Hilfe in einer kurzen Kette.' + }, + { + exerciseTypeId: 1, + title: 'Arzt und Weg', + instruction: 'Fülle die Lücke: Arzt und Bewegung.', + questionData: { + type: 'gap_fill', + text: 'Adto ta sa {gap}.', + gaps: 1 + }, + answerData: { + type: 'gap_fill', + answers: ['doktor'] + }, + explanation: '„Adto ta sa doktor." ist ein typischer Rollenspiel-Satz zum Arztbesuch.' + }, + { + exerciseTypeId: 4, + title: 'Kind sicher holen', + instruction: 'Übersetze ins Bisaya.', + questionData: { + type: 'transformation', + text: 'Ich hole das Kind.', + sourceLanguage: 'Deutsch', + targetLanguage: 'Bisaya' + }, + answerData: { + type: 'transformation', + correct: 'Kuhaon nako ang bata.', + alternatives: ['Kuhaon nako si bata.', 'Akong kuhaon ang bata.'] + }, + explanation: 'Im Familienalltag taucht „bata" in Rollenspielen sehr häufig auf.' + }, + withTypeName('situational_response', { + title: 'Drei Mini-Sätze', + instruction: 'Antworte in drei kurzen Sätzen (Rollenspiel).', + questionData: { + type: 'situational_response', + question: + 'Spiel eine kurze Szene: ihr geht zum Arzt, braucht den Weg zur Haltestelle und bietet jemandem Hilfe an.', + keywords: ['doktor', 'sakayan', 'tabang'] + }, + answerData: { + modelAnswer: 'Adto ta sa doktor. Asa ang sakayan? Pwede ko motabang nimo?', + keywords: ['doktor', 'sakayan', 'tabang'] + }, + explanation: 'Mehrere kurze Sätze hintereinander üben typische Rollenspiel-Ketten.' + }) + ], + 'Freies Sprechen - Alltag ohne Stütze': [ { exerciseTypeId: 2, @@ -5167,16 +5234,64 @@ async function findOrCreateSystemUser() { return systemUser; } +function normalizeLessonTitleForMatch(title) { + return String(title || '') + .normalize('NFKC') + .replace(/\s+/g, ' ') + .trim(); +} + +/** Lektionen, bei denen alte Platzhalter-Übungen verworfen und neu erzeugt werden sollen. */ +const PLACEHOLDER_REBUILD_TITLES = new Set([ + 'Woche 1 - Wiederholung', + 'Woche 1 - Vokabeltest', + 'Begrüßungen & Höflichkeit', + 'Familienwörter', + 'Essen & Fürsorge', + 'Alltagsgespräche - Teil 1', + 'Alltagsgespräche - Teil 2', + 'Haus & Familie', + 'Ort & Richtung', + 'Zeitformen - Grundlagen', + 'Zeit & Datum', + 'Einkaufen & Preise', + 'Zahlen & Preise', + 'Zahlen 1–20', + 'Zahlen: Zehner', + 'Zahlen: Hunderter', + 'Zahlen: Tausender', + 'Woche 2 - Wiederholung', + 'Woche 2 - Vokabeltest', + 'Familie - Verwandte & Stieffamilie', + 'Rollenspiele - echte Situationen' +]); + +function lessonMatchesPlaceholderRebuildList(lesson) { + const n = normalizeLessonTitleForMatch(lesson.title); + if (PLACEHOLDER_REBUILD_TITLES.has(n)) return true; + // Lektion 18: alte Bezeichnung „Zahlen & Preise“ trotz Leerzeichen/Varianten + const num = Number(lesson.lessonNumber); + if (num === 18 && /\bzahlen\b/i.test(n) && /\bpreis/i.test(n)) return true; + return false; +} + function getExercisesForLesson(lesson) { const lessonTitle = lesson.title; + const normalizedTitle = normalizeLessonTitleForMatch(lessonTitle); // Alte Kurstitel (DB noch nicht migriert) - if (lessonTitle === 'Zahlen & Preise' && BISAYA_EXERCISES['Zahlen 1–20']) { + const isLegacyZahlenPreise = + normalizedTitle === 'Zahlen & Preise' || + (Number(lesson.lessonNumber) === 18 && /\bzahlen\b/i.test(normalizedTitle) && /\bpreis/i.test(normalizedTitle)); + if (isLegacyZahlenPreise && BISAYA_EXERCISES['Zahlen 1–20']) { return BISAYA_EXERCISES['Zahlen 1–20']; } // Suche nach exaktem Titel if (BISAYA_EXERCISES[lessonTitle]) { return BISAYA_EXERCISES[lessonTitle]; } + if (BISAYA_EXERCISES[normalizedTitle]) { + return BISAYA_EXERCISES[normalizedTitle]; + } // Fallback: Suche nach Teilstring for (const [key, exercises] of Object.entries(BISAYA_EXERCISES)) { @@ -5245,28 +5360,7 @@ async function createBisayaCourseContent() { } // Lektionen mit Platzhalter-Ersetzung: alte Übungen entfernen und durch echte ersetzen - const replacePlaceholders = [ - 'Woche 1 - Wiederholung', - 'Woche 1 - Vokabeltest', - 'Begrüßungen & Höflichkeit', - 'Familienwörter', - 'Essen & Fürsorge', - 'Alltagsgespräche - Teil 1', - 'Alltagsgespräche - Teil 2', - 'Haus & Familie', - 'Ort & Richtung', - 'Zeitformen - Grundlagen', - 'Zeit & Datum', - 'Einkaufen & Preise', - 'Zahlen & Preise', - 'Zahlen 1–20', - 'Zahlen: Zehner', - 'Zahlen: Hunderter', - 'Zahlen: Tausender', - 'Woche 2 - Wiederholung', - 'Woche 2 - Vokabeltest', - 'Familie - Verwandte & Stieffamilie' - ].includes(lesson.title); + const replacePlaceholders = lessonMatchesPlaceholderRebuildList(lesson); const existingCount = await VocabGrammarExercise.count({ where: { lessonId: lesson.id } });