feat(course): add Cebu travel path with lessons and didactics for Bisaya courses
All checks were successful
Deploy to production / deploy (push) Successful in 24s

This commit is contained in:
Torsten Schulz (local)
2026-09-02 14:03:39 +02:00
parent 504f527a3f
commit b2e5d3d950
6 changed files with 261 additions and 7 deletions

View File

@@ -0,0 +1,176 @@
/** Ergänzende Lektionen für Reisen und längere Aufenthalte auf Cebu. */
export const BISAYA_CEBU_TRAVEL_DIDACTICS = {
'Cebu ankommen & einchecken': {
learningGoals: [
'Bei der Ankunft höflich grüßen und einchecken.',
'Nach Zimmer und Schlüssel fragen.',
'Eine Buchung kurz bestätigen.'
],
corePatterns: [
{ target: 'Maayong adlaw.', gloss: 'Guten Tag.' },
{ target: 'Naa koy reservation.', gloss: 'Ich habe eine Reservierung.' },
{ target: 'Pwede ko mag-check in?', gloss: 'Kann ich einchecken?' },
{ target: 'Asa ang akong kwarto?', gloss: 'Wo ist mein Zimmer?' },
{ target: 'Salamat kaayo.', gloss: 'Vielen Dank.' }
],
speakingPrompts: [{
title: 'Ankunft im Hotel',
prompt: 'Begrüße die Rezeption, sage, dass du eine Reservierung hast, und frage nach deinem Zimmer.',
cue: 'Maayong adlaw. Naa koy reservation. Asa ang akong kwarto?'
}],
practicalTasks: [{
title: 'Check-in',
text: 'Führe einen kurzen Check-in mit Begrüßung, Reservierung und Zimmerfrage.'
}]
},
'Unterkunft & kleine Probleme': {
learningGoals: [
'Wichtige Wörter zur Unterkunft verstehen.',
'Freundlich nach Schlüssel, Wasser oder Handtuch fragen.',
'Ein kleines Problem im Zimmer kurz benennen.'
],
corePatterns: [
{ target: 'kwarto', gloss: 'Zimmer' },
{ target: 'yawe', gloss: 'Schlüssel' },
{ target: 'tuwalya', gloss: 'Handtuch' },
{ target: 'tubig', gloss: 'Wasser' },
{ target: 'Asa ang yawe sa kwarto?', gloss: 'Wo ist der Zimmerschlüssel?' },
{ target: 'Pwede mangayo ug tubig?', gloss: 'Kann ich um Wasser bitten?' },
{ target: 'Naay problema sa kwarto.', gloss: 'Es gibt ein Problem im Zimmer.' }
],
speakingPrompts: [{
title: 'An der Rezeption',
prompt: 'Frage nach dem Zimmerschlüssel und bitte anschließend um Wasser oder ein Handtuch.',
cue: 'Asa ang yawe sa kwarto? Pwede mangayo ug tubig?'
}],
practicalTasks: [{
title: 'Kleines Hotelproblem',
text: 'Beschreibe höflich ein kleines Problem im Zimmer und bitte um Hilfe.'
}]
},
'Restaurant & Bestellen': {
learningGoals: [
'Im Restaurant höflich bestellen.',
'Nach Wasser und Preis fragen.',
'Auf Essen freundlich reagieren.'
],
corePatterns: [
{ target: 'Gusto ko ani.', gloss: 'Ich möchte das.' },
{ target: 'Pwede mangayo ug tubig?', gloss: 'Kann ich um Wasser bitten?' },
{ target: 'Tagpila ni?', gloss: 'Wie viel kostet das?' },
{ target: 'Lami kaayo.', gloss: 'Sehr lecker.' },
{ target: 'Mubayad ko.', gloss: 'Ich möchte bezahlen.' }
],
speakingPrompts: [{
title: 'Bestellung',
prompt: 'Bestelle etwas, bitte um Wasser, frage nach dem Preis und reagiere auf das Essen.',
cue: 'Gusto ko ani. Pwede mangayo ug tubig? Tagpila ni? Lami kaayo.'
}],
practicalTasks: [{
title: 'Im Restaurant',
text: 'Führe eine kurze Bestellung vom Wunsch bis zum Bezahlen.'
}]
},
'Markt, Souvenirs & Bezahlen': {
learningGoals: [
'Am Markt nach Preis und Menge fragen.',
'Freundlich auf einen Preis reagieren.',
'Einen Einkauf abschließen.'
],
corePatterns: [
{ target: 'Pila ni tanan?', gloss: 'Wie viel kostet das alles?' },
{ target: 'Mahal ra.', gloss: 'Das ist teuer.' },
{ target: 'Barato ra.', gloss: 'Das ist günstig.' },
{ target: 'Kuhaon nako ni.', gloss: 'Ich nehme das.' },
{ target: 'Naay sukli?', gloss: 'Gibt es Wechselgeld?' }
],
speakingPrompts: [{
title: 'Souvenir kaufen',
prompt: 'Frage nach dem Gesamtpreis, reagiere freundlich und sage, dass du den Artikel nimmst.',
cue: 'Pila ni tanan? Mahal ra. Kuhaon nako ni.'
}],
practicalTasks: [{
title: 'Marktgespräch',
text: 'Führe ein kurzes, höfliches Gespräch beim Kauf eines Souvenirs.'
}]
},
'Ausflüge, Inseln & Transport': {
learningGoals: [
'Nach Hafen, Fahrt und Ausstieg fragen.',
'Einen einfachen Ausflug auf Cebu oder zu einer Insel planen.',
'Richtungs- und Transportwörter im Zusammenhang anwenden.'
],
corePatterns: [
{ target: 'Asa ang pantalan?', gloss: 'Wo ist der Hafen?' },
{ target: 'Kanus-a ang biyahe?', gloss: 'Wann ist die Fahrt?' },
{ target: 'Moadto ko sa isla.', gloss: 'Ich fahre auf die Insel.' },
{ target: 'Asa ta manaog?', gloss: 'Wo steigen wir aus?' },
{ target: 'Diretso lang.', gloss: 'Einfach geradeaus.' }
],
speakingPrompts: [{
title: 'Zum Hafen',
prompt: 'Frage nach dem Hafen und der Abfahrtszeit. Sage danach, dass du auf eine Insel fahren möchtest.',
cue: 'Asa ang pantalan? Kanus-a ang biyahe? Moadto ko sa isla.'
}],
practicalTasks: [{
title: 'Ausflug planen',
text: 'Plane einen kurzen Ausflug: Hafen finden, Fahrtzeit erfragen und richtig aussteigen.'
}]
},
'Hilfe unterwegs & Reiseprobleme': {
learningGoals: [
'In einer einfachen Problemsituation Hilfe holen.',
'Eine verlorene Tasche oder ein gesundheitliches Problem benennen.',
'Höflich und klar reagieren.'
],
corePatterns: [
{ target: 'Kinahanglan nako ug tabang.', gloss: 'Ich brauche Hilfe.' },
{ target: 'Nawala ang akong bag.', gloss: 'Meine Tasche ist verloren gegangen.' },
{ target: 'Asa ang botika?', gloss: 'Wo ist die Apotheke?' },
{ target: 'Tawag ug tabang, palihug.', gloss: 'Ruf bitte Hilfe.' },
{ target: 'Okay ra ko.', gloss: 'Mir geht es gut / Es ist in Ordnung.' }
],
speakingPrompts: [{
title: 'Unterwegs Hilfe holen',
prompt: 'Sage, dass du Hilfe brauchst, und erkläre kurz, ob du deine Tasche verloren hast oder eine Apotheke suchst.',
cue: 'Kinahanglan nako ug tabang. Nawala ang akong bag. Asa ang botika?'
}],
practicalTasks: [{
title: 'Notfall-Minirolle',
text: 'Bitte klar und höflich um Hilfe und nenne ein konkretes Problem.'
}]
},
'Cebu-Reisepfad Abschluss': {
learningGoals: [
'Die wichtigsten Reisesituationen auf Cebu zusammenhängend anwenden.',
'Bei Unterkunft, Essen, Transport und Hilfe selbstständig reagieren.',
'Höflichkeit und kurze, klare Sätze sicher einsetzen.'
],
corePatterns: [
{ target: 'Naa koy reservation.', gloss: 'Ich habe eine Reservierung.' },
{ target: 'Gusto ko ani.', gloss: 'Ich möchte das.' },
{ target: 'Asa ang pantalan?', gloss: 'Wo ist der Hafen?' },
{ target: 'Kinahanglan nako ug tabang.', gloss: 'Ich brauche Hilfe.' },
{ target: 'Amping.', gloss: 'Pass auf dich auf.' }
],
speakingPrompts: [{
title: 'Ein Reisetag auf Cebu',
prompt: 'Verbinde Unterkunft, Essen, Transport und Hilfe in einem kurzen Reisetag.',
cue: 'Naa koy reservation. Gusto ko ani. Asa ang pantalan? Amping.'
}],
practicalTasks: [{
title: 'Cebu-Reiseprobe',
text: 'Spiele einen Reisetag nach: einchecken, essen, zum Hafen fahren und bei Bedarf Hilfe holen.'
}]
}
};
export const BISAYA_CEBU_TRAVEL_LESSONS = [
{ week: 16, day: 2, num: 155, type: 'conversation', title: 'Cebu ankommen & einchecken', desc: 'Einchecken, Reservierung und Zimmerfrage auf Cebu', targetMin: 18, targetScore: 78, review: false, cultural: 'Ein höflicher Gruß und ein kurzes Salamat kaayo öffnen viele Gespräche freundlich.' },
{ week: 16, day: 2, num: 156, type: 'vocab', title: 'Unterkunft & kleine Probleme', desc: 'Zimmer, Schlüssel, Wasser und einfache Bitten an der Rezeption', targetMin: 16, targetScore: 82, review: true, cultural: null },
{ week: 16, day: 3, num: 157, type: 'conversation', title: 'Restaurant & Bestellen', desc: 'Bestellen, nach Wasser und Preis fragen und bezahlen', targetMin: 18, targetScore: 78, review: false, cultural: 'Lami kaayo ist eine warme, natürliche Reaktion auf gutes Essen.' },
{ week: 16, day: 3, num: 158, type: 'vocab', title: 'Markt, Souvenirs & Bezahlen', desc: 'Preise, Wechselgeld und höflicher Einkauf auf dem Markt', targetMin: 16, targetScore: 82, review: true, cultural: null },
{ week: 16, day: 4, num: 159, type: 'conversation', title: 'Ausflüge, Inseln & Transport', desc: 'Hafen, Fahrt und Ausstieg für Ausflüge auf Cebu', targetMin: 18, targetScore: 78, review: false, cultural: 'Bei Fahrten mit Jeepney, Bus oder Boot helfen kurze Orts- und Zeitfragen am meisten.' },
{ week: 16, day: 4, num: 160, type: 'conversation', title: 'Hilfe unterwegs & Reiseprobleme', desc: 'Tasche verloren, Apotheke finden und Hilfe holen', targetMin: 18, targetScore: 78, review: false, cultural: null },
{ week: 16, day: 5, num: 161, type: 'review', title: 'Cebu-Reisepfad Abschluss', desc: 'Prüfung und Wiederholung der wichtigsten Reisesituationen', targetMin: 22, targetScore: 82, review: false, cultural: 'Kurze, klare und höfliche Sätze sind unterwegs wirksamer als lange Erklärungen.' }
];

View File

@@ -18,6 +18,7 @@ import { BISAYA_DIDACTICS_24_43, BISAYA_LESSONS_24_43_BY_NUMBER, BISAYA_RELATION
import { BISAYA_PHASE3_DIDACTICS, BISAYA_PHASE3_LESSONS } from './bisaya-course-phase3-extension.js';
import { BISAYA_PHASE4_DIDACTICS, BISAYA_PHASE4_LESSONS } from './bisaya-course-phase4-extension.js';
import { BISAYA_PHASE5_DIDACTICS, BISAYA_PHASE5_LESSONS } from './bisaya-course-phase5-extension.js';
import { BISAYA_CEBU_TRAVEL_DIDACTICS, BISAYA_CEBU_TRAVEL_LESSONS } from './bisaya-course-cebu-travel-extension.js';
function withTypeName(exerciseTypeName, exercise) {
return {
@@ -33,7 +34,8 @@ const GENERATED_BISAYA_DIDACTICS = {
...BISAYA_DIDACTICS_24_43,
...BISAYA_PHASE3_DIDACTICS,
...BISAYA_PHASE4_DIDACTICS,
...BISAYA_PHASE5_DIDACTICS
...BISAYA_PHASE5_DIDACTICS,
...BISAYA_CEBU_TRAVEL_DIDACTICS
};
const SAFE_EXERCISE_UPDATE_TITLES = new Set([
@@ -49,7 +51,8 @@ const SAFE_EXERCISE_UPDATE_TITLES = new Set([
'Bitten & Fragen',
...BISAYA_PHASE3_LESSONS.map((lesson) => lesson.title),
...BISAYA_PHASE4_LESSONS.map((lesson) => lesson.title),
...BISAYA_PHASE5_LESSONS.map((lesson) => lesson.title)
...BISAYA_PHASE5_LESSONS.map((lesson) => lesson.title),
...BISAYA_CEBU_TRAVEL_LESSONS.map((lesson) => lesson.title)
]);
function normalizeText(value) {

View File

@@ -16,6 +16,7 @@ import { BISAYA_DIDACTICS_24_43, BISAYA_LESSONS_24_43, BISAYA_RELATIONSHIP_ANCHO
import { BISAYA_PHASE3_DIDACTICS, BISAYA_PHASE3_LESSONS } from './bisaya-course-phase3-extension.js';
import { BISAYA_PHASE4_DIDACTICS, BISAYA_PHASE4_LESSONS } from './bisaya-course-phase4-extension.js';
import { BISAYA_PHASE5_DIDACTICS, BISAYA_PHASE5_LESSONS } from './bisaya-course-phase5-extension.js';
import { BISAYA_CEBU_TRAVEL_DIDACTICS, BISAYA_CEBU_TRAVEL_LESSONS } from './bisaya-course-cebu-travel-extension.js';
const LESSON_DIDACTICS = {
'Begrüßungen & Höflichkeit': {
@@ -868,7 +869,8 @@ const LESSON_DIDACTICS = {
...BISAYA_DIDACTICS_24_43,
...BISAYA_PHASE3_DIDACTICS,
...BISAYA_PHASE4_DIDACTICS,
...BISAYA_PHASE5_DIDACTICS
...BISAYA_PHASE5_DIDACTICS,
...BISAYA_CEBU_TRAVEL_DIDACTICS
};
const LESSONS = [
@@ -993,10 +995,11 @@ const LESSONS = [
...BISAYA_PHASE3_LESSONS,
...BISAYA_PHASE4_LESSONS,
...BISAYA_PHASE5_LESSONS
...BISAYA_PHASE5_LESSONS,
...BISAYA_CEBU_TRAVEL_LESSONS
];
const BISAYA_FAMILY_COURSE_TITLE = 'Bisaya für Familien - Alltag & Stabilisierung';
const BISAYA_FAMILY_COURSE_TITLE = 'Bisaya für Cebu Reise, Familie & Alltag';
async function createBisayaCourse(languageId, ownerHashedId) {
try {
@@ -1034,7 +1037,7 @@ async function createBisayaCourse(languageId, ownerHashedId) {
const course = await VocabCourse.create({
ownerUserId: user.id,
title: BISAYA_FAMILY_COURSE_TITLE,
description: 'Lerne Bisaya (Cebuano) praxisnah für den Familienalltag. Der Pfad verbindet Schnellstart, Alltagsmodule und Stabilisierungsblöcke mit Spiralwiederholung, Fehlertraining und freier Produktion.',
description: 'Lerne Bisaya (Cebuano) praxisnah für Reisen und längere Aufenthalte auf Cebu mit Unterkunft, Essen, Markt, Ausflügen, Familie, Höflichkeit und Hilfe unterwegs.',
languageId: Number(languageId),
difficultyLevel: 1,
isPublic: true,

View File

@@ -0,0 +1,69 @@
#!/usr/bin/env node
/** Fügt Bisaya-Kursen den optionalen Reisepfad „Cebu Reise & Alltag“ hinzu. */
import { sequelize } from '../utils/sequelize.js';
import VocabCourse from '../models/community/vocab_course.js';
import VocabCourseLesson from '../models/community/vocab_course_lesson.js';
import { BISAYA_CEBU_TRAVEL_DIDACTICS, BISAYA_CEBU_TRAVEL_LESSONS } from './bisaya-course-cebu-travel-extension.js';
async function extendBisayaCourseForCebuTravel() {
await sequelize.authenticate();
const [language] = await sequelize.query(
`SELECT id FROM community.vocab_language WHERE name = 'Bisaya' LIMIT 1`,
{ type: sequelize.QueryTypes.SELECT }
);
if (!language) throw new Error('Bisaya-Sprache nicht gefunden.');
const courses = await VocabCourse.findAll({ where: { languageId: language.id } });
let addedLessons = 0;
for (const course of courses) {
await course.update({
title: 'Bisaya für Cebu Reise, Familie & Alltag',
description: 'Praxisnahes Cebuano für Reisen und längere Aufenthalte auf Cebu: Unterkunft, Essen, Markt, Ausflüge, Familie, Höflichkeit und Hilfe unterwegs.'
});
for (const lessonData of BISAYA_CEBU_TRAVEL_LESSONS) {
const existing = await VocabCourseLesson.findOne({
where: { courseId: course.id, lessonNumber: lessonData.num }
});
if (existing) continue;
const didactics = BISAYA_CEBU_TRAVEL_DIDACTICS[lessonData.title];
await VocabCourseLesson.create({
courseId: course.id,
chapterId: null,
lessonNumber: lessonData.num,
title: lessonData.title,
description: lessonData.desc,
weekNumber: lessonData.week,
dayNumber: lessonData.day,
lessonType: lessonData.type,
culturalNotes: lessonData.cultural,
learningGoals: didactics.learningGoals,
corePatterns: didactics.corePatterns,
grammarFocus: didactics.grammarFocus || [],
speakingPrompts: didactics.speakingPrompts || [],
practicalTasks: didactics.practicalTasks || [],
targetMinutes: lessonData.targetMin,
targetScorePercent: lessonData.targetScore,
requiresReview: lessonData.review,
didacticMode: 'travel_practice',
phaseLabel: 'Cebu Reise & Alltag',
blockNumber: 8,
difficultyWeight: 1,
newUnitTarget: 8,
reviewWeight: lessonData.review ? 2 : 1,
isIntensiveReview: false
});
addedLessons += 1;
console.log(`✅ Kurs ${course.id}: Lektion ${lessonData.num} ${lessonData.title} ergänzt`);
}
}
console.log(`Cebu-Reisepfad: ${addedLessons} Lektionen ergänzt.`);
}
extendBisayaCourseForCebuTravel()
.then(() => sequelize.close())
.catch((error) => {
console.error('❌ Fehler:', error);
sequelize.close();
process.exitCode = 1;
});

View File

@@ -27,6 +27,7 @@ const SAFE_SYNC_STEPS = {
'backend/scripts/extend-bisaya-course-phase3.js',
'backend/scripts/extend-bisaya-course-phase4.js',
'backend/scripts/extend-bisaya-course-phase5.js',
'backend/scripts/extend-bisaya-course-cebu-travel.js',
// Alte Zusatzlektionen 44/45 teilen Nummern mit Phase 3. Erst der
// Content-Schritt stellt ihre sichtbaren Zahlentitel wieder her; danach
// kann die Didaktik die Zahlmuster statt der Besuchsmuster einspielen.

View File

@@ -19,6 +19,7 @@ import {
import { BISAYA_PHASE3_DIDACTICS } from './bisaya-course-phase3-extension.js';
import { BISAYA_PHASE4_DIDACTICS } from './bisaya-course-phase4-extension.js';
import { BISAYA_PHASE5_DIDACTICS } from './bisaya-course-phase5-extension.js';
import { BISAYA_CEBU_TRAVEL_DIDACTICS } from './bisaya-course-cebu-travel-extension.js';
/** Alte Kurstitel → aktueller Schlüssel in LESSON_DIDACTICS (bestehende Datenbanken). */
export const LEGACY_DIDACTICS_TITLE_MAP = {
@@ -501,7 +502,8 @@ export const LESSON_DIDACTICS = {
...BISAYA_DIDACTICS_24_43,
...BISAYA_PHASE3_DIDACTICS,
...BISAYA_PHASE4_DIDACTICS,
...BISAYA_PHASE5_DIDACTICS
...BISAYA_PHASE5_DIDACTICS,
...BISAYA_CEBU_TRAVEL_DIDACTICS
};
function resolveDidacticsForLesson(lesson) {