Enhance exercise generation for family conversations and feelings & affection

- Updated multiple choice exercises to include randomized wrong options for improved engagement and challenge.
- Added new exercise types for reading aloud and speaking from memory, enhancing interactive learning experiences.
- Improved gap fill exercises with clearer instructions and multiple variants for better user understanding.
- Enhanced the vocabulary service to support new exercise types, ensuring robust answer checking and feedback mechanisms.
- Updated localization files to include new instructions and messages related to the new exercise types.
This commit is contained in:
Torsten Schulz (local)
2026-01-20 14:30:19 +01:00
parent e5d4a5f95f
commit 8d32d704b5
8 changed files with 612 additions and 105 deletions

View File

@@ -1421,6 +1421,15 @@ export default class VocabService {
? answerData.answers.join(', ')
: answerData.answers;
}
// Für Reading Aloud: Extrahiere den erwarteten Text
else if (questionData.type === 'reading_aloud') {
correctAnswer = questionData.text || answerData.expectedText || '';
}
// Für Speaking From Memory: Extrahiere erwarteten Text oder Schlüsselwörter
else if (questionData.type === 'speaking_from_memory') {
correctAnswer = questionData.expectedText || questionData.text || '';
alternatives = questionData.keywords || [];
}
// Fallback: Versuche correct oder correctAnswer
else {
correctAnswer = Array.isArray(answerData.correct)
@@ -1438,6 +1447,11 @@ export default class VocabService {
};
}
async _getExerciseTypeIdByName(typeName) {
const type = await VocabGrammarExerciseType.findOne({ where: { name: typeName } });
return type ? type.id : null;
}
_checkAnswer(answerData, questionData, userAnswer, exerciseTypeId) {
// Vereinfachte Antwortprüfung - kann je nach Übungstyp erweitert werden
if (!answerData || userAnswer === undefined || userAnswer === null) return false;
@@ -1476,6 +1490,32 @@ export default class VocabService {
}
}
// Für Reading Aloud: userAnswer ist der erkannte Text (String)
// Vergleiche mit dem erwarteten Text aus questionData.text
if (parsedQuestionData.type === 'reading_aloud' || parsedQuestionData.type === 'speaking_from_memory') {
const normalize = (str) => String(str || '').trim().toLowerCase().replace(/[.,!?;:]/g, '');
const expectedText = parsedQuestionData.text || parsedQuestionData.expectedText || '';
const normalizedExpected = normalize(expectedText);
const normalizedUser = normalize(userAnswer);
// Für reading_aloud: Exakter Vergleich oder Levenshtein-Distanz
if (parsedQuestionData.type === 'reading_aloud') {
// Exakter Vergleich (kann später mit Levenshtein erweitert werden)
return normalizedUser === normalizedExpected;
}
// Für speaking_from_memory: Flexibler Vergleich (Schlüsselwörter)
if (parsedQuestionData.type === 'speaking_from_memory') {
const keywords = parsedQuestionData.keywords || [];
if (keywords.length === 0) {
// Fallback: Exakter Vergleich
return normalizedUser === normalizedExpected;
}
// Prüfe ob alle Schlüsselwörter vorhanden sind
return keywords.every(keyword => normalizedUser.includes(normalize(keyword)));
}
}
// Für andere Typen: einfacher String-Vergleich (kann später erweitert werden)
const normalize = (str) => String(str || '').trim().toLowerCase();
const correctAnswers = parsedAnswerData.correct || parsedAnswerData.correctAnswer || [];