feat(vocabService): enhance answer validation for multiple choice exercises

- Improved answer validation logic by expanding multiple choice answer data based on lesson context.
- Added methods to extract indices of correct answers and normalize question prompts for better accuracy.
- Refactored existing answer checking to accommodate new data handling, ensuring robust evaluation of user responses.
This commit is contained in:
Torsten Schulz (local)
2026-03-28 12:46:34 +01:00
parent 71e120bf20
commit 8a96951b50

View File

@@ -1702,8 +1702,18 @@ export default class VocabService {
throw err; throw err;
} }
// Überprüfe Antwort (vereinfachte Logik - kann je nach Übungstyp erweitert werden) const originalAnswerData = typeof exercise.answerData === 'string'
const isCorrect = this._checkAnswer(exercise.answerData, exercise.questionData, userAnswer, exercise.exerciseTypeId); ? JSON.parse(exercise.answerData)
: exercise.answerData;
const questionData = typeof exercise.questionData === 'string'
? JSON.parse(exercise.questionData)
: exercise.questionData;
const effectiveAnswerData = exercise.exerciseTypeId === 2
? await this._expandMultipleChoiceAnswerData(exercise, originalAnswerData, questionData)
: originalAnswerData;
// Überprüfe Antwort
const isCorrect = this._checkAnswer(effectiveAnswerData, questionData, userAnswer, exercise.exerciseTypeId);
// Speichere Fortschritt // Speichere Fortschritt
const [progress, created] = await VocabGrammarExerciseProgress.findOrCreate({ const [progress, created] = await VocabGrammarExerciseProgress.findOrCreate({
@@ -1736,12 +1746,7 @@ export default class VocabService {
} }
// Extrahiere richtige Antwort und Alternativen // Extrahiere richtige Antwort und Alternativen
const answerData = typeof exercise.answerData === 'string' const answerData = effectiveAnswerData;
? JSON.parse(exercise.answerData)
: exercise.answerData;
const questionData = typeof exercise.questionData === 'string'
? JSON.parse(exercise.questionData)
: exercise.questionData;
let correctAnswer = null; let correctAnswer = null;
let alternatives = []; let alternatives = [];
@@ -1817,6 +1822,86 @@ export default class VocabService {
return type ? type.id : null; return type ? type.id : null;
} }
_extractMultipleChoiceIndices(answerData) {
if (!answerData) return [];
if (answerData.correctAnswer !== undefined) {
return Array.isArray(answerData.correctAnswer)
? answerData.correctAnswer.map(idx => Number(idx)).filter(Number.isInteger)
: [Number(answerData.correctAnswer)].filter(Number.isInteger);
}
if (answerData.correct !== undefined) {
return Array.isArray(answerData.correct)
? answerData.correct.map(idx => Number(idx)).filter(Number.isInteger)
: [Number(answerData.correct)].filter(Number.isInteger);
}
return [];
}
_getMultipleChoicePrompt(questionData) {
return this._normalizeTextAnswer(
questionData?.question || questionData?.text || questionData?.prompt || ''
);
}
async _expandMultipleChoiceAnswerData(exercise, answerData, questionData) {
const options = Array.isArray(questionData?.options) ? questionData.options : [];
const baseIndices = this._extractMultipleChoiceIndices(answerData);
if (!options.length || !baseIndices.length || !exercise?.lessonId) {
return answerData;
}
const prompt = this._getMultipleChoicePrompt(questionData);
if (!prompt) {
return answerData;
}
const optionIndexMap = new Map();
options.forEach((option, index) => {
const normalizedOption = this._normalizeTextAnswer(option);
if (!normalizedOption) return;
const existing = optionIndexMap.get(normalizedOption) || [];
existing.push(index);
optionIndexMap.set(normalizedOption, existing);
});
const lessonExercises = await VocabGrammarExercise.findAll({
where: {
lessonId: exercise.lessonId,
exerciseTypeId: 2
},
attributes: ['id', 'questionData', 'answerData']
});
const expandedIndices = new Set(baseIndices);
lessonExercises.forEach((candidate) => {
const candidateQuestionData = typeof candidate.questionData === 'string'
? JSON.parse(candidate.questionData)
: candidate.questionData;
const candidatePrompt = this._getMultipleChoicePrompt(candidateQuestionData);
if (candidatePrompt !== prompt) {
return;
}
const candidateAnswerData = typeof candidate.answerData === 'string'
? JSON.parse(candidate.answerData)
: candidate.answerData;
const candidateOptions = Array.isArray(candidateQuestionData?.options) ? candidateQuestionData.options : [];
const candidateIndices = this._extractMultipleChoiceIndices(candidateAnswerData);
candidateIndices.forEach((candidateIndex) => {
const candidateOption = candidateOptions[candidateIndex];
const normalizedOption = this._normalizeTextAnswer(candidateOption);
const matchingIndices = optionIndexMap.get(normalizedOption) || [];
matchingIndices.forEach((matchingIndex) => expandedIndices.add(matchingIndex));
});
});
return {
...answerData,
correctAnswer: Array.from(expandedIndices).sort((a, b) => a - b)
};
}
_checkAnswer(answerData, questionData, userAnswer, exerciseTypeId) { _checkAnswer(answerData, questionData, userAnswer, exerciseTypeId) {
// Vereinfachte Antwortprüfung - kann je nach Übungstyp erweitert werden // Vereinfachte Antwortprüfung - kann je nach Übungstyp erweitert werden
if (!answerData || userAnswer === undefined || userAnswer === null) return false; if (!answerData || userAnswer === undefined || userAnswer === null) return false;