- 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.
78 lines
5.6 KiB
JavaScript
78 lines
5.6 KiB
JavaScript
#!/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; });
|