Refactor lesson types and update review handling
All checks were successful
Deploy to production / deploy (push) Successful in 2m22s

- Changed lesson type from 'review' to 'weekly_review' in multiple lesson definitions across phase 3 and phase 4 extensions.
- Updated the logic in the vocabService to accommodate 'weekly_review' in various methods, ensuring proper handling of weekly review lessons.
- Modified the VocabLessonView component to recognize 'weekly_review' as a valid lesson type for calculations and access checks.
- Enhanced the didactics update script to include 'weekly_review' in the lesson type checks.
- Adjusted the create scripts to reflect the new lesson type for weekly reviews.
This commit is contained in:
Torsten Schulz (local)
2026-05-27 11:34:03 +02:00
parent 664a7b3530
commit d441b4fa31
9 changed files with 131 additions and 67 deletions

View File

@@ -539,11 +539,11 @@ export const BISAYA_PHASE3_LESSONS = [
{ week: 5, day: 1, num: 45, type: 'vocab', title: 'Besuch & Haushalt', desc: 'Haus-, Besuchs- und Haushaltswörter in kurzen Ortssätzen verwenden', targetMin: 22, targetScore: 85, review: true, cultural: null }, { week: 5, day: 1, num: 45, type: 'vocab', title: 'Besuch & Haushalt', desc: 'Haus-, Besuchs- und Haushaltswörter in kurzen Ortssätzen verwenden', targetMin: 22, targetScore: 85, review: true, cultural: null },
{ week: 5, day: 2, num: 46, type: 'grammar', title: 'Fragen im Alltag vertiefen', desc: 'Rückfragen, Folgefragen, Bedeutung und höfliches Nachhaken sicher kombinieren', targetMin: 26, targetScore: 78, review: true, cultural: null }, { week: 5, day: 2, num: 46, type: 'grammar', title: 'Fragen im Alltag vertiefen', desc: 'Rückfragen, Folgefragen, Bedeutung und höfliches Nachhaken sicher kombinieren', targetMin: 26, targetScore: 78, review: true, cultural: null },
{ week: 5, day: 2, num: 47, type: 'conversation', title: 'Termine & Verabredungen', desc: 'Treffen planen, Uhrzeiten absprechen, zusagen und weich verschieben', targetMin: 24, targetScore: 80, review: false, cultural: null }, { week: 5, day: 2, num: 47, type: 'conversation', title: 'Termine & Verabredungen', desc: 'Treffen planen, Uhrzeiten absprechen, zusagen und weich verschieben', targetMin: 24, targetScore: 80, review: false, cultural: null },
{ week: 5, day: 3, num: 48, type: 'review', title: 'Woche 5 - Intensivwiederholung I', desc: 'Besuch, Familie, Fürsorge und Terminplanung in schnellen Rollenwechseln mischen', targetMin: 34, targetScore: 82, review: false, cultural: null }, { week: 5, day: 3, num: 48, type: 'weekly_review', title: 'Woche 5 - Intensivwiederholung I', desc: 'Besuch, Familie, Fürsorge und Terminplanung in schnellen Rollenwechseln mischen', targetMin: 34, targetScore: 82, review: false, cultural: null },
{ week: 5, day: 3, num: 49, type: 'vocab', title: 'Spiralwiederholung - Familie & Fürsorge', desc: 'Alte Familien- und Fürsorgemuster aktiv reaktivieren und in neue Szenen übertragen', targetMin: 24, targetScore: 85, review: true, cultural: null }, { week: 5, day: 3, num: 49, type: 'vocab', title: 'Spiralwiederholung - Familie & Fürsorge', desc: 'Alte Familien- und Fürsorgemuster aktiv reaktivieren und in neue Szenen übertragen', targetMin: 24, targetScore: 85, review: true, cultural: null },
{ week: 5, day: 4, num: 50, type: 'conversation', title: 'Gesundheit im Alltag', desc: 'Beschwerden erfragen, Ruhe/Wasser/Medizin anbieten und nach Besserung fragen', targetMin: 26, targetScore: 80, review: false, cultural: null }, { week: 5, day: 4, num: 50, type: 'conversation', title: 'Gesundheit im Alltag', desc: 'Beschwerden erfragen, Ruhe/Wasser/Medizin anbieten und nach Besserung fragen', targetMin: 26, targetScore: 80, review: false, cultural: null },
{ week: 5, day: 4, num: 51, type: 'vocab', title: 'Medikamente & Beschwerden', desc: 'Symptome, Körperwörter und Hilfewortschatz in Fürsorgesätzen verwenden', targetMin: 24, targetScore: 85, review: true, cultural: null }, { week: 5, day: 4, num: 51, type: 'vocab', title: 'Medikamente & Beschwerden', desc: 'Symptome, Körperwörter und Hilfewortschatz in Fürsorgesätzen verwenden', targetMin: 24, targetScore: 85, review: true, cultural: null },
{ week: 5, day: 5, num: 52, type: 'review', title: 'Woche 5 - Intensivwiederholung II', desc: 'Gesundheit, Besuch, Fragen und Terminplanung unter Abrufdruck kontrastieren', targetMin: 34, targetScore: 82, review: false, cultural: null }, { week: 5, day: 5, num: 52, type: 'weekly_review', title: 'Woche 5 - Intensivwiederholung II', desc: 'Gesundheit, Besuch, Fragen und Terminplanung unter Abrufdruck kontrastieren', targetMin: 34, targetScore: 82, review: false, cultural: null },
{ week: 5, day: 5, num: 53, type: 'vocab', title: 'Woche 5 - Checkpoint', desc: 'Diagnostischer Checkpoint zu Besuch, Termin, Frageketten und Gesundheit', targetMin: 24, targetScore: 84, review: true, cultural: null }, { week: 5, day: 5, num: 53, type: 'vocab', title: 'Woche 5 - Checkpoint', desc: 'Diagnostischer Checkpoint zu Besuch, Termin, Frageketten und Gesundheit', targetMin: 24, targetScore: 84, review: true, cultural: null },
{ week: 6, day: 1, num: 54, type: 'conversation', title: 'Unterwegs & Transport', desc: 'Nach Weg, Haltestelle, Fahrpreis, Ziel und Ausstieg fragen', targetMin: 26, targetScore: 80, review: false, cultural: null }, { week: 6, day: 1, num: 54, type: 'conversation', title: 'Unterwegs & Transport', desc: 'Nach Weg, Haltestelle, Fahrpreis, Ziel und Ausstieg fragen', targetMin: 26, targetScore: 80, review: false, cultural: null },
{ week: 6, day: 1, num: 55, type: 'vocab', title: 'Wege & Verkehr', desc: 'Verkehrs-, Richtungs- und Bewegungswortschatz in einfachen Routen verwenden', targetMin: 24, targetScore: 85, review: true, cultural: null }, { week: 6, day: 1, num: 55, type: 'vocab', title: 'Wege & Verkehr', desc: 'Verkehrs-, Richtungs- und Bewegungswortschatz in einfachen Routen verwenden', targetMin: 24, targetScore: 85, review: true, cultural: null },

View File

@@ -977,51 +977,51 @@ export const BISAYA_PHASE4_LESSONS = [
{ week: 7, day: 1, num: 65, type: 'vocab', title: 'Schule & Betreuung', desc: 'Schul- und Betreuungsvokabeln plus kurze Alltagssätze (Tasche, Buch, Lehrkraft, Aufgabe)', targetMin: 24, targetScore: 85, review: true, cultural: null }, { week: 7, day: 1, num: 65, type: 'vocab', title: 'Schule & Betreuung', desc: 'Schul- und Betreuungsvokabeln plus kurze Alltagssätze (Tasche, Buch, Lehrkraft, Aufgabe)', targetMin: 24, targetScore: 85, review: true, cultural: null },
{ week: 7, day: 2, num: 66, type: 'grammar', title: 'Fragen an Kinder vereinfachen', desc: 'Kurze Fragen vs. Aufforderungen in Betreuungssituationen sicher bauen', targetMin: 26, targetScore: 78, review: true, cultural: null }, { week: 7, day: 2, num: 66, type: 'grammar', title: 'Fragen an Kinder vereinfachen', desc: 'Kurze Fragen vs. Aufforderungen in Betreuungssituationen sicher bauen', targetMin: 26, targetScore: 78, review: true, cultural: null },
{ week: 7, day: 2, num: 67, type: 'conversation', title: 'Hausaufgaben & Routine', desc: 'Hausaufgaben, Lernen, Spielen und Schlafen in eine klare Routine bringen', targetMin: 26, targetScore: 80, review: false, cultural: null }, { week: 7, day: 2, num: 67, type: 'conversation', title: 'Hausaufgaben & Routine', desc: 'Hausaufgaben, Lernen, Spielen und Schlafen in eine klare Routine bringen', targetMin: 26, targetScore: 80, review: false, cultural: null },
{ week: 7, day: 3, num: 68, type: 'review', title: 'Woche 7 - Intensivwiederholung I', desc: 'Kinder, Schule und Familienroutine intensiv wiederholen (Abruf + Rollenwechsel)', targetMin: 34, targetScore: 82, review: false, cultural: null }, { week: 7, day: 3, num: 68, type: 'weekly_review', title: 'Woche 7 - Intensivwiederholung I', desc: 'Kinder, Schule und Familienroutine intensiv wiederholen (Abruf + Rollenwechsel)', targetMin: 34, targetScore: 82, review: false, cultural: null },
{ week: 7, day: 3, num: 69, type: 'vocab', title: 'Spiralwiederholung - Familie, Kinder & Fürsorge', desc: 'Frühe Kernmuster in Kinder-/Schulszenen reaktivieren', targetMin: 24, targetScore: 85, review: true, cultural: null }, { week: 7, day: 3, num: 69, type: 'vocab', title: 'Spiralwiederholung - Familie, Kinder & Fürsorge', desc: 'Frühe Kernmuster in Kinder-/Schulszenen reaktivieren', targetMin: 24, targetScore: 85, review: true, cultural: null },
{ week: 7, day: 4, num: 70, type: 'conversation', title: 'Spielen & Freizeit', desc: 'Spiel, Pause, Regeln und ruhige Korrektur im Kinderalltag sprechen', targetMin: 24, targetScore: 80, review: false, cultural: null }, { week: 7, day: 4, num: 70, type: 'conversation', title: 'Spielen & Freizeit', desc: 'Spiel, Pause, Regeln und ruhige Korrektur im Kinderalltag sprechen', targetMin: 24, targetScore: 80, review: false, cultural: null },
{ week: 7, day: 4, num: 71, type: 'vocab', title: 'Spielsachen & Aktivitäten', desc: 'Spiel- und Freizeitwortschatz aktiv nutzen (Auswahl, Fragen, kurze Sätze)', targetMin: 24, targetScore: 85, review: true, cultural: null }, { week: 7, day: 4, num: 71, type: 'vocab', title: 'Spielsachen & Aktivitäten', desc: 'Spiel- und Freizeitwortschatz aktiv nutzen (Auswahl, Fragen, kurze Sätze)', targetMin: 24, targetScore: 85, review: true, cultural: null },
{ week: 7, day: 5, num: 72, type: 'review', title: 'Woche 7 - Intensivwiederholung II', desc: 'Große Mischwiederholung zur Kinder- und Schulwoche (Szene bauen)', targetMin: 34, targetScore: 82, review: false, cultural: null }, { week: 7, day: 5, num: 72, type: 'weekly_review', title: 'Woche 7 - Intensivwiederholung II', desc: 'Große Mischwiederholung zur Kinder- und Schulwoche (Szene bauen)', targetMin: 34, targetScore: 82, review: false, cultural: null },
{ week: 7, day: 5, num: 73, type: 'vocab', title: 'Woche 7 - Checkpoint', desc: 'Checkpoint zu Kindern, Schule, Hausaufgaben und Routine (Diagnose + freie Szene)', targetMin: 24, targetScore: 84, review: true, cultural: null }, { week: 7, day: 5, num: 73, type: 'vocab', title: 'Woche 7 - Checkpoint', desc: 'Checkpoint zu Kindern, Schule, Hausaufgaben und Routine (Diagnose + freie Szene)', targetMin: 24, targetScore: 84, review: true, cultural: null },
{ week: 8, day: 1, num: 74, type: 'conversation', title: 'Arzt & Termin', desc: 'Arzttermine, Beschwerden und Wartezeit als Mini-Dialog organisieren', targetMin: 26, targetScore: 80, review: false, cultural: null }, { week: 8, day: 1, num: 74, type: 'conversation', title: 'Arzt & Termin', desc: 'Arzttermine, Beschwerden und Wartezeit als Mini-Dialog organisieren', targetMin: 26, targetScore: 80, review: false, cultural: null },
{ week: 8, day: 1, num: 75, type: 'vocab', title: 'Apotheke & Medikamente', desc: 'Apotheke, Rezept, Dosierung und Einnahmefragen aktiv abrufen', targetMin: 24, targetScore: 85, review: true, cultural: null }, { week: 8, day: 1, num: 75, type: 'vocab', title: 'Apotheke & Medikamente', desc: 'Apotheke, Rezept, Dosierung und Einnahmefragen aktiv abrufen', targetMin: 24, targetScore: 85, review: true, cultural: null },
{ week: 8, day: 2, num: 76, type: 'grammar', title: 'Beschwerden genauer beschreiben', desc: 'Stärke, Verlauf, Zeitpunkte und wiederkehrende Beschwerden ausdrücken', targetMin: 26, targetScore: 78, review: true, cultural: null }, { week: 8, day: 2, num: 76, type: 'grammar', title: 'Beschwerden genauer beschreiben', desc: 'Stärke, Verlauf, Zeitpunkte und wiederkehrende Beschwerden ausdrücken', targetMin: 26, targetScore: 78, review: true, cultural: null },
{ week: 8, day: 2, num: 77, type: 'conversation', title: 'Notfälle & Hilfe', desc: 'Hilfe rufen, kurze Anweisungen geben und Notfallanker sicher sprechen', targetMin: 26, targetScore: 80, review: false, cultural: null }, { week: 8, day: 2, num: 77, type: 'conversation', title: 'Notfälle & Hilfe', desc: 'Hilfe rufen, kurze Anweisungen geben und Notfallanker sicher sprechen', targetMin: 26, targetScore: 80, review: false, cultural: null },
{ week: 8, day: 3, num: 78, type: 'review', title: 'Woche 8 - Intensivwiederholung I', desc: 'Arzt, Apotheke, Beschwerden und Hilfe intensiv wiederholen (Tempo + Abruf)', targetMin: 34, targetScore: 82, review: false, cultural: null }, { week: 8, day: 3, num: 78, type: 'weekly_review', title: 'Woche 8 - Intensivwiederholung I', desc: 'Arzt, Apotheke, Beschwerden und Hilfe intensiv wiederholen (Tempo + Abruf)', targetMin: 34, targetScore: 82, review: false, cultural: null },
{ week: 8, day: 3, num: 79, type: 'vocab', title: 'Spiralwiederholung - Gesundheit', desc: 'Fürsorge und Gesundheit als Langzeitabruf (Problem -> Antwort)', targetMin: 24, targetScore: 85, review: true, cultural: null }, { week: 8, day: 3, num: 79, type: 'vocab', title: 'Spiralwiederholung - Gesundheit', desc: 'Fürsorge und Gesundheit als Langzeitabruf (Problem -> Antwort)', targetMin: 24, targetScore: 85, review: true, cultural: null },
{ week: 8, day: 4, num: 80, type: 'conversation', title: 'Essen, Ruhe & Genesung', desc: 'Pflegegespräche: Essen, Wasser, Ruhe, Medizin und Besserung verbinden', targetMin: 24, targetScore: 80, review: false, cultural: null }, { week: 8, day: 4, num: 80, type: 'conversation', title: 'Essen, Ruhe & Genesung', desc: 'Pflegegespräche: Essen, Wasser, Ruhe, Medizin und Besserung verbinden', targetMin: 24, targetScore: 80, review: false, cultural: null },
{ week: 8, day: 4, num: 81, type: 'vocab', title: 'Körper, Symptome & Pflege', desc: 'Körper, Symptome und Pflegewortschatz mit kurzen Schmerzsätzen kombinieren', targetMin: 24, targetScore: 85, review: true, cultural: null }, { week: 8, day: 4, num: 81, type: 'vocab', title: 'Körper, Symptome & Pflege', desc: 'Körper, Symptome und Pflegewortschatz mit kurzen Schmerzsätzen kombinieren', targetMin: 24, targetScore: 85, review: true, cultural: null },
{ week: 8, day: 5, num: 82, type: 'review', title: 'Woche 8 - Intensivwiederholung II', desc: 'Große Mischwiederholung zu Gesundheit und Hilfe (Kontraste + freie Antworten)', targetMin: 34, targetScore: 82, review: false, cultural: null }, { week: 8, day: 5, num: 82, type: 'weekly_review', title: 'Woche 8 - Intensivwiederholung II', desc: 'Große Mischwiederholung zu Gesundheit und Hilfe (Kontraste + freie Antworten)', targetMin: 34, targetScore: 82, review: false, cultural: null },
{ week: 8, day: 5, num: 83, type: 'vocab', title: 'Woche 8 - Checkpoint', desc: 'Checkpoint zu Arzt, Apotheke, Beschwerden und Notfall (Diagnose + Typing)', targetMin: 24, targetScore: 84, review: true, cultural: null }, { week: 8, day: 5, num: 83, type: 'vocab', title: 'Woche 8 - Checkpoint', desc: 'Checkpoint zu Arzt, Apotheke, Beschwerden und Notfall (Diagnose + Typing)', targetMin: 24, targetScore: 84, review: true, cultural: null },
{ week: 9, day: 1, num: 84, type: 'conversation', title: 'Einkaufen vertiefen', desc: 'Komplexere Einkaufs- und Auswahlgespräche führen', targetMin: 18, targetScore: 80, review: false, cultural: null }, { week: 9, day: 1, num: 84, type: 'conversation', title: 'Einkaufen vertiefen', desc: 'Komplexere Einkaufs- und Auswahlgespräche führen', targetMin: 18, targetScore: 80, review: false, cultural: null },
{ week: 9, day: 1, num: 85, type: 'vocab', title: 'Markt & Mengen', desc: 'Wortschatz für Markt, Mengen und Auswahl', targetMin: 18, targetScore: 85, review: true, cultural: null }, { week: 9, day: 1, num: 85, type: 'vocab', title: 'Markt & Mengen', desc: 'Wortschatz für Markt, Mengen und Auswahl', targetMin: 18, targetScore: 85, review: true, cultural: null },
{ week: 9, day: 2, num: 86, type: 'grammar', title: 'Wünsche, Bedarf und Bitte', desc: 'Wunsch, Notwendigkeit und höfliche Bitte unterscheiden', targetMin: 20, targetScore: 78, review: true, cultural: null }, { week: 9, day: 2, num: 86, type: 'grammar', title: 'Wünsche, Bedarf und Bitte', desc: 'Wunsch, Notwendigkeit und höfliche Bitte unterscheiden', targetMin: 20, targetScore: 78, review: true, cultural: null },
{ week: 9, day: 2, num: 87, type: 'conversation', title: 'Behördengänge & Formulare', desc: 'Nach Formularen, Schaltern und Terminen fragen', targetMin: 18, targetScore: 80, review: false, cultural: null }, { week: 9, day: 2, num: 87, type: 'conversation', title: 'Behördengänge & Formulare', desc: 'Nach Formularen, Schaltern und Terminen fragen', targetMin: 18, targetScore: 80, review: false, cultural: null },
{ week: 9, day: 3, num: 88, type: 'review', title: 'Woche 9 - Intensivwiederholung I', desc: 'Einkaufen und Erledigungen intensiv wiederholen', targetMin: 28, targetScore: 80, review: false, cultural: null }, { week: 9, day: 3, num: 88, type: 'weekly_review', title: 'Woche 9 - Intensivwiederholung I', desc: 'Einkaufen und Erledigungen intensiv wiederholen', targetMin: 28, targetScore: 80, review: false, cultural: null },
{ week: 9, day: 3, num: 89, type: 'vocab', title: 'Spiralwiederholung - Preise & Erledigungen', desc: 'Preis- und Erledigungsmuster spiralig festigen', targetMin: 20, targetScore: 85, review: true, cultural: null }, { week: 9, day: 3, num: 89, type: 'vocab', title: 'Spiralwiederholung - Preise & Erledigungen', desc: 'Preis- und Erledigungsmuster spiralig festigen', targetMin: 20, targetScore: 85, review: true, cultural: null },
{ week: 9, day: 4, num: 90, type: 'conversation', title: 'Bank, Geld & Bezahlen', desc: 'Über Bezahlen, Wechselgeld und Geldsituationen sprechen', targetMin: 18, targetScore: 80, review: false, cultural: null }, { week: 9, day: 4, num: 90, type: 'conversation', title: 'Bank, Geld & Bezahlen', desc: 'Über Bezahlen, Wechselgeld und Geldsituationen sprechen', targetMin: 18, targetScore: 80, review: false, cultural: null },
{ week: 9, day: 4, num: 91, type: 'vocab', title: 'Dokumente & Termine', desc: 'Papierkram, Termine und Dokumente benennen', targetMin: 18, targetScore: 85, review: true, cultural: null }, { week: 9, day: 4, num: 91, type: 'vocab', title: 'Dokumente & Termine', desc: 'Papierkram, Termine und Dokumente benennen', targetMin: 18, targetScore: 85, review: true, cultural: null },
{ week: 9, day: 5, num: 92, type: 'review', title: 'Woche 9 - Intensivwiederholung II', desc: 'Große Mischwiederholung zu Geld, Formularen und Terminen', targetMin: 28, targetScore: 80, review: false, cultural: null }, { week: 9, day: 5, num: 92, type: 'weekly_review', title: 'Woche 9 - Intensivwiederholung II', desc: 'Große Mischwiederholung zu Geld, Formularen und Terminen', targetMin: 28, targetScore: 80, review: false, cultural: null },
{ week: 9, day: 5, num: 93, type: 'vocab', title: 'Woche 9 - Checkpoint', desc: 'Checkpoint zu Einkauf, Bezahlen und Erledigungen', targetMin: 16, targetScore: 82, review: true, cultural: null }, { week: 9, day: 5, num: 93, type: 'vocab', title: 'Woche 9 - Checkpoint', desc: 'Checkpoint zu Einkauf, Bezahlen und Erledigungen', targetMin: 16, targetScore: 82, review: true, cultural: null },
{ week: 10, day: 1, num: 94, type: 'conversation', title: 'Nachbarschaft & Besuche', desc: 'Mit Nachbarn sprechen und Besuche im Umfeld einordnen', targetMin: 18, targetScore: 80, review: false, cultural: null }, { week: 10, day: 1, num: 94, type: 'conversation', title: 'Nachbarschaft & Besuche', desc: 'Mit Nachbarn sprechen und Besuche im Umfeld einordnen', targetMin: 18, targetScore: 80, review: false, cultural: null },
{ week: 10, day: 1, num: 95, type: 'vocab', title: 'Hilfe & Unterstützung', desc: 'Wortschatz für Hilfe, Unterstützung und soziale Nähe', targetMin: 18, targetScore: 85, review: true, cultural: null }, { week: 10, day: 1, num: 95, type: 'vocab', title: 'Hilfe & Unterstützung', desc: 'Wortschatz für Hilfe, Unterstützung und soziale Nähe', targetMin: 18, targetScore: 85, review: true, cultural: null },
{ week: 10, day: 2, num: 96, type: 'grammar', title: 'Höflich reagieren und ablehnen', desc: 'Einladungen, Hilfe und Vorschläge fein abstufen', targetMin: 20, targetScore: 78, review: true, cultural: null }, { week: 10, day: 2, num: 96, type: 'grammar', title: 'Höflich reagieren und ablehnen', desc: 'Einladungen, Hilfe und Vorschläge fein abstufen', targetMin: 20, targetScore: 78, review: true, cultural: null },
{ week: 10, day: 2, num: 97, type: 'conversation', title: 'Feste & Einladungen', desc: 'Über Feste, Besuche und Einladungen sprechen', targetMin: 18, targetScore: 80, review: false, cultural: null }, { week: 10, day: 2, num: 97, type: 'conversation', title: 'Feste & Einladungen', desc: 'Über Feste, Besuche und Einladungen sprechen', targetMin: 18, targetScore: 80, review: false, cultural: null },
{ week: 10, day: 3, num: 98, type: 'review', title: 'Woche 10 - Intensivwiederholung I', desc: 'Soziale Situationen und Hilfe intensiv wiederholen', targetMin: 28, targetScore: 80, review: false, cultural: null }, { week: 10, day: 3, num: 98, type: 'weekly_review', title: 'Woche 10 - Intensivwiederholung I', desc: 'Soziale Situationen und Hilfe intensiv wiederholen', targetMin: 28, targetScore: 80, review: false, cultural: null },
{ week: 10, day: 3, num: 99, type: 'vocab', title: 'Spiralwiederholung - Soziale Situationen', desc: 'Besuche, Hilfe und Reaktionen blockübergreifend festigen', targetMin: 20, targetScore: 85, review: true, cultural: null }, { week: 10, day: 3, num: 99, type: 'vocab', title: 'Spiralwiederholung - Soziale Situationen', desc: 'Besuche, Hilfe und Reaktionen blockübergreifend festigen', targetMin: 20, targetScore: 85, review: true, cultural: null },
{ week: 10, day: 4, num: 100, type: 'conversation', title: 'Konflikte ruhig lösen', desc: 'Missverständnisse ansprechen und Spannungen abfedern', targetMin: 18, targetScore: 80, review: false, cultural: null }, { week: 10, day: 4, num: 100, type: 'conversation', title: 'Konflikte ruhig lösen', desc: 'Missverständnisse ansprechen und Spannungen abfedern', targetMin: 18, targetScore: 80, review: false, cultural: null },
{ week: 10, day: 4, num: 101, type: 'vocab', title: 'Gefühle im Gespräch vertiefen', desc: 'Emotionen in sozialen Gesprächen genauer ausdrücken', targetMin: 18, targetScore: 85, review: true, cultural: null }, { week: 10, day: 4, num: 101, type: 'vocab', title: 'Gefühle im Gespräch vertiefen', desc: 'Emotionen in sozialen Gesprächen genauer ausdrücken', targetMin: 18, targetScore: 85, review: true, cultural: null },
{ week: 10, day: 5, num: 102, type: 'review', title: 'Woche 10 - Intensivwiederholung II', desc: 'Große Mischwiederholung zu sozialen Situationen und Emotionen', targetMin: 28, targetScore: 80, review: false, cultural: null }, { week: 10, day: 5, num: 102, type: 'weekly_review', title: 'Woche 10 - Intensivwiederholung II', desc: 'Große Mischwiederholung zu sozialen Situationen und Emotionen', targetMin: 28, targetScore: 80, review: false, cultural: null },
{ week: 10, day: 5, num: 103, type: 'vocab', title: 'Woche 10 - Checkpoint', desc: 'Checkpoint zu Hilfe, Besuchen und Gefühlen', targetMin: 16, targetScore: 82, review: true, cultural: null }, { week: 10, day: 5, num: 103, type: 'vocab', title: 'Woche 10 - Checkpoint', desc: 'Checkpoint zu Hilfe, Besuchen und Gefühlen', targetMin: 16, targetScore: 82, review: true, cultural: null },
{ week: 11, day: 1, num: 104, type: 'conversation', title: 'Zuhause organisieren', desc: 'Ordnung, Haushalt und kleine Aufgaben zuhause besprechen', targetMin: 18, targetScore: 80, review: false, cultural: null }, { week: 11, day: 1, num: 104, type: 'conversation', title: 'Zuhause organisieren', desc: 'Ordnung, Haushalt und kleine Aufgaben zuhause besprechen', targetMin: 18, targetScore: 80, review: false, cultural: null },
{ week: 11, day: 1, num: 105, type: 'vocab', title: 'Haushalt & Reparaturen', desc: 'Wörter für Haushalt, Ordnung und kleine Reparaturen', targetMin: 18, targetScore: 85, review: true, cultural: null }, { week: 11, day: 1, num: 105, type: 'vocab', title: 'Haushalt & Reparaturen', desc: 'Wörter für Haushalt, Ordnung und kleine Reparaturen', targetMin: 18, targetScore: 85, review: true, cultural: null },
{ week: 11, day: 2, num: 106, type: 'grammar', title: 'Vergangenes und Pläne im Alltag', desc: 'Vergangenheit und kommende Schritte in Routinen verbinden', targetMin: 20, targetScore: 78, review: true, cultural: null }, { week: 11, day: 2, num: 106, type: 'grammar', title: 'Vergangenes und Pläne im Alltag', desc: 'Vergangenheit und kommende Schritte in Routinen verbinden', targetMin: 20, targetScore: 78, review: true, cultural: null },
{ week: 11, day: 2, num: 107, type: 'conversation', title: 'Arbeit, Schule und Termine verbinden', desc: 'Mehrere Alltagsbereiche in einer Erzählung kombinieren', targetMin: 18, targetScore: 80, review: false, cultural: null }, { week: 11, day: 2, num: 107, type: 'conversation', title: 'Arbeit, Schule und Termine verbinden', desc: 'Mehrere Alltagsbereiche in einer Erzählung kombinieren', targetMin: 18, targetScore: 80, review: false, cultural: null },
{ week: 11, day: 3, num: 108, type: 'review', title: 'Woche 11 - Intensivwiederholung I', desc: 'Haushalt, Planung und Alltagslogistik intensiv wiederholen', targetMin: 28, targetScore: 80, review: false, cultural: null }, { week: 11, day: 3, num: 108, type: 'weekly_review', title: 'Woche 11 - Intensivwiederholung I', desc: 'Haushalt, Planung und Alltagslogistik intensiv wiederholen', targetMin: 28, targetScore: 80, review: false, cultural: null },
{ week: 11, day: 3, num: 109, type: 'vocab', title: 'Spiralwiederholung - Alltagsmodule', desc: 'Module 6 bis 10 zusammenziehen und wiederholen', targetMin: 20, targetScore: 85, review: true, cultural: null }, { week: 11, day: 3, num: 109, type: 'vocab', title: 'Spiralwiederholung - Alltagsmodule', desc: 'Module 6 bis 10 zusammenziehen und wiederholen', targetMin: 20, targetScore: 85, review: true, cultural: null },
{ week: 11, day: 4, num: 110, type: 'conversation', title: 'Unterwegs im Familienalltag', desc: 'Familie, Wege und Termine im Alltag verbinden', targetMin: 18, targetScore: 80, review: false, cultural: null }, { week: 11, day: 4, num: 110, type: 'conversation', title: 'Unterwegs im Familienalltag', desc: 'Familie, Wege und Termine im Alltag verbinden', targetMin: 18, targetScore: 80, review: false, cultural: null },
{ week: 11, day: 4, num: 111, type: 'vocab', title: 'Nachbarschaft & Orte', desc: 'Wichtige Orte und Nachbarschaftswortschatz vertiefen', targetMin: 18, targetScore: 85, review: true, cultural: null }, { week: 11, day: 4, num: 111, type: 'vocab', title: 'Nachbarschaft & Orte', desc: 'Wichtige Orte und Nachbarschaftswortschatz vertiefen', targetMin: 18, targetScore: 85, review: true, cultural: null },
{ week: 11, day: 5, num: 112, type: 'review', title: 'Woche 11 - Intensivwiederholung II', desc: 'Große Mischwiederholung zu Organisation und Familienlogistik', targetMin: 28, targetScore: 80, review: false, cultural: null }, { week: 11, day: 5, num: 112, type: 'weekly_review', title: 'Woche 11 - Intensivwiederholung II', desc: 'Große Mischwiederholung zu Organisation und Familienlogistik', targetMin: 28, targetScore: 80, review: false, cultural: null },
{ week: 11, day: 5, num: 113, type: 'vocab', title: 'Woche 11 - Checkpoint', desc: 'Checkpoint zu Organisation, Haushalt und Alltagslogistik', targetMin: 16, targetScore: 82, review: true, cultural: null }, { week: 11, day: 5, num: 113, type: 'vocab', title: 'Woche 11 - Checkpoint', desc: 'Checkpoint zu Organisation, Haushalt und Alltagslogistik', targetMin: 16, targetScore: 82, review: true, cultural: null },
{ week: 12, day: 1, num: 114, type: 'conversation', title: 'Freies Gespräch - Alltag I', desc: 'Längere Alltagsgespräche freier und stabiler führen', targetMin: 22, targetScore: 78, review: false, cultural: null }, { week: 12, day: 1, num: 114, type: 'conversation', title: 'Freies Gespräch - Alltag I', desc: 'Längere Alltagsgespräche freier und stabiler führen', targetMin: 22, targetScore: 78, review: false, cultural: null },
{ week: 12, day: 1, num: 115, type: 'vocab', title: 'Mischtraining - Kernwortschatz', desc: 'Kernwortschatz aus allen Modulen gemischt trainieren', targetMin: 20, targetScore: 85, review: true, cultural: null }, { week: 12, day: 1, num: 115, type: 'vocab', title: 'Mischtraining - Kernwortschatz', desc: 'Kernwortschatz aus allen Modulen gemischt trainieren', targetMin: 20, targetScore: 85, review: true, cultural: null },

View File

@@ -7,7 +7,7 @@ const BISAYA_LESSONS_24_43_BASE = [
{ week: 3, day: 3, num: 29, type: 'conversation', title: 'Bitten & Nachfragen', desc: 'Hilfe, Wiederholung, Bedeutung und langsames Sprechen erbitten', targetMin: 18, targetScore: 80, review: false, cultural: null }, { week: 3, day: 3, num: 29, type: 'conversation', title: 'Bitten & Nachfragen', desc: 'Hilfe, Wiederholung, Bedeutung und langsames Sprechen erbitten', targetMin: 18, targetScore: 80, review: false, cultural: null },
{ week: 3, day: 4, num: 30, type: 'conversation', title: 'Kinder & Familie', desc: 'Mit Kindern kurz, klar und freundlich sprechen', targetMin: 18, targetScore: 80, review: false, cultural: 'Kurze klare Sätze funktionieren mit Kindern natürlicher als lange Erklärungen.' }, { week: 3, day: 4, num: 30, type: 'conversation', title: 'Kinder & Familie', desc: 'Mit Kindern kurz, klar und freundlich sprechen', targetMin: 18, targetScore: 80, review: false, cultural: 'Kurze klare Sätze funktionieren mit Kindern natürlicher als lange Erklärungen.' },
{ week: 3, day: 4, num: 31, type: 'vocab', title: 'Kinder, Spiel & Routine', desc: 'Kinderalltag mit Spielen, Fertigsein und Schlaf verbinden', targetMin: 20, targetScore: 85, review: true, cultural: null }, { week: 3, day: 4, num: 31, type: 'vocab', title: 'Kinder, Spiel & Routine', desc: 'Kinderalltag mit Spielen, Fertigsein und Schlaf verbinden', targetMin: 20, targetScore: 85, review: true, cultural: null },
{ week: 3, day: 5, num: 32, type: 'review', title: 'Woche 3 - Intensivwiederholung', desc: 'Gefühle, Gesundheit, Kinder und Höflichkeit kontrastieren', targetMin: 30, targetScore: 82, review: false, cultural: null }, { week: 3, day: 5, num: 32, type: 'weekly_review', title: 'Woche 3 - Intensivwiederholung', desc: 'Gefühle, Gesundheit, Kinder und Höflichkeit kontrastieren', targetMin: 30, targetScore: 82, review: false, cultural: null },
{ week: 3, day: 5, num: 33, type: 'vocab', title: 'Woche 3 - Checkpoint', desc: 'Aktiver Checkpoint zur ersten Alltagserweiterung', targetMin: 18, targetScore: 84, review: true, cultural: null }, { week: 3, day: 5, num: 33, type: 'vocab', title: 'Woche 3 - Checkpoint', desc: 'Aktiver Checkpoint zur ersten Alltagserweiterung', targetMin: 18, targetScore: 84, review: true, cultural: null },
{ week: 3, day: 5, num: 44, type: 'vocab', title: 'Zahlen & Zählen (Woche 3)', desc: 'Gezielte Zählübungen 120 und runde Zahlen', targetMin: 14, targetScore: 85, review: true, cultural: null }, { week: 3, day: 5, num: 44, type: 'vocab', title: 'Zahlen & Zählen (Woche 3)', desc: 'Gezielte Zählübungen 120 und runde Zahlen', targetMin: 14, targetScore: 85, review: true, cultural: null },
{ week: 4, day: 1, num: 34, type: 'conversation', title: 'Alltagsszene: Zuhause morgens', desc: 'Morgens zuhause über Aufstehen, Essen, Schule und Aufgaben sprechen', targetMin: 22, targetScore: 78, review: false, cultural: 'Morgenszenen verbinden Fürsorge, Zeit und Familienorganisation.' }, { week: 4, day: 1, num: 34, type: 'conversation', title: 'Alltagsszene: Zuhause morgens', desc: 'Morgens zuhause über Aufstehen, Essen, Schule und Aufgaben sprechen', targetMin: 22, targetScore: 78, review: false, cultural: 'Morgenszenen verbinden Fürsorge, Zeit und Familienorganisation.' },

View File

@@ -806,7 +806,7 @@ function generateExercisesFromDidactics(lesson) {
if (String(lesson.title || '').toLowerCase().includes('zeitformen')) { if (String(lesson.title || '').toLowerCase().includes('zeitformen')) {
generated.push(...buildZeitformenDrills(lesson.title)); generated.push(...buildZeitformenDrills(lesson.title));
} }
} else if (lesson.lessonType === 'review' || lesson.didacticMode === 'intensive_review') { } else if (lesson.lessonType === 'review' || lesson.lessonType === 'weekly_review' || lesson.didacticMode === 'intensive_review') {
generated = [ generated = [
buildReviewChoiceExercise(lesson, didactics, patternA, lessonPool), buildReviewChoiceExercise(lesson, didactics, patternA, lessonPool),
buildReviewChoiceExercise(lesson, didactics, patternB, lessonPool), buildReviewChoiceExercise(lesson, didactics, patternB, lessonPool),
@@ -6188,8 +6188,8 @@ async function createBisayaCourseContent() {
exerciseNumber, exerciseNumber,
title: exercise.title, title: exercise.title,
instruction: exercise.instruction, instruction: exercise.instruction,
questionData: JSON.stringify(exercise.questionData), questionData: exercise.questionData,
answerData: JSON.stringify(exercise.answerData), answerData: exercise.answerData,
explanation: exercise.explanation, explanation: exercise.explanation,
createdByUserId: course.ownerUserId || systemUser.id createdByUserId: course.ownerUserId || systemUser.id
}; };
@@ -6266,8 +6266,8 @@ async function createBisayaCourseContent() {
exerciseNumber: exerciseNumber++, exerciseNumber: exerciseNumber++,
title: exercise.title, title: exercise.title,
instruction: exercise.instruction, instruction: exercise.instruction,
questionData: JSON.stringify(exercise.questionData), questionData: exercise.questionData,
answerData: JSON.stringify(exercise.answerData), answerData: exercise.answerData,
explanation: exercise.explanation, explanation: exercise.explanation,
createdByUserId: course.ownerUserId || systemUser.id createdByUserId: course.ownerUserId || systemUser.id
}); });

View File

@@ -896,7 +896,7 @@ const LESSONS = [
targetMin: 20, targetScore: 85, review: true, targetMin: 20, targetScore: 85, review: true,
cultural: null }, cultural: null },
{ week: 1, day: 5, num: 9, type: 'review', title: 'Woche 1 - Wiederholung', { week: 1, day: 5, num: 9, type: 'weekly_review', title: 'Woche 1 - Wiederholung',
desc: 'Wiederhole alle Inhalte der ersten Woche', desc: 'Wiederhole alle Inhalte der ersten Woche',
targetMin: 30, targetScore: 80, review: false, targetMin: 30, targetScore: 80, review: false,
cultural: 'Wiederholung ist der Schlüssel zum Erfolg!' }, cultural: 'Wiederholung ist der Schlüssel zum Erfolg!' },
@@ -962,7 +962,7 @@ const LESSONS = [
targetMin: 18, targetScore: 85, review: true, targetMin: 18, targetScore: 85, review: true,
cultural: null }, cultural: null },
{ week: 2, day: 5, num: 22, type: 'review', title: 'Woche 2 - Wiederholung', { week: 2, day: 5, num: 22, type: 'weekly_review', title: 'Woche 2 - Wiederholung',
desc: 'Wiederhole alle Inhalte der zweiten Woche', desc: 'Wiederhole alle Inhalte der zweiten Woche',
targetMin: 30, targetScore: 80, review: false, targetMin: 30, targetScore: 80, review: false,
cultural: null }, cultural: null },

View File

@@ -66,7 +66,7 @@ const LESSON_TEMPLATE = [
targetMin: 20, targetScore: 85, review: true, targetMin: 20, targetScore: 85, review: true,
cultural: null }, cultural: null },
{ week: 1, day: 5, num: 9, type: 'review', title: 'Woche 1 - Wiederholung', { week: 1, day: 5, num: 9, type: 'weekly_review', title: 'Woche 1 - Wiederholung',
desc: 'Wiederhole alle Inhalte der ersten Woche', desc: 'Wiederhole alle Inhalte der ersten Woche',
targetMin: 30, targetScore: 80, review: false, targetMin: 30, targetScore: 80, review: false,
cultural: 'Wiederholung ist der Schlüssel zum Erfolg!' }, cultural: 'Wiederholung ist der Schlüssel zum Erfolg!' },
@@ -132,7 +132,7 @@ const LESSON_TEMPLATE = [
targetMin: 18, targetScore: 85, review: true, targetMin: 18, targetScore: 85, review: true,
cultural: null }, cultural: null },
{ week: 2, day: 5, num: 22, type: 'review', title: 'Woche 2 - Wiederholung', { week: 2, day: 5, num: 22, type: 'weekly_review', title: 'Woche 2 - Wiederholung',
desc: 'Wiederhole alle Inhalte der zweiten Woche', desc: 'Wiederhole alle Inhalte der zweiten Woche',
targetMin: 30, targetScore: 80, review: false, targetMin: 30, targetScore: 80, review: false,
cultural: null }, cultural: null },
@@ -183,7 +183,7 @@ const LESSON_TEMPLATE = [
targetMin: 20, targetScore: 85, review: true, targetMin: 20, targetScore: 85, review: true,
cultural: null }, cultural: null },
{ week: 3, day: 5, num: 32, type: 'review', title: 'Woche 3 - Wiederholung', { week: 3, day: 5, num: 32, type: 'weekly_review', title: 'Woche 3 - Wiederholung',
desc: 'Wiederhole alle Inhalte der dritten Woche', desc: 'Wiederhole alle Inhalte der dritten Woche',
targetMin: 30, targetScore: 80, review: false, targetMin: 30, targetScore: 80, review: false,
cultural: null }, cultural: null },

View File

@@ -41,6 +41,10 @@ export const LEGACY_DIDACTICS_TITLE_MAP = {
'Kulturelle Tipps & Tricks': 'Kultur: Höflichkeit, Familie, Alltag' 'Kulturelle Tipps & Tricks': 'Kultur: Höflichkeit, Familie, Alltag'
}; };
function isWeeklyReviewTitle(title) {
return /^Woche \d+ - (?:Wiederholung|Intensivwiederholung)(?:\s|$)/.test(String(title || '').trim());
}
export const LESSON_DIDACTICS = { export const LESSON_DIDACTICS = {
'Begrüßungen & Höflichkeit': { 'Begrüßungen & Höflichkeit': {
learningGoals: [ learningGoals: [
@@ -535,9 +539,13 @@ async function updateBisayaDidactics() {
}; };
const didactics = resolveDidacticsForLesson(lessonLike); const didactics = resolveDidacticsForLesson(lessonLike);
const pedagogy = getBisayaLessonPedagogy(lessonLike.lessonNumber); const pedagogy = getBisayaLessonPedagogy(lessonLike.lessonNumber);
if (!didactics && !pedagogy) continue; const weeklyReview = isWeeklyReviewTitle(row.title);
if (!didactics && !pedagogy && !weeklyReview) continue;
const patch = {}; const patch = {};
if (weeklyReview) {
patch.lessonType = 'weekly_review';
}
if (didactics) { if (didactics) {
patch.learningGoals = didactics.learningGoals || []; patch.learningGoals = didactics.learningGoals || [];
patch.corePatterns = didactics.corePatterns || []; patch.corePatterns = didactics.corePatterns || [];

View File

@@ -421,7 +421,7 @@ export default class VocabService {
_supportsScheduledReview(lessonData = null) { _supportsScheduledReview(lessonData = null) {
const lessonType = String(lessonData?.lessonType || '').toLowerCase(); const lessonType = String(lessonData?.lessonType || '').toLowerCase();
const didacticMode = String(lessonData?.didacticMode || '').toLowerCase(); const didacticMode = String(lessonData?.didacticMode || '').toLowerCase();
if (lessonType === 'culture' || lessonType === 'review' || lessonType === 'vocab_review') { if (lessonType === 'culture' || lessonType === 'review' || lessonType === 'vocab_review' || lessonType === 'weekly_review') {
return false; return false;
} }
if (didacticMode === 'intensive_review' || didacticMode === 'checkpoint') { if (didacticMode === 'intensive_review' || didacticMode === 'checkpoint') {
@@ -1051,7 +1051,7 @@ export default class VocabService {
if (title.includes('abschluss') || title.includes('prüfung') || title.includes('test')) { if (title.includes('abschluss') || title.includes('prüfung') || title.includes('test')) {
return 'checkpoint'; return 'checkpoint';
} }
if (plainLesson.isIntensiveReview || lessonType === 'review' || lessonType === 'vocab_review' || title.includes('wiederholung')) { if (plainLesson.isIntensiveReview || lessonType === 'review' || lessonType === 'vocab_review' || lessonType === 'weekly_review' || title.includes('wiederholung')) {
return 'intensive_review'; return 'intensive_review';
} }
if (lessonType === 'grammar') { if (lessonType === 'grammar') {
@@ -2663,7 +2663,7 @@ export default class VocabService {
if (!plainLesson?.chapterId) { if (!plainLesson?.chapterId) {
return list; return list;
} }
if (plainLesson.lessonType === 'review' || plainLesson.lessonType === 'vocab_review') { if (plainLesson.lessonType === 'review' || plainLesson.lessonType === 'vocab_review' || plainLesson.lessonType === 'weekly_review') {
return list; return list;
} }
let rows = []; let rows = [];
@@ -2899,13 +2899,47 @@ export default class VocabService {
} }
}); });
const isWeeklyReview = plainLesson.lessonType === 'weekly_review';
// Lade Vokabeln aus vorherigen Lektionen (für Wiederholung UND für gemischten Vokabeltrainer) // Lade Vokabeln aus vorherigen Lektionen (für Wiederholung UND für gemischten Vokabeltrainer)
if (plainLesson.lessonNumber > 1) { if (plainLesson.lessonNumber > 1 && !isWeeklyReview) {
plainLesson.previousLessonExercises = await this._getReviewVocabExercises(plainLesson.courseId, plainLesson.lessonNumber); plainLesson.previousLessonExercises = await this._getReviewVocabExercises(plainLesson.courseId, plainLesson.lessonNumber);
} }
// Bei Wiederholungslektionen: Auch Lektions-Liste für Anzeige if (isWeeklyReview) {
if (plainLesson.lessonType === 'review' || plainLesson.lessonType === 'vocab_review') { const weeklyLessons = await this._getReviewLessons(
plainLesson.reviewLessons = await this._getReviewLessons(plainLesson.courseId, plainLesson.lessonNumber); plainLesson.courseId,
plainLesson.lessonNumber,
plainLesson.weekNumber
);
const weeklyExercises = await this._getReviewVocabExercises(
plainLesson.courseId,
plainLesson.lessonNumber,
plainLesson.weekNumber
);
plainLesson.reviewLessons = weeklyLessons;
plainLesson.previousLessonExercises = [];
plainLesson.weeklyReviewTrainingExercises = weeklyExercises;
plainLesson.reviewVocabExercises = this._selectWeeklyReviewExamExercises(weeklyExercises, plainLesson.id);
plainLesson.weeklyReviewExamCount = plainLesson.reviewVocabExercises.length;
plainLesson.weeklyReviewTrainingCount = weeklyExercises.length;
plainLesson.corePatterns = weeklyLessons.flatMap((entry) => {
if (Array.isArray(entry.corePatterns)) return entry.corePatterns;
if (typeof entry.corePatterns === 'string') {
try {
const parsed = JSON.parse(entry.corePatterns);
return Array.isArray(parsed) ? parsed : [];
} catch (error) {
return [];
}
}
return [];
});
} else if (plainLesson.lessonType === 'review' || plainLesson.lessonType === 'vocab_review') {
// Kursweite/gezielt kuratierte Reviews behalten das bisherige Verhalten.
plainLesson.reviewLessons = await this._getReviewLessons(
plainLesson.courseId,
plainLesson.lessonNumber
);
plainLesson.reviewVocabExercises = plainLesson.previousLessonExercises || []; plainLesson.reviewVocabExercises = plainLesson.previousLessonExercises || [];
} }
@@ -3091,19 +3125,24 @@ export default class VocabService {
/** /**
* Sammelt alle Lektionen, die in einer Wiederholungslektion wiederholt werden sollen * Sammelt alle Lektionen, die in einer Wiederholungslektion wiederholt werden sollen
*/ */
async _getReviewLessons(courseId, currentLessonNumber) { async _getReviewLessons(courseId, currentLessonNumber, weekNumber = null) {
const lessons = await VocabCourseLesson.findAll({ const where = {
where: {
courseId: courseId, courseId: courseId,
lessonNumber: { lessonNumber: {
[Op.lt]: currentLessonNumber // Nur Lektionen mit kleinerer Nummer [Op.lt]: currentLessonNumber // Nur Lektionen mit kleinerer Nummer
}, },
lessonType: { lessonType: {
[Op.notIn]: ['review', 'vocab_review'] // Keine anderen Wiederholungslektionen [Op.notIn]: ['review', 'vocab_review', 'weekly_review'] // Keine anderen Wiederholungslektionen
} }
}, };
if (weekNumber != null) {
where.weekNumber = weekNumber;
}
const lessons = await VocabCourseLesson.findAll({
where,
order: [['lessonNumber', 'ASC']], order: [['lessonNumber', 'ASC']],
attributes: ['id', 'lessonNumber', 'title'] attributes: ['id', 'lessonNumber', 'title', 'corePatterns']
}); });
return lessons.map(l => l.get({ plain: true })); return lessons.map(l => l.get({ plain: true }));
} }
@@ -3111,17 +3150,22 @@ export default class VocabService {
/** /**
* Sammelt alle Grammatik-Übungen aus vorherigen Lektionen für Wiederholungslektionen * Sammelt alle Grammatik-Übungen aus vorherigen Lektionen für Wiederholungslektionen
*/ */
async _getReviewVocabExercises(courseId, currentLessonNumber) { async _getReviewVocabExercises(courseId, currentLessonNumber, weekNumber = null) {
const previousLessons = await VocabCourseLesson.findAll({ const where = {
where: {
courseId: courseId, courseId: courseId,
lessonNumber: { lessonNumber: {
[Op.lt]: currentLessonNumber [Op.lt]: currentLessonNumber
}, },
lessonType: { lessonType: {
[Op.notIn]: ['review', 'vocab_review'] [Op.notIn]: ['review', 'vocab_review', 'weekly_review']
} }
}, };
if (weekNumber != null) {
where.weekNumber = weekNumber;
}
const previousLessons = await VocabCourseLesson.findAll({
where,
attributes: ['id'] attributes: ['id']
}); });
@@ -3156,6 +3200,16 @@ export default class VocabService {
return exercises.map(e => e.get({ plain: true })); return exercises.map(e => e.get({ plain: true }));
} }
_selectWeeklyReviewExamExercises(exercises = [], lessonId) {
const list = Array.isArray(exercises) ? exercises : [];
if (list.length === 0) return [];
const seed = (Number(lessonId) * 100003) >>> 0;
const percentage = 40 + (seed % 21);
const targetCount = Math.max(1, Math.ceil((list.length * percentage) / 100));
return this._seededShuffle(list.slice(), seed).slice(0, targetCount);
}
/** /**
* Sammelt GrammatikÜbungen aus vorherigen Lektionen derselben Woche * Sammelt GrammatikÜbungen aus vorherigen Lektionen derselben Woche
*/ */
@@ -3166,7 +3220,7 @@ export default class VocabService {
courseId: courseId, courseId: courseId,
weekNumber: weekNumber, weekNumber: weekNumber,
lessonNumber: { [Op.lt]: currentLessonNumber }, lessonNumber: { [Op.lt]: currentLessonNumber },
lessonType: { [Op.notIn]: ['review', 'vocab_review'] } lessonType: { [Op.notIn]: ['review', 'vocab_review', 'weekly_review'] }
}, },
attributes: ['id'] attributes: ['id']
}); });

View File

@@ -1200,7 +1200,7 @@ export default {
if (['dialogue', 'phrases', 'survival', 'grammar'].includes(lessonType)) { if (['dialogue', 'phrases', 'survival', 'grammar'].includes(lessonType)) {
return 1.2; return 1.2;
} }
if (lessonType === 'review' || lessonType === 'vocab_review') { if (lessonType === 'review' || lessonType === 'vocab_review' || lessonType === 'weekly_review') {
return 0.9; return 0.9;
} }
return 1; return 1;
@@ -1216,7 +1216,7 @@ export default {
}, },
trainerMinimumCurrentExposures() { trainerMinimumCurrentExposures() {
const mode = this.lessonPedagogy?.didacticMode || this.lesson?.lessonType || ''; const mode = this.lessonPedagogy?.didacticMode || this.lesson?.lessonType || '';
if (mode === 'intensive_review' || mode === 'review' || mode === 'vocab_review') { if (mode === 'intensive_review' || mode === 'review' || mode === 'vocab_review' || mode === 'weekly_review') {
return 2; return 2;
} }
if (['grammar', 'dialogue', 'phrases', 'survival'].includes(this.lesson?.lessonType)) { if (['grammar', 'dialogue', 'phrases', 'survival'].includes(this.lesson?.lessonType)) {
@@ -1243,8 +1243,8 @@ export default {
} }
const lessonType = String(this.lesson?.lessonType || '').toLowerCase(); const lessonType = String(this.lesson?.lessonType || '').toLowerCase();
const didacticMode = String(this.lessonPedagogy?.didacticMode || '').toLowerCase(); const didacticMode = String(this.lessonPedagogy?.didacticMode || '').toLowerCase();
const isReviewLesson = ['review', 'vocab_review'].includes(lessonType) const isReviewLesson = ['review', 'vocab_review', 'weekly_review'].includes(lessonType)
|| ['review', 'vocab_review', 'intensive_review'].includes(didacticMode); || ['review', 'vocab_review', 'weekly_review', 'intensive_review'].includes(didacticMode);
if (isReviewLesson) { if (isReviewLesson) {
// In Wiederholungslektionen soll altes Material frueher und staerker einfliesen. // In Wiederholungslektionen soll altes Material frueher und staerker einfliesen.
@@ -1266,8 +1266,8 @@ export default {
} }
const lessonType = String(this.lesson?.lessonType || '').toLowerCase(); const lessonType = String(this.lesson?.lessonType || '').toLowerCase();
const didacticMode = String(this.lessonPedagogy?.didacticMode || '').toLowerCase(); const didacticMode = String(this.lessonPedagogy?.didacticMode || '').toLowerCase();
const isReviewLesson = ['review', 'vocab_review'].includes(lessonType) const isReviewLesson = ['review', 'vocab_review', 'weekly_review'].includes(lessonType)
|| ['review', 'vocab_review', 'intensive_review'].includes(didacticMode); || ['review', 'vocab_review', 'weekly_review', 'intensive_review'].includes(didacticMode);
if (isReviewLesson) { if (isReviewLesson) {
return Math.max(0.25, 1 - this.currentReviewShare); return Math.max(0.25, 1 - this.currentReviewShare);
@@ -1329,7 +1329,7 @@ export default {
canAccessExercises() { canAccessExercises() {
if (!this.hasExercises) return false; if (!this.hasExercises) return false;
if (this.exerciseNeedsReinforcement) return false; if (this.exerciseNeedsReinforcement) return false;
const isReview = this.lesson?.lessonType === 'review' || this.lesson?.lessonType === 'vocab_review'; const isReview = ['review', 'vocab_review', 'weekly_review'].includes(this.lesson?.lessonType);
if (isReview) return true; if (isReview) return true;
if (this.trainableLessonVocab.length === 0 && this.prepItems.length > 0) { if (this.trainableLessonVocab.length === 0 && this.prepItems.length > 0) {
return this.lessonPrepStage >= 2; return this.lessonPrepStage >= 2;
@@ -1358,7 +1358,7 @@ export default {
/** Für Wiederholungslektionen: Übungen aus vorherigen Lektionen (Kapitelprüfung). Sonst: eigene Grammatik-Übungen. */ /** Für Wiederholungslektionen: Übungen aus vorherigen Lektionen (Kapitelprüfung). Sonst: eigene Grammatik-Übungen. */
effectiveExercises() { effectiveExercises() {
if (!this.lesson) return []; if (!this.lesson) return [];
const isReview = this.lesson.lessonType === 'review' || this.lesson.lessonType === 'vocab_review'; const isReview = ['review', 'vocab_review', 'weekly_review'].includes(this.lesson.lessonType);
if (isReview && this.lesson.reviewVocabExercises && Array.isArray(this.lesson.reviewVocabExercises) && this.lesson.reviewVocabExercises.length > 0) { if (isReview && this.lesson.reviewVocabExercises && Array.isArray(this.lesson.reviewVocabExercises) && this.lesson.reviewVocabExercises.length > 0) {
return this.lesson.reviewVocabExercises; return this.lesson.reviewVocabExercises;
} }
@@ -1504,9 +1504,10 @@ export default {
importantVocab() { importantVocab() {
// Extrahiere wichtige Begriffe aus den Übungen // Extrahiere wichtige Begriffe aus den Übungen
try { try {
// Bei Wiederholungslektionen: Verwende Vokabeln aus vorherigen Lektionen (effectiveExercises = reviewVocabExercises) // Wochenwiederholungen trainieren die vollständige Wochenmenge; geprüft wird nur eine Teilmenge.
// Normale Lektion: Verwende effectiveExercises (grammarExercises) const exercises = this.lesson?.lessonType === 'weekly_review'
const exercises = this.effectiveExercises; ? this.lesson.weeklyReviewTrainingExercises
: this.effectiveExercises;
if (!exercises || !Array.isArray(exercises) || exercises.length === 0) { if (!exercises || !Array.isArray(exercises) || exercises.length === 0) {
debugLog('[importantVocab] Keine Übungen vorhanden'); debugLog('[importantVocab] Keine Übungen vorhanden');
return []; return [];
@@ -3042,7 +3043,8 @@ export default {
conversation: this.$t('socialnetwork.vocab.courses.lessonTypeConversation'), conversation: this.$t('socialnetwork.vocab.courses.lessonTypeConversation'),
culture: this.$t('socialnetwork.vocab.courses.lessonTypeCulture'), culture: this.$t('socialnetwork.vocab.courses.lessonTypeCulture'),
review: this.$t('socialnetwork.vocab.courses.lessonTypeReview'), review: this.$t('socialnetwork.vocab.courses.lessonTypeReview'),
vocab_review: this.$t('socialnetwork.vocab.courses.lessonTypeReview') vocab_review: this.$t('socialnetwork.vocab.courses.lessonTypeReview'),
weekly_review: this.$t('socialnetwork.vocab.courses.lessonTypeReview')
}; };
return labels[lessonType] || lessonType || this.$t('socialnetwork.vocab.courses.lessonTypeVocab'); return labels[lessonType] || lessonType || this.$t('socialnetwork.vocab.courses.lessonTypeVocab');
}, },
@@ -3869,8 +3871,8 @@ export default {
const lessonType = String(this.lesson?.lessonType || '').toLowerCase(); const lessonType = String(this.lesson?.lessonType || '').toLowerCase();
const didacticMode = String(this.lessonPedagogy?.didacticMode || '').toLowerCase(); const didacticMode = String(this.lessonPedagogy?.didacticMode || '').toLowerCase();
const isReviewLesson = ['review', 'vocab_review'].includes(lessonType) const isReviewLesson = ['review', 'vocab_review', 'weekly_review'].includes(lessonType)
|| ['review', 'vocab_review', 'intensive_review'].includes(didacticMode); || ['review', 'vocab_review', 'weekly_review', 'intensive_review'].includes(didacticMode);
// Reviews sollen nicht nur aus Multiple Choice bestehen: // Reviews sollen nicht nur aus Multiple Choice bestehen:
// früherer Wechsel zu Typing, damit aktiver Abruf im Vordergrund steht. // früherer Wechsel zu Typing, damit aktiver Abruf im Vordergrund steht.
const switchAfterAttempts = isReviewLesson const switchAfterAttempts = isReviewLesson