From 14eb28d37ffd5f66a636c05856abc93e52b09cc4 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Mon, 19 Jan 2026 22:59:46 +0100 Subject: [PATCH] Refactor family vocabulary exercises generation for improved flexibility - Replaced static family vocabulary exercises with a dynamic structure that generates exercises based on native language input, enhancing adaptability for different languages. - Introduced a mapping for family words and their translations, allowing for a more comprehensive and organized approach to exercise creation. - Updated logging to include native language context during exercise generation, improving clarity for developers and users. - Streamlined the gap fill and transformation exercises to ensure accurate and relevant content for learners. --- .../scripts/update-family-words-exercises.js | 249 ++++++++++-------- update-vocab-courses.sh | 107 ++++++++ 2 files changed, 249 insertions(+), 107 deletions(-) create mode 100755 update-vocab-courses.sh diff --git a/backend/scripts/update-family-words-exercises.js b/backend/scripts/update-family-words-exercises.js index a3103cb..2451d59 100755 --- a/backend/scripts/update-family-words-exercises.js +++ b/backend/scripts/update-family-words-exercises.js @@ -11,133 +11,161 @@ import { sequelize } from '../utils/sequelize.js'; import VocabCourseLesson from '../models/community/vocab_course_lesson.js'; import VocabGrammarExercise from '../models/community/vocab_grammar_exercise.js'; +import VocabCourse from '../models/community/vocab_course.js'; import User from '../models/community/user.js'; -// Spezifische Übungen für Familienwörter -const FAMILY_WORDS_EXERCISES = [ - { - exerciseTypeId: 2, // multiple_choice - title: 'Wie sagt man "Mutter" auf Bisaya?', - instruction: 'Wähle die richtige Übersetzung.', - questionData: { - type: 'multiple_choice', - question: 'Wie sagt man "Mutter" auf Bisaya?', - options: ['Nanay', 'Tatay', 'Kuya', 'Ate'] - }, - answerData: { - type: 'multiple_choice', - correctAnswer: 0 - }, - explanation: '"Nanay" bedeutet "Mutter" auf Bisaya. "Mama" wird auch verwendet.' +// Familienwörter-Übersetzungen in verschiedene Muttersprachen +const FAMILY_WORDS = { + Mutter: { + de: 'Mutter', + en: 'Mother', + es: 'Madre', + fr: 'Mère', + it: 'Madre', + pt: 'Mãe' }, - { - exerciseTypeId: 2, // multiple_choice - title: 'Wie sagt man "Vater" auf Bisaya?', - instruction: 'Wähle die richtige Übersetzung.', - questionData: { - type: 'multiple_choice', - question: 'Wie sagt man "Vater" auf Bisaya?', - options: ['Tatay', 'Nanay', 'Kuya', 'Ate'] - }, - answerData: { - type: 'multiple_choice', - correctAnswer: 0 - }, - explanation: '"Tatay" bedeutet "Vater" auf Bisaya. "Papa" wird auch verwendet.' + Vater: { + de: 'Vater', + en: 'Father', + es: 'Padre', + fr: 'Père', + it: 'Padre', + pt: 'Pai' }, - { - exerciseTypeId: 2, // multiple_choice - title: 'Wie sagt man "älterer Bruder" auf Bisaya?', - instruction: 'Wähle die richtige Übersetzung.', - questionData: { - type: 'multiple_choice', - question: 'Wie sagt man "älterer Bruder" auf Bisaya?', - options: ['Kuya', 'Ate', 'Nanay', 'Tatay'] - }, - answerData: { - type: 'multiple_choice', - correctAnswer: 0 - }, - explanation: '"Kuya" bedeutet "älterer Bruder" auf Bisaya. Wird auch für respektvolle Anrede von älteren Männern verwendet.' + 'älterer Bruder': { + de: 'älterer Bruder', + en: 'older brother', + es: 'hermano mayor', + fr: 'frère aîné', + it: 'fratello maggiore', + pt: 'irmão mais velho' }, - { - exerciseTypeId: 2, // multiple_choice - title: 'Wie sagt man "ältere Schwester" auf Bisaya?', - instruction: 'Wähle die richtige Übersetzung.', - questionData: { - type: 'multiple_choice', - question: 'Wie sagt man "ältere Schwester" auf Bisaya?', - options: ['Ate', 'Kuya', 'Nanay', 'Tatay'] - }, - answerData: { - type: 'multiple_choice', - correctAnswer: 0 - }, - explanation: '"Ate" bedeutet "ältere Schwester" auf Bisaya. Wird auch für respektvolle Anrede von älteren Frauen verwendet.' + 'ältere Schwester': { + de: 'ältere Schwester', + en: 'older sister', + es: 'hermana mayor', + fr: 'sœur aînée', + it: 'sorella maggiore', + pt: 'irmã mais velha' }, - { - exerciseTypeId: 2, // multiple_choice - title: 'Wie sagt man "Großmutter" auf Bisaya?', - instruction: 'Wähle die richtige Übersetzung.', - questionData: { - type: 'multiple_choice', - question: 'Wie sagt man "Großmutter" auf Bisaya?', - options: ['Lola', 'Lolo', 'Nanay', 'Tatay'] - }, - answerData: { - type: 'multiple_choice', - correctAnswer: 0 - }, - explanation: '"Lola" bedeutet "Großmutter" auf Bisaya.' + Großmutter: { + de: 'Großmutter', + en: 'Grandmother', + es: 'Abuela', + fr: 'Grand-mère', + it: 'Nonna', + pt: 'Avó' }, - { - exerciseTypeId: 2, // multiple_choice - title: 'Wie sagt man "Großvater" auf Bisaya?', - instruction: 'Wähle die richtige Übersetzung.', - questionData: { - type: 'multiple_choice', - question: 'Wie sagt man "Großvater" auf Bisaya?', - options: ['Lolo', 'Lola', 'Nanay', 'Tatay'] - }, - answerData: { - type: 'multiple_choice', - correctAnswer: 0 - }, - explanation: '"Lolo" bedeutet "Großvater" auf Bisaya.' - }, - { + Großvater: { + de: 'Großvater', + en: 'Grandfather', + es: 'Abuelo', + fr: 'Grand-père', + it: 'Nonno', + pt: 'Avô' + } +}; + +// Bisaya-Übersetzungen +const BISAYA_TRANSLATIONS = { + 'Mutter': 'Nanay', + 'Vater': 'Tatay', + 'älterer Bruder': 'Kuya', + 'ältere Schwester': 'Ate', + 'Großmutter': 'Lola', + 'Großvater': 'Lolo' +}; + +// Sprach-Codes für Mapping +const LANGUAGE_CODES = { + 'Deutsch': 'de', + 'English': 'en', + 'Español': 'es', + 'Français': 'fr', + 'Italiano': 'it', + 'Português': 'pt', + 'Spanish': 'es', + 'French': 'fr', + 'Italian': 'it', + 'Portuguese': 'pt' +}; + +// Erstelle Übungen basierend auf Muttersprache +function createFamilyWordsExercises(nativeLanguageName) { + const langCode = LANGUAGE_CODES[nativeLanguageName] || 'de'; // Fallback zu Deutsch + + const exercises = []; + + // Multiple Choice Übungen für jedes Familienwort + const familyWords = Object.keys(FAMILY_WORDS); + const bisayaWords = ['Nanay', 'Tatay', 'Kuya', 'Ate', 'Lola', 'Lolo']; + + familyWords.forEach((key, index) => { + const nativeWord = FAMILY_WORDS[key][langCode]; + const bisayaWord = BISAYA_TRANSLATIONS[key]; + + // Erstelle Multiple Choice mit falschen Antworten + const wrongAnswers = bisayaWords.filter(w => w !== bisayaWord); + const shuffledWrong = wrongAnswers.sort(() => Math.random() - 0.5).slice(0, 3); + const options = [bisayaWord, ...shuffledWrong].sort(() => Math.random() - 0.5); + const correctIndex = options.indexOf(bisayaWord); + + exercises.push({ + exerciseTypeId: 2, // multiple_choice + title: `Wie sagt man "${nativeWord}" auf Bisaya?`, + instruction: 'Wähle die richtige Übersetzung.', + questionData: { + type: 'multiple_choice', + question: `Wie sagt man "${nativeWord}" auf Bisaya?`, + options: options + }, + answerData: { + type: 'multiple_choice', + correctAnswer: correctIndex + }, + explanation: `"${bisayaWord}" bedeutet "${nativeWord}" auf Bisaya.` + }); + }); + + // Gap Fill Übung + const nativeWords = familyWords.map(key => FAMILY_WORDS[key][langCode]); + exercises.push({ exerciseTypeId: 1, // gap_fill title: 'Familienwörter vervollständigen', - instruction: 'Fülle die Lücken mit den richtigen Bisaya-Familienwörtern.', + instruction: `Fülle die Lücken mit den richtigen Bisaya-Familienwörtern.`, questionData: { type: 'gap_fill', - text: '{gap} (Mutter) | {gap} (Vater) | {gap} (älterer Bruder) | {gap} (ältere Schwester) | {gap} (Großmutter) | {gap} (Großvater)', - gaps: 6 + text: familyWords.map((key, i) => `{gap} (${nativeWords[i]})`).join(' | '), + gaps: familyWords.length }, answerData: { type: 'gap_fill', - answers: ['Nanay', 'Tatay', 'Kuya', 'Ate', 'Lola', 'Lolo'] + answers: bisayaWords }, - explanation: 'Nanay = Mutter, Tatay = Vater, Kuya = älterer Bruder, Ate = ältere Schwester, Lola = Großmutter, Lolo = Großvater.' - }, - { + explanation: bisayaWords.map((bw, i) => `${bw} = ${nativeWords[i]}`).join(', ') + '.' + }); + + // Transformation Übung + exercises.push({ exerciseTypeId: 4, // transformation title: 'Familienwörter übersetzen', - instruction: 'Übersetze das Familienwort ins Bisaya.', + instruction: `Übersetze das Familienwort ins Bisaya.`, questionData: { type: 'transformation', - text: 'Mutter', - sourceLanguage: 'Deutsch', + text: nativeWords[0], // Erste Muttersprache als Beispiel + sourceLanguage: nativeLanguageName || 'Deutsch', targetLanguage: 'Bisaya' }, answerData: { type: 'transformation', - correct: 'Nanay', - alternatives: ['Mama', 'Nanay', 'Inahan'] + correct: bisayaWords[0], + alternatives: [bisayaWords[0]] // Nur die korrekte Antwort }, - explanation: '"Nanay" oder "Mama" bedeutet "Mutter" auf Bisaya.' - } -]; + explanation: `"${bisayaWords[0]}" bedeutet "${nativeWords[0]}" auf Bisaya.` + }); + + return exercises; +} async function findOrCreateSystemUser() { let systemUser = await User.findOne({ @@ -183,7 +211,10 @@ async function updateFamilyWordsExercises() { } const courses = await sequelize.query( - `SELECT id, title, owner_user_id FROM community.vocab_course WHERE language_id = :languageId`, + `SELECT c.id, c.title, c.owner_user_id, c.native_language_id, nl.name as native_language_name + FROM community.vocab_course c + LEFT JOIN community.vocab_language nl ON c.native_language_id = nl.id + WHERE c.language_id = :languageId`, { replacements: { languageId: bisayaLanguage.id }, type: sequelize.QueryTypes.SELECT @@ -196,7 +227,11 @@ async function updateFamilyWordsExercises() { let totalLessonsUpdated = 0; for (const course of courses) { - console.log(`📚 Kurs: ${course.title} (ID: ${course.id})`); + const nativeLangName = course.native_language_name || 'Deutsch'; // Fallback zu Deutsch + console.log(`📚 Kurs: ${course.title} (ID: ${course.id}, Muttersprache: ${nativeLangName})`); + + // Erstelle Übungen für diese Muttersprache + const exercises = createFamilyWordsExercises(nativeLangName); // Finde "Familienwörter"-Lektionen const lessons = await VocabCourseLesson.findAll({ @@ -219,7 +254,7 @@ async function updateFamilyWordsExercises() { // Erstelle neue Übungen let exerciseNumber = 1; - for (const exerciseData of FAMILY_WORDS_EXERCISES) { + for (const exerciseData of exercises) { await VocabGrammarExercise.create({ lessonId: lesson.id, exerciseTypeId: exerciseData.exerciseTypeId, @@ -234,7 +269,7 @@ async function updateFamilyWordsExercises() { totalExercisesUpdated++; } - console.log(` ✅ Lektion ${lesson.lessonNumber}: "${lesson.title}" - ${FAMILY_WORDS_EXERCISES.length} neue Übung(en) erstellt`); + console.log(` ✅ Lektion ${lesson.lessonNumber}: "${lesson.title}" - ${exercises.length} neue Übung(en) erstellt (${nativeLangName} → Bisaya)`); totalLessonsUpdated++; } diff --git a/update-vocab-courses.sh b/update-vocab-courses.sh new file mode 100755 index 0000000..ed2fde1 --- /dev/null +++ b/update-vocab-courses.sh @@ -0,0 +1,107 @@ +#!/bin/bash +# Script zum Aktualisieren der Sprachkurse +# Führt Git-Fetch/Pull aus und aktualisiert die Kurs-Übungen +# Findet automatisch die neuesten/relevanten Script-Dateien + +# Kein "set -e", damit Scripts auch bei Fehlern weiterlaufen können + +echo "🔄 Starte Update der Sprachkurse..." +echo "" + +# Prüfe ob wir im richtigen Verzeichnis sind +if [ ! -f "package.json" ]; then + echo "❌ Fehler: Bitte im Projekt-Root-Verzeichnis ausführen" + exit 1 +fi + +# 1. Git Fetch und Pull +echo "📥 Führe Git Fetch aus..." +git fetch + +echo "📥 Führe Git Pull aus..." +git pull + +echo "" +echo "✅ Git Update abgeschlossen" +echo "" + +# 2. Wechsle ins Backend-Verzeichnis +cd backend + +# 3. Finde relevante Script-Dateien +# Suche nach Scripts, die mit Sprachkursen zu tun haben +echo "🔍 Suche nach relevanten Script-Dateien..." +echo "" + +SCRIPT_DIR="scripts" +RELEVANT_KEYWORDS=("vocab" "course" "lesson" "exercise" "bisaya" "family" "survival" "grammar" "update" "create") + +# Finde alle .js-Dateien im scripts-Verzeichnis, die relevante Keywords enthalten +# Sortiere nach Änderungsdatum (neueste zuerst) +TEMP_SCRIPT_LIST=$(mktemp) +find "$SCRIPT_DIR" -name "*.js" -type f | while read -r file; do + filename=$(basename "$file" .js) + lowercase=$(echo "$filename" | tr '[:upper:]' '[:lower:]') + for keyword in "${RELEVANT_KEYWORDS[@]}"; do + if [[ "$lowercase" == *"$keyword"* ]]; then + echo "$file" + break + fi + done +done | xargs ls -t 2>/dev/null > "$TEMP_SCRIPT_LIST" || true + +SCRIPT_FILES=$(cat "$TEMP_SCRIPT_LIST" 2>/dev/null || true) +rm -f "$TEMP_SCRIPT_LIST" + +if [ -z "$SCRIPT_FILES" ]; then + echo "⚠️ Keine relevanten Script-Dateien gefunden" + echo "" +else + echo "📋 Gefundene Script-Dateien (nach Änderungsdatum sortiert):" + while IFS= read -r script; do + if [ -n "$script" ] && [ -f "$script" ]; then + echo " - $script" + fi + done <<< "$SCRIPT_FILES" + echo "" + + # 4. Führe die Scripts aus + echo "▶️ Starte Ausführung der Scripts..." + echo "" + + EXECUTED_COUNT=0 + FAILED_COUNT=0 + + while IFS= read -r script; do + if [ -n "$script" ] && [ -f "$script" ]; then + script_name=$(basename "$script") + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "📝 Führe aus: $script_name" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + if sudo -u yourpart node "$script"; then + echo "✅ $script_name erfolgreich abgeschlossen" + EXECUTED_COUNT=$((EXECUTED_COUNT + 1)) + else + EXIT_CODE=$? + echo "❌ $script_name fehlgeschlagen (Exit Code: $EXIT_CODE)" + FAILED_COUNT=$((FAILED_COUNT + 1)) + # Setze nicht -e, damit weitere Scripts ausgeführt werden können + fi + echo "" + fi + done <<< "$SCRIPT_FILES" + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "🎉 Update der Sprachkurse abgeschlossen!" + echo "" + echo "📊 Zusammenfassung:" + echo " - Git-Repository aktualisiert" + echo " - $EXECUTED_COUNT Script(s) erfolgreich ausgeführt" + if [ $FAILED_COUNT -gt 0 ]; then + echo " - ⚠️ $FAILED_COUNT Script(s) fehlgeschlagen" + fi + echo "" +fi + +echo "💡 Hinweis: Das Frontend muss separat aktualisiert werden mit: ./deploy-frontend.sh"