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.
This commit is contained in:
@@ -11,133 +11,161 @@
|
|||||||
import { sequelize } from '../utils/sequelize.js';
|
import { sequelize } from '../utils/sequelize.js';
|
||||||
import VocabCourseLesson from '../models/community/vocab_course_lesson.js';
|
import VocabCourseLesson from '../models/community/vocab_course_lesson.js';
|
||||||
import VocabGrammarExercise from '../models/community/vocab_grammar_exercise.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';
|
import User from '../models/community/user.js';
|
||||||
|
|
||||||
// Spezifische Übungen für Familienwörter
|
// Familienwörter-Übersetzungen in verschiedene Muttersprachen
|
||||||
const FAMILY_WORDS_EXERCISES = [
|
const FAMILY_WORDS = {
|
||||||
{
|
Mutter: {
|
||||||
exerciseTypeId: 2, // multiple_choice
|
de: 'Mutter',
|
||||||
title: 'Wie sagt man "Mutter" auf Bisaya?',
|
en: 'Mother',
|
||||||
instruction: 'Wähle die richtige Übersetzung.',
|
es: 'Madre',
|
||||||
questionData: {
|
fr: 'Mère',
|
||||||
type: 'multiple_choice',
|
it: 'Madre',
|
||||||
question: 'Wie sagt man "Mutter" auf Bisaya?',
|
pt: 'Mãe'
|
||||||
options: ['Nanay', 'Tatay', 'Kuya', 'Ate']
|
|
||||||
},
|
|
||||||
answerData: {
|
|
||||||
type: 'multiple_choice',
|
|
||||||
correctAnswer: 0
|
|
||||||
},
|
|
||||||
explanation: '"Nanay" bedeutet "Mutter" auf Bisaya. "Mama" wird auch verwendet.'
|
|
||||||
},
|
},
|
||||||
{
|
Vater: {
|
||||||
exerciseTypeId: 2, // multiple_choice
|
de: 'Vater',
|
||||||
title: 'Wie sagt man "Vater" auf Bisaya?',
|
en: 'Father',
|
||||||
instruction: 'Wähle die richtige Übersetzung.',
|
es: 'Padre',
|
||||||
questionData: {
|
fr: 'Père',
|
||||||
type: 'multiple_choice',
|
it: 'Padre',
|
||||||
question: 'Wie sagt man "Vater" auf Bisaya?',
|
pt: 'Pai'
|
||||||
options: ['Tatay', 'Nanay', 'Kuya', 'Ate']
|
|
||||||
},
|
|
||||||
answerData: {
|
|
||||||
type: 'multiple_choice',
|
|
||||||
correctAnswer: 0
|
|
||||||
},
|
|
||||||
explanation: '"Tatay" bedeutet "Vater" auf Bisaya. "Papa" wird auch verwendet.'
|
|
||||||
},
|
},
|
||||||
{
|
'älterer Bruder': {
|
||||||
exerciseTypeId: 2, // multiple_choice
|
de: 'älterer Bruder',
|
||||||
title: 'Wie sagt man "älterer Bruder" auf Bisaya?',
|
en: 'older brother',
|
||||||
instruction: 'Wähle die richtige Übersetzung.',
|
es: 'hermano mayor',
|
||||||
questionData: {
|
fr: 'frère aîné',
|
||||||
type: 'multiple_choice',
|
it: 'fratello maggiore',
|
||||||
question: 'Wie sagt man "älterer Bruder" auf Bisaya?',
|
pt: 'irmão mais velho'
|
||||||
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.'
|
|
||||||
},
|
},
|
||||||
{
|
'ältere Schwester': {
|
||||||
exerciseTypeId: 2, // multiple_choice
|
de: 'ältere Schwester',
|
||||||
title: 'Wie sagt man "ältere Schwester" auf Bisaya?',
|
en: 'older sister',
|
||||||
instruction: 'Wähle die richtige Übersetzung.',
|
es: 'hermana mayor',
|
||||||
questionData: {
|
fr: 'sœur aînée',
|
||||||
type: 'multiple_choice',
|
it: 'sorella maggiore',
|
||||||
question: 'Wie sagt man "ältere Schwester" auf Bisaya?',
|
pt: 'irmã mais velha'
|
||||||
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.'
|
|
||||||
},
|
},
|
||||||
{
|
Großmutter: {
|
||||||
exerciseTypeId: 2, // multiple_choice
|
de: 'Großmutter',
|
||||||
title: 'Wie sagt man "Großmutter" auf Bisaya?',
|
en: 'Grandmother',
|
||||||
instruction: 'Wähle die richtige Übersetzung.',
|
es: 'Abuela',
|
||||||
questionData: {
|
fr: 'Grand-mère',
|
||||||
type: 'multiple_choice',
|
it: 'Nonna',
|
||||||
question: 'Wie sagt man "Großmutter" auf Bisaya?',
|
pt: 'Avó'
|
||||||
options: ['Lola', 'Lolo', 'Nanay', 'Tatay']
|
|
||||||
},
|
|
||||||
answerData: {
|
|
||||||
type: 'multiple_choice',
|
|
||||||
correctAnswer: 0
|
|
||||||
},
|
|
||||||
explanation: '"Lola" bedeutet "Großmutter" auf Bisaya.'
|
|
||||||
},
|
},
|
||||||
{
|
Großvater: {
|
||||||
exerciseTypeId: 2, // multiple_choice
|
de: 'Großvater',
|
||||||
title: 'Wie sagt man "Großvater" auf Bisaya?',
|
en: 'Grandfather',
|
||||||
instruction: 'Wähle die richtige Übersetzung.',
|
es: 'Abuelo',
|
||||||
questionData: {
|
fr: 'Grand-père',
|
||||||
type: 'multiple_choice',
|
it: 'Nonno',
|
||||||
question: 'Wie sagt man "Großvater" auf Bisaya?',
|
pt: 'Avô'
|
||||||
options: ['Lolo', 'Lola', 'Nanay', 'Tatay']
|
}
|
||||||
},
|
};
|
||||||
answerData: {
|
|
||||||
type: 'multiple_choice',
|
// Bisaya-Übersetzungen
|
||||||
correctAnswer: 0
|
const BISAYA_TRANSLATIONS = {
|
||||||
},
|
'Mutter': 'Nanay',
|
||||||
explanation: '"Lolo" bedeutet "Großvater" auf Bisaya.'
|
'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
|
exerciseTypeId: 1, // gap_fill
|
||||||
title: 'Familienwörter vervollständigen',
|
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: {
|
questionData: {
|
||||||
type: 'gap_fill',
|
type: 'gap_fill',
|
||||||
text: '{gap} (Mutter) | {gap} (Vater) | {gap} (älterer Bruder) | {gap} (ältere Schwester) | {gap} (Großmutter) | {gap} (Großvater)',
|
text: familyWords.map((key, i) => `{gap} (${nativeWords[i]})`).join(' | '),
|
||||||
gaps: 6
|
gaps: familyWords.length
|
||||||
},
|
},
|
||||||
answerData: {
|
answerData: {
|
||||||
type: 'gap_fill',
|
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
|
exerciseTypeId: 4, // transformation
|
||||||
title: 'Familienwörter übersetzen',
|
title: 'Familienwörter übersetzen',
|
||||||
instruction: 'Übersetze das Familienwort ins Bisaya.',
|
instruction: `Übersetze das Familienwort ins Bisaya.`,
|
||||||
questionData: {
|
questionData: {
|
||||||
type: 'transformation',
|
type: 'transformation',
|
||||||
text: 'Mutter',
|
text: nativeWords[0], // Erste Muttersprache als Beispiel
|
||||||
sourceLanguage: 'Deutsch',
|
sourceLanguage: nativeLanguageName || 'Deutsch',
|
||||||
targetLanguage: 'Bisaya'
|
targetLanguage: 'Bisaya'
|
||||||
},
|
},
|
||||||
answerData: {
|
answerData: {
|
||||||
type: 'transformation',
|
type: 'transformation',
|
||||||
correct: 'Nanay',
|
correct: bisayaWords[0],
|
||||||
alternatives: ['Mama', 'Nanay', 'Inahan']
|
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() {
|
async function findOrCreateSystemUser() {
|
||||||
let systemUser = await User.findOne({
|
let systemUser = await User.findOne({
|
||||||
@@ -183,7 +211,10 @@ async function updateFamilyWordsExercises() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const courses = await sequelize.query(
|
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 },
|
replacements: { languageId: bisayaLanguage.id },
|
||||||
type: sequelize.QueryTypes.SELECT
|
type: sequelize.QueryTypes.SELECT
|
||||||
@@ -196,7 +227,11 @@ async function updateFamilyWordsExercises() {
|
|||||||
let totalLessonsUpdated = 0;
|
let totalLessonsUpdated = 0;
|
||||||
|
|
||||||
for (const course of courses) {
|
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
|
// Finde "Familienwörter"-Lektionen
|
||||||
const lessons = await VocabCourseLesson.findAll({
|
const lessons = await VocabCourseLesson.findAll({
|
||||||
@@ -219,7 +254,7 @@ async function updateFamilyWordsExercises() {
|
|||||||
|
|
||||||
// Erstelle neue Übungen
|
// Erstelle neue Übungen
|
||||||
let exerciseNumber = 1;
|
let exerciseNumber = 1;
|
||||||
for (const exerciseData of FAMILY_WORDS_EXERCISES) {
|
for (const exerciseData of exercises) {
|
||||||
await VocabGrammarExercise.create({
|
await VocabGrammarExercise.create({
|
||||||
lessonId: lesson.id,
|
lessonId: lesson.id,
|
||||||
exerciseTypeId: exerciseData.exerciseTypeId,
|
exerciseTypeId: exerciseData.exerciseTypeId,
|
||||||
@@ -234,7 +269,7 @@ async function updateFamilyWordsExercises() {
|
|||||||
totalExercisesUpdated++;
|
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++;
|
totalLessonsUpdated++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
107
update-vocab-courses.sh
Executable file
107
update-vocab-courses.sh
Executable file
@@ -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"
|
||||||
Reference in New Issue
Block a user