#!/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; });