Files
yourpart3/backend/scripts/create-bisaya-b1-bridge-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

78 lines
5.6 KiB
JavaScript
Raw Permalink 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 die B1-Brücke als eigenständigen Folgepfad (96 Lektionen). */
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 = [
['Arbeit & Bildung', 'Arbeitsaufgaben, Lernen und Ziele vergleichen und begründen.'],
['Wohnen & Gemeinschaft', 'Wohnen, Nachbarschaft und gemeinsame Regeln erläutern.'],
['Gesellschaft & Dienste', 'Öffentliche Themen verständlich erklären und nachfragen.'],
['Medien & digitale Welt', 'Informationen zusammenfassen und vorsichtig bewerten.'],
['Reise & Erfahrungen', 'Erlebnisse zusammenhängend und nachvollziehbar erzählen.'],
['Kultur & Gewohnheiten', 'Unterschiede respektvoll vergleichen und einordnen.'],
['Meinungen & Gründe', 'Eine Position begründen und auf Gegenargumente eingehen.'],
['Konflikt & Verhandlung', 'Interessen erklären, Kompromisse vorschlagen und reagieren.'],
['Hypothesen & Möglichkeiten', 'Bedingungen, Folgen und unsichere Pläne ausdrücken.'],
['Erzählungen vertiefen', 'Hintergrund, Reihenfolge und persönliche Reaktion verbinden.'],
['Längere Gespräche', 'Themenwechsel erkennen und das Gespräch aktiv tragen.'],
['B1-Transfer I', 'Mehrere vertraute Themen spontan verknüpfen.'],
['B1-Transfer II', 'Eine Erfahrung, Meinung und Lösung verbinden.'],
['Fehlertraining', 'Wiederkehrende Fokus-, Aspekt- und Verknüpfungsfehler reduzieren.'],
['Prüfungsszenen', 'Gespräche, Hörtexte und schriftliche Antworten unter Zeitdruck bewältigen.'],
['Weiterlernen', 'Persönliche Lücken erkennen und einen realistischen Übungsplan erstellen.']
];
const KINDS = ['vocab', 'grammar', 'conversation', 'listening', 'conversation', 'review'];
const NAMES = ['Ausdrucksmittel', 'Grammatik im Zusammenhang', 'Gespräch mit Standpunkt', 'Hörverstehen', 'Rollenspiel', 'Transfer-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 } });
const [language] = await sequelize.query("SELECT id FROM community.vocab_language WHERE name = 'Bisaya' LIMIT 1", { type: sequelize.QueryTypes.SELECT });
if (!owner || !language) throw new Error('Nutzer oder Bisaya-Sprache nicht gefunden.');
const a2 = await VocabCourse.findOne({ where: { title: 'Bisaya A2 Alltag & Gespräch', languageId: language.id } });
const [course] = await VocabCourse.findOrCreate({
where: { title: 'Bisaya B1-Brücke Meinung & freie Gespräche', languageId: language.id },
defaults: {
ownerUserId: owner.id, description: 'B1-Teilkompetenzen für vertraute Themen: Erzählungen, Begründungen und längere Gespräche.',
difficultyLevel: 3, cefrLevel: 'B1-bridge', courseKind: 'continuation', prerequisiteCourseId: a2?.id || null,
isPublic: true, shareCode: crypto.randomBytes(8).toString('hex')
}
});
let created = 0;
for (const [blockIndex, [topic, goal]] of BLOCKS.entries()) {
for (let offset = 0; offset < 6; offset += 1) {
const lessonNumber = blockIndex * 6 + offset + 1;
const [, made] = await VocabCourseLesson.findOrCreate({
where: { courseId: course.id, lessonNumber },
defaults: {
courseId: course.id, chapterId: null, lessonNumber, weekNumber: blockIndex + 1, dayNumber: offset + 1,
title: `${topic}: ${NAMES[offset]}`, description: goal, lessonType: KINDS[offset],
didacticMode: offset === 5 ? 'checkpoint' : KINDS[offset] === 'listening' ? 'listening' : 'real_life_scenario',
phaseLabel: 'b1_bridge', blockNumber: blockIndex + 1, difficultyWeight: 3, newUnitTarget: offset === 5 ? 0 : 7,
reviewWeight: offset === 5 ? 75 : 35, isIntensiveReview: offset === 5, targetMinutes: 35, targetScorePercent: 80, requiresReview: offset === 5,
learningGoals: [goal, 'Eine begründete Antwort mit mindestens acht Sätzen formulieren.', 'Ein Gegenargument aufgreifen oder eine Rückfrage stellen.'],
corePatterns: [
{ target: 'Sa akong panan-aw, importante kini tungod kay…', gloss: 'Meiner Ansicht nach ist das wichtig, weil …' },
{ target: 'Bisan pa man, mahimo pud nga…', gloss: 'Trotzdem kann es auch sein, dass …' },
{ target: 'Kung mao kana ang kahimtang, mas maayo nga…', gloss: 'Wenn das die Situation ist, wäre es besser, dass …' },
{ target: 'Nasabtan nako, apan lahi ang akong kasinatian.', gloss: 'Ich verstehe das, aber meine Erfahrung ist anders.' }
],
grammarFocus: [{ title: 'Verknüpfte Gedanken', text: 'Verbinde Standpunkt, Grund und mögliche Folge in einem zusammenhängenden Beitrag.', example: 'Sa akong panan-aw, importante kini tungod kay…' }],
speakingPrompts: [{ title: 'Standpunkt entwickeln', prompt: `${goal} Nenne eine Meinung, zwei Gründe und reagiere auf eine mögliche Gegenposition.`, cue: 'Sa akong panan-aw… Bisan pa man…' }],
practicalTasks: [{ title: 'Echte Anwendung', text: 'Führe eine fünfminütige Unterhaltung mit einer Person oder dokumentiere, welche Stelle noch zu schwer war.' }]
}
});
if (made) created += 1;
}
}
console.log(`B1-Brücke ${course.id}: ${created} Lektionen angelegt.`);
}
main().then(() => sequelize.close()).catch((error) => { console.error(error); sequelize.close(); process.exitCode = 1; });