Enhance transformation exercise formatting and improve choice option generation in VocabLessonView
- Added formatting for transformation exercises to display prompts as "Übersetze 'X' ins Bisaya", improving clarity for users. - Updated the buildChoiceOptions method to exclude the prompt from the choice options, ensuring a more relevant selection for multiple choice questions. - Enhanced console logging to provide better insights into the prompt and answer during choice option creation, aiding in debugging and user feedback.
This commit is contained in:
88
backend/scripts/delete-all-family-words-exercises.js
Executable file
88
backend/scripts/delete-all-family-words-exercises.js
Executable file
@@ -0,0 +1,88 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Script zum Löschen ALLER "Familienwörter"-Übungen aus Bisaya-Kursen
|
||||||
|
*
|
||||||
|
* Verwendung:
|
||||||
|
* node backend/scripts/delete-all-family-words-exercises.js
|
||||||
|
*
|
||||||
|
* Löscht alle Grammatik-Übungen von "Familienwörter"-Lektionen, um Platz für neue zu schaffen.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { sequelize } from '../utils/sequelize.js';
|
||||||
|
import VocabCourseLesson from '../models/community/vocab_course_lesson.js';
|
||||||
|
import VocabGrammarExercise from '../models/community/vocab_grammar_exercise.js';
|
||||||
|
|
||||||
|
async function deleteAllFamilyWordsExercises() {
|
||||||
|
await sequelize.authenticate();
|
||||||
|
console.log('Datenbankverbindung erfolgreich hergestellt.\n');
|
||||||
|
|
||||||
|
// Finde alle Bisaya-Kurse
|
||||||
|
const [bisayaLanguage] = await sequelize.query(
|
||||||
|
`SELECT id FROM community.vocab_language WHERE name = 'Bisaya' LIMIT 1`,
|
||||||
|
{
|
||||||
|
type: sequelize.QueryTypes.SELECT
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!bisayaLanguage) {
|
||||||
|
console.error('❌ Bisaya-Sprache nicht gefunden.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const courses = await sequelize.query(
|
||||||
|
`SELECT id, title FROM community.vocab_course WHERE language_id = :languageId`,
|
||||||
|
{
|
||||||
|
replacements: { languageId: bisayaLanguage.id },
|
||||||
|
type: sequelize.QueryTypes.SELECT
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`Gefunden: ${courses.length} Bisaya-Kurse\n`);
|
||||||
|
|
||||||
|
let totalDeleted = 0;
|
||||||
|
let totalLessons = 0;
|
||||||
|
|
||||||
|
for (const course of courses) {
|
||||||
|
console.log(`📚 Kurs: ${course.title} (ID: ${course.id})`);
|
||||||
|
|
||||||
|
// Finde "Familienwörter"-Lektionen
|
||||||
|
const lessons = await VocabCourseLesson.findAll({
|
||||||
|
where: {
|
||||||
|
courseId: course.id,
|
||||||
|
title: 'Familienwörter'
|
||||||
|
},
|
||||||
|
order: [['lessonNumber', 'ASC']]
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(` ${lessons.length} "Familienwörter"-Lektionen gefunden`);
|
||||||
|
|
||||||
|
for (const lesson of lessons) {
|
||||||
|
// Lösche ALLE bestehenden Übungen
|
||||||
|
const deletedCount = await VocabGrammarExercise.destroy({
|
||||||
|
where: { lessonId: lesson.id }
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(` 🗑️ Lektion ${lesson.lessonNumber}: "${lesson.title}" - ${deletedCount} Übung(en) gelöscht`);
|
||||||
|
totalDeleted += deletedCount;
|
||||||
|
totalLessons++;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\n🎉 Zusammenfassung:`);
|
||||||
|
console.log(` ${totalLessons} Lektionen bearbeitet`);
|
||||||
|
console.log(` ${totalDeleted} Übungen gelöscht`);
|
||||||
|
console.log(`\n💡 Hinweis: Führe jetzt das update-family-words-exercises.js Script aus, um neue Übungen zu erstellen.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteAllFamilyWordsExercises()
|
||||||
|
.then(() => {
|
||||||
|
sequelize.close();
|
||||||
|
process.exit(0);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('❌ Fehler:', error);
|
||||||
|
sequelize.close();
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -541,6 +541,14 @@ export default {
|
|||||||
if (!qData) return exercise.title;
|
if (!qData) return exercise.title;
|
||||||
|
|
||||||
if (qData.question) return qData.question;
|
if (qData.question) return qData.question;
|
||||||
|
|
||||||
|
// Für Transformation-Übungen: Formatiere als "Übersetze 'X' ins Bisaya"
|
||||||
|
if (qData.type === 'transformation' && qData.text) {
|
||||||
|
const sourceLang = qData.sourceLanguage || 'Deutsch';
|
||||||
|
const targetLang = qData.targetLanguage || 'Bisaya';
|
||||||
|
return `Übersetze "${qData.text}" ins ${targetLang}`;
|
||||||
|
}
|
||||||
|
|
||||||
if (qData.text) return qData.text;
|
if (qData.text) return qData.text;
|
||||||
return exercise.title;
|
return exercise.title;
|
||||||
},
|
},
|
||||||
@@ -769,28 +777,42 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
buildChoiceOptions(correctAnswer, allVocabs) {
|
buildChoiceOptions(correctAnswer, allVocabs, excludePrompt = null) {
|
||||||
const options = new Set([correctAnswer]);
|
const options = new Set([correctAnswer]);
|
||||||
|
// Normalisiere alle Werte für den Vergleich (trim + lowercase)
|
||||||
|
const normalizedExcludeSet = new Set();
|
||||||
|
normalizedExcludeSet.add(this.normalizeVocab(correctAnswer));
|
||||||
|
// Wichtig: Der Prompt (die Frage) darf nicht in den Optionen erscheinen
|
||||||
|
if (excludePrompt) {
|
||||||
|
normalizedExcludeSet.add(this.normalizeVocab(excludePrompt));
|
||||||
|
}
|
||||||
|
|
||||||
// Füge 3 Distraktoren hinzu
|
// Füge 3 Distraktoren hinzu
|
||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
const maxAttempts = 50; // Verhindere Endlosschleife
|
const maxAttempts = 100; // Erhöht, da wir mehr Filter haben
|
||||||
|
|
||||||
while (options.size < 4 && allVocabs.length > 1 && attempts < maxAttempts) {
|
while (options.size < 4 && allVocabs.length > 1 && attempts < maxAttempts) {
|
||||||
attempts++;
|
attempts++;
|
||||||
const randomVocab = allVocabs[Math.floor(Math.random() * allVocabs.length)];
|
const randomVocab = allVocabs[Math.floor(Math.random() * allVocabs.length)];
|
||||||
const distractor = this.vocabTrainerDirection === 'L2R' ? randomVocab.reference : randomVocab.learning;
|
const distractor = this.vocabTrainerDirection === 'L2R' ? randomVocab.reference : randomVocab.learning;
|
||||||
if (distractor !== correctAnswer) {
|
|
||||||
|
// Prüfe: Distraktor darf nicht gleich correctAnswer oder excludePrompt sein (mit Normalisierung)
|
||||||
|
if (distractor && distractor.trim()) {
|
||||||
|
const normalizedDistractor = this.normalizeVocab(distractor);
|
||||||
|
if (!normalizedExcludeSet.has(normalizedDistractor) && !options.has(distractor)) {
|
||||||
options.add(distractor);
|
options.add(distractor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Falls nicht genug Optionen gefunden wurden, füge generische Optionen hinzu
|
// Falls nicht genug Optionen gefunden wurden, füge generische Optionen hinzu
|
||||||
if (options.size < 4) {
|
if (options.size < 4) {
|
||||||
const genericOptions = ['Option A', 'Option B', 'Option C', 'Option D'];
|
const genericOptions = ['Option A', 'Option B', 'Option C', 'Option D'];
|
||||||
let index = 0;
|
let index = 0;
|
||||||
while (options.size < 4 && index < genericOptions.length) {
|
while (options.size < 4 && index < genericOptions.length) {
|
||||||
if (!options.has(genericOptions[index])) {
|
const option = genericOptions[index];
|
||||||
options.add(genericOptions[index]);
|
if (!excludeSet.has(option)) {
|
||||||
|
options.add(option);
|
||||||
}
|
}
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
@@ -836,8 +858,15 @@ export default {
|
|||||||
// Erstelle Choice-Optionen für Multiple Choice
|
// Erstelle Choice-Optionen für Multiple Choice
|
||||||
if (this.vocabTrainerMode === 'multiple_choice') {
|
if (this.vocabTrainerMode === 'multiple_choice') {
|
||||||
console.log('[VocabLessonView] Erstelle Choice-Optionen...');
|
console.log('[VocabLessonView] Erstelle Choice-Optionen...');
|
||||||
this.vocabTrainerChoiceOptions = this.buildChoiceOptions(this.currentVocabQuestion.answer, this.vocabTrainerPool);
|
console.log('[VocabLessonView] Prompt:', this.currentVocabQuestion.prompt);
|
||||||
console.log('[VocabLessonView] Choice-Optionen erstellt:', this.vocabTrainerChoiceOptions.length);
|
console.log('[VocabLessonView] Answer:', this.currentVocabQuestion.answer);
|
||||||
|
// Wichtig: Der Prompt (die Frage) darf nicht in den Optionen erscheinen
|
||||||
|
this.vocabTrainerChoiceOptions = this.buildChoiceOptions(
|
||||||
|
this.currentVocabQuestion.answer,
|
||||||
|
this.vocabTrainerPool,
|
||||||
|
this.currentVocabQuestion.prompt // Exkludiere den Prompt
|
||||||
|
);
|
||||||
|
console.log('[VocabLessonView] Choice-Optionen erstellt:', this.vocabTrainerChoiceOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fokussiere Eingabefeld im Typing-Modus
|
// Fokussiere Eingabefeld im Typing-Modus
|
||||||
|
|||||||
Reference in New Issue
Block a user