From 3ff8e4fc407ce80caa20eb44ed480ca2d4b83442 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Wed, 1 Apr 2026 15:52:53 +0200 Subject: [PATCH] refactor(bisaya-course): enhance pedagogy logic for German lessons - Updated the `getGermanForBisayaLessonPedagogy` function to include lesson titles for improved didactic mode determination, specifically adding support for 'contrast_training'. - Modified multiple scripts to utilize the updated pedagogy function, ensuring consistent application of the new logic across various course phases. - Enhanced the VocabService to recognize and handle 'contrast_training' as a didactic mode, improving lesson management and user experience. - Updated UI components to reflect the new didactic mode, ensuring clarity in lesson presentation. --- .../create-german-for-bisaya-course.js | 2 +- .../extend-german-for-bisaya-course-phase3.js | 2 +- .../extend-german-for-bisaya-course-phase4.js | 2 +- .../extend-german-for-bisaya-course-phase5.js | 2 +- .../german-for-bisaya-phase2-pedagogy.js | 27 +++++++++++++++- backend/services/vocabService.js | 32 +++++++++++++++++-- frontend/src/views/social/VocabCourseView.vue | 2 ++ frontend/src/views/social/VocabLessonView.vue | 2 ++ 8 files changed, 63 insertions(+), 8 deletions(-) diff --git a/backend/scripts/create-german-for-bisaya-course.js b/backend/scripts/create-german-for-bisaya-course.js index 3c2c4eb..65a5c6c 100644 --- a/backend/scripts/create-german-for-bisaya-course.js +++ b/backend/scripts/create-german-for-bisaya-course.js @@ -106,7 +106,7 @@ async function createGermanForBisayaCourse(ownerHashedId) { for (const lessonData of ALL_GERMAN_FOR_BISAYA_LESSONS) { const didactics = ALL_GERMAN_FOR_BISAYA_DIDACTICS[lessonData.title] || {}; - const pedagogy = getGermanForBisayaLessonPedagogy(lessonData.num, lessonData.type); + const pedagogy = getGermanForBisayaLessonPedagogy(lessonData.num, lessonData.type, lessonData.title); await VocabCourseLesson.create({ courseId: course.id, diff --git a/backend/scripts/extend-german-for-bisaya-course-phase3.js b/backend/scripts/extend-german-for-bisaya-course-phase3.js index f20e836..a8c4b64 100644 --- a/backend/scripts/extend-german-for-bisaya-course-phase3.js +++ b/backend/scripts/extend-german-for-bisaya-course-phase3.js @@ -37,7 +37,7 @@ async function extendGermanForBisayaPhase3() { if (existing) continue; const didactics = GERMAN_FOR_BISAYA_PHASE3_DIDACTICS[lessonData.title] || {}; - const pedagogy = getGermanForBisayaLessonPedagogy(lessonData.num, lessonData.type); + const pedagogy = getGermanForBisayaLessonPedagogy(lessonData.num, lessonData.type, lessonData.title); await VocabCourseLesson.create({ courseId: course.id, diff --git a/backend/scripts/extend-german-for-bisaya-course-phase4.js b/backend/scripts/extend-german-for-bisaya-course-phase4.js index acdd381..e275e48 100644 --- a/backend/scripts/extend-german-for-bisaya-course-phase4.js +++ b/backend/scripts/extend-german-for-bisaya-course-phase4.js @@ -37,7 +37,7 @@ async function extendGermanForBisayaPhase4() { if (existing) continue; const didactics = GERMAN_FOR_BISAYA_PHASE4_DIDACTICS[lessonData.title] || {}; - const pedagogy = getGermanForBisayaLessonPedagogy(lessonData.num, lessonData.type); + const pedagogy = getGermanForBisayaLessonPedagogy(lessonData.num, lessonData.type, lessonData.title); await VocabCourseLesson.create({ courseId: course.id, diff --git a/backend/scripts/extend-german-for-bisaya-course-phase5.js b/backend/scripts/extend-german-for-bisaya-course-phase5.js index 3d01e26..3fc8c2d 100644 --- a/backend/scripts/extend-german-for-bisaya-course-phase5.js +++ b/backend/scripts/extend-german-for-bisaya-course-phase5.js @@ -37,7 +37,7 @@ async function extendGermanForBisayaPhase5() { if (existing) continue; const didactics = GERMAN_FOR_BISAYA_PHASE5_DIDACTICS[lessonData.title] || {}; - const pedagogy = getGermanForBisayaLessonPedagogy(lessonData.num, lessonData.type); + const pedagogy = getGermanForBisayaLessonPedagogy(lessonData.num, lessonData.type, lessonData.title); await VocabCourseLesson.create({ courseId: course.id, diff --git a/backend/scripts/german-for-bisaya-phase2-pedagogy.js b/backend/scripts/german-for-bisaya-phase2-pedagogy.js index 29ec50b..1f70e97 100644 --- a/backend/scripts/german-for-bisaya-phase2-pedagogy.js +++ b/backend/scripts/german-for-bisaya-phase2-pedagogy.js @@ -1,14 +1,37 @@ -export function getGermanForBisayaLessonPedagogy(lessonNumber, lessonType) { +function isContrastTrainingLesson(lessonType, lessonTitle = '') { + const normalizedType = String(lessonType || '').toLowerCase(); + if (normalizedType !== 'grammar') { + return false; + } + + const title = String(lessonTitle || '').toLowerCase(); + return [ + 'kontrast', + 'fehlertraining', + ' / ', + 'nicht / kein', + 'der / die / das', + 'wo / wohin', + 'du / sie', + 'haben / sein', + 'ich bin / ich habe', + 'ich bin / ich heiße / ich komme' + ].some((marker) => title.includes(marker)); +} + +export function getGermanForBisayaLessonPedagogy(lessonNumber, lessonType, lessonTitle = '') { const phaseLabel = lessonNumber <= 60 ? 'quickstart' : lessonNumber <= 120 ? 'daily_life' : 'stabilization'; const blockNumber = Math.ceil(lessonNumber / 10); let didacticMode = 'core_input'; if (lessonType === 'conversation') didacticMode = 'guided_dialogue'; if (lessonType === 'grammar') didacticMode = 'pattern_drill'; + if (isContrastTrainingLesson(lessonType, lessonTitle)) didacticMode = 'contrast_training'; if (lessonType === 'review') didacticMode = 'intensive_review'; if (lessonType === 'culture') didacticMode = 'real_life_scenario'; const difficultyWeight = + didacticMode === 'contrast_training' ? 3 : lessonType === 'grammar' ? 3 : lessonType === 'review' ? 2 : lessonType === 'conversation' ? 2 : @@ -16,6 +39,7 @@ export function getGermanForBisayaLessonPedagogy(lessonNumber, lessonType) { const newUnitTarget = lessonType === 'review' ? 2 : + didacticMode === 'contrast_training' ? 3 : lessonType === 'grammar' ? 4 : phaseLabel === 'quickstart' ? 6 : phaseLabel === 'daily_life' ? 5 : @@ -23,6 +47,7 @@ export function getGermanForBisayaLessonPedagogy(lessonNumber, lessonType) { const reviewWeight = lessonType === 'review' ? 90 : + didacticMode === 'contrast_training' ? 70 : lessonType === 'grammar' ? 60 : lessonType === 'vocab' ? 55 : lessonType === 'culture' ? 20 : diff --git a/backend/services/vocabService.js b/backend/services/vocabService.js index e797ba6..7b11b30 100644 --- a/backend/services/vocabService.js +++ b/backend/services/vocabService.js @@ -620,11 +620,32 @@ export default class VocabService { } _inferLessonDidacticMode(plainLesson) { - if (plainLesson.didacticMode) { - return plainLesson.didacticMode; - } const lessonType = String(plainLesson.lessonType || '').toLowerCase(); const title = String(plainLesson.title || '').toLowerCase(); + const storedMode = String(plainLesson.didacticMode || '').trim(); + + const isContrastTraining = lessonType === 'grammar' && [ + 'kontrast', + 'fehlertraining', + ' / ', + 'nicht / kein', + 'der / die / das', + 'wo / wohin', + 'du / sie', + 'haben / sein', + 'ich bin / ich habe', + 'ich bin / ich heiße / ich komme' + ].some((marker) => title.includes(marker)); + + if (storedMode && storedMode !== 'pattern_drill') { + return storedMode; + } + if (isContrastTraining) { + return 'contrast_training'; + } + if (storedMode) { + return storedMode; + } if (title.includes('abschluss') || title.includes('prüfung') || title.includes('test')) { return 'checkpoint'; } @@ -648,6 +669,7 @@ export default class VocabService { return plainLesson.difficultyWeight; } switch (didacticMode) { + case 'contrast_training': case 'pattern_drill': return 3; case 'guided_dialogue': @@ -666,6 +688,8 @@ export default class VocabService { return plainLesson.newUnitTarget; } switch (didacticMode) { + case 'contrast_training': + return 3; case 'core_input': return 8; case 'guided_dialogue': @@ -692,6 +716,8 @@ export default class VocabService { return 90; case 'checkpoint': return 70; + case 'contrast_training': + return 70; case 'pattern_drill': return 55; case 'real_life_scenario': diff --git a/frontend/src/views/social/VocabCourseView.vue b/frontend/src/views/social/VocabCourseView.vue index 3c0b7e1..5c9e8ec 100644 --- a/frontend/src/views/social/VocabCourseView.vue +++ b/frontend/src/views/social/VocabCourseView.vue @@ -369,6 +369,8 @@ export default { return 'Neuer Stoff'; case 'guided_dialogue': return 'Geführter Dialog'; + case 'contrast_training': + return 'Kontrasttraining'; case 'pattern_drill': return 'Mustertraining'; case 'real_life_scenario': diff --git a/frontend/src/views/social/VocabLessonView.vue b/frontend/src/views/social/VocabLessonView.vue index 74bf20b..afa87a4 100644 --- a/frontend/src/views/social/VocabLessonView.vue +++ b/frontend/src/views/social/VocabLessonView.vue @@ -1972,6 +1972,8 @@ export default { return 'Neuer Stoff'; case 'guided_dialogue': return 'Geführter Dialog'; + case 'contrast_training': + return 'Kontrasttraining'; case 'pattern_drill': return 'Mustertraining'; case 'real_life_scenario':