Files
yourpart3/backend/scripts/create-bisaya-a2-course.js
Torsten Schulz (local) 3248579529 feat: add A2-B1 course expansion features including listening activities and conversation sessions
- Updated VocabCourseView.vue to display CEFR level for courses.
- Enhanced VocabLessonView.vue with new sections for listening activities and conversation labs, including audio controls and transcript toggling.
- Created migration to add new columns to vocab_course and new tables for conversation sessions and listening activities.
- Implemented models for vocab_conversation_session and vocab_listening_activity.
- Developed scripts to create new A2 and B1 bridge courses with structured lesson plans.
- Documented the A2-B1 expansion plan detailing course structure, learning goals, and assessment criteria.
2026-08-27 15:18:16 +02:00

91 lines
5.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* Erstellt den eigenständigen Anschlusskurs „Bisaya A2 Alltag & Gespräch“.
* Verwendung: node backend/scripts/create-bisaya-a2-course.js <ownerHashedId>
*/
import crypto from 'crypto';
import { sequelize } from '../utils/sequelize.js';
import User from '../models/community/user.js';
import VocabCourse from '../models/community/vocab_course.js';
import VocabCourseLesson from '../models/community/vocab_course_lesson.js';
const BLOCKS = [
['Wohnen & Reparaturen', 'Wohnung beschreiben, Probleme melden und eine Reparatur organisieren.', 'Naay problema sa balay.', 'Es gibt ein Problem im Haus.'],
['Arbeit & Tagesorganisation', 'Aufgaben, Arbeitszeiten und Prioritäten nachvollziehbar erklären.', 'Humanon nako una kini.', 'Ich mache das zuerst fertig.'],
['Schule & digitale Kommunikation', 'Schule, Nachrichten und einfache Absprachen verbinden.', 'Magpadala ko ug mensahe unya.', 'Ich schicke später eine Nachricht.'],
['Familie & Beziehungen', 'Gefühle, Wünsche und Rücksicht in längeren Antworten ausdrücken.', 'Importante ka para nako.', 'Du bist mir wichtig.'],
['Meinung & Entscheidungen', 'Zustimmen, begründen und höflich eine andere Meinung sagen.', 'Para nako, mas maayo kung…', 'Für mich wäre es besser, wenn …'],
['Erlebnisse erzählen', 'Erlebtes in sinnvoller Reihenfolge erzählen und nachfragen.', 'Pagkahuman, miadto mi sa merkado.', 'Danach gingen wir zum Markt.'],
['Reisen & Unterkunft', 'Fahrt, Unterkunft und Änderungen eines Plans klären.', 'Pwede ba nato usbon ang plano?', 'Können wir den Plan ändern?'],
['Geld, Dokumente & Dienste', 'Zahlung, Dokumente und öffentliche Erledigungen bewältigen.', 'Kinahanglan nako ang resibo.', 'Ich brauche die Quittung.'],
['Gesundheit & unerwartete Probleme', 'Beschwerden genauer beschreiben und Hilfe organisieren.', 'Dugay na ni nagsakit.', 'Das tut schon länger weh.'],
['A2-Transfer: Alltag', 'Mehrere Alltagsthemen ohne feste Reihenfolge verbinden.', 'Una, kinahanglan nato…', 'Zuerst müssen wir …'],
['A2-Transfer: Gespräch', 'Ein längeres Gespräch beginnen, tragen und freundlich beenden.', 'Nasabtan nako imong giingon.', 'Ich verstehe, was du gesagt hast.'],
['A2-Abschluss & Weiterlernen', 'Eigene Stärken und nächste Lernziele benennen.', 'Gusto pa ko mopadayon ug praktis.', 'Ich möchte weiter üben.']
];
const TYPES = ['vocab', 'grammar', 'conversation', 'listening', 'conversation', 'review'];
const SUFFIXES = ['Wortschatz im Kontext', 'Muster & Grammatik', 'Geführtes Gespräch', 'Hörverstehen', 'Rollenspiel', 'Checkpoint'];
async function main() {
const ownerHashedId = String(process.argv[2] || '').trim();
if (!ownerHashedId) throw new Error('Bitte ownerHashedId angeben.');
await sequelize.authenticate();
const owner = await User.findOne({ where: { hashedId: ownerHashedId } });
if (!owner) throw new Error('Nutzer nicht gefunden.');
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 baseCourse = await VocabCourse.findOne({
where: { languageId: language.id, courseKind: 'core' }, order: [['id', 'ASC']]
});
const [course] = await VocabCourse.findOrCreate({
where: { title: 'Bisaya A2 Alltag & Gespräch', languageId: language.id },
defaults: {
ownerUserId: owner.id,
description: 'Anschlusskurs für variablere Alltagssprache, Hörverstehen und längere Gespräche.',
nativeLanguageId: null,
difficultyLevel: 2,
cefrLevel: 'A2',
courseKind: 'continuation',
prerequisiteCourseId: baseCourse?.id || null,
isPublic: true,
shareCode: crypto.randomBytes(8).toString('hex')
}
});
let created = 0;
for (const [blockIndex, block] of BLOCKS.entries()) {
const [topic, goal, target, gloss] = block;
for (let offset = 0; offset < 6; offset += 1) {
const lessonNumber = blockIndex * 6 + offset + 1;
const [lesson, made] = await VocabCourseLesson.findOrCreate({
where: { courseId: course.id, lessonNumber },
defaults: {
courseId: course.id, chapterId: null, lessonNumber,
title: `${topic}: ${SUFFIXES[offset]}`,
description: goal,
weekNumber: blockIndex + 1, dayNumber: offset + 1,
lessonType: TYPES[offset], didacticMode: TYPES[offset] === 'review' ? 'checkpoint' : TYPES[offset] === 'listening' ? 'listening' : 'real_life_scenario',
phaseLabel: 'a2', blockNumber: blockIndex + 1, difficultyWeight: 2,
newUnitTarget: offset === 5 ? 0 : 6, reviewWeight: offset === 5 ? 70 : 35,
isIntensiveReview: offset === 5, targetMinutes: offset === 3 ? 25 : 30,
targetScorePercent: 80, requiresReview: offset === 5,
learningGoals: [goal, 'Mindestens vier eigene Sätze zum Thema formulieren.', 'Frühere Muster passend wiederverwenden.'],
corePatterns: [
{ target, gloss },
{ target: 'Pwede nimo ko tabangan?', gloss: 'Kannst du mir helfen?' },
{ target: 'Sa akong hunahuna, maayo ni.', gloss: 'Ich finde, das ist gut.' },
{ target: 'Kung mahimo, buhaton nato unya.', gloss: 'Wenn möglich, machen wir es später.' }
],
grammarFocus: [{ title: 'Muster im Kontext', text: 'Nutze die neuen Formen, um einen vollständigen Gedanken zu verbinden.', example: target }],
speakingPrompts: [{ title: 'Eigene Antwort', prompt: `${goal} Antworte frei in vier bis sechs Sätzen.`, cue: target }],
practicalTasks: [{ title: 'Transfer', text: 'Nutze ein Kernmuster heute in einer echten oder simulierten Nachricht.' }]
}
});
if (made) created += 1;
}
}
console.log(`A2-Kurs ${course.id}: ${created} Lektionen angelegt.`);
}
main().then(() => sequelize.close()).catch((error) => { console.error(error); sequelize.close(); process.exitCode = 1; });