Resolved answer mapping & formatting: Enhanced quiz-logic to handle text-to-key answer resolution and updated app.js indentation/logic.

This commit is contained in:
2026-04-16 20:00:24 +02:00
parent 34153c3c15
commit 604d23bcdf
2 changed files with 78 additions and 11 deletions
+38 -6
View File
@@ -507,17 +507,49 @@ function SoloQuizPage({ params, navigate }) {
const checkAnswer = () => { const checkAnswer = () => {
const rawCorrect = (q['Richtige Antwort'] || '').trim(); const rawCorrect = (q['Richtige Antwort'] || '').trim();
let correct, ok; let correctKeysResolved, ok;
if (typ === 'freitext') { if (typ === 'freitext') {
correct = []; setCorrectText(rawCorrect); correctKeysResolved = [];
setCorrectText(rawCorrect);
ok = normalizeText(selected[0]) === normalizeText(rawCorrect); ok = normalizeText(selected[0]) === normalizeText(rawCorrect);
} else { } else {
correct = rawCorrect.split(',').map((s) => s.trim().toUpperCase()).filter(Boolean); // Resolve correct answer: could be keys ("A","B") or text ("Wahr","Falsch")
const correctSet = new Set(correct); const parts = rawCorrect.split(',').map((s) => s.trim()).filter(Boolean);
const validKeys = new Set(['A', 'B', 'C', 'D']);
const allAreKeys = parts.length > 0 && parts.every((p) => validKeys.has(p.toUpperCase()));
if (allAreKeys) {
correctKeysResolved = parts.map((p) => p.toUpperCase());
} else {
// Match text against answer options
correctKeysResolved = [];
for (const part of parts) {
const partLower = part.toLowerCase().trim();
// Search in all answers (including shuffled ones which keep their original keys)
const allAnswers = [];
['A', 'B', 'C', 'D'].forEach((k) => {
if (q[`Antwort ${k}`]) allAnswers.push({ key: k, text: q[`Antwort ${k}`] });
});
for (const ans of allAnswers) {
if (ans.text.toLowerCase().trim() === partLower) {
correctKeysResolved.push(ans.key);
break;
}
}
}
// Fallback
if (correctKeysResolved.length === 0) {
correctKeysResolved = parts.map((p) => p.toUpperCase());
}
}
const correctSet = new Set(correctKeysResolved);
const selectedSet = new Set(selected.map((s) => s.toUpperCase())); const selectedSet = new Set(selected.map((s) => s.toUpperCase()));
ok = correct.length === selectedSet.size && correct.every((c) => selectedSet.has(c)); ok = correctKeysResolved.length === selectedSet.size && correctKeysResolved.every((c) => selectedSet.has(c));
} }
setCorrectKeys(correct);
setCorrectKeys(correctKeysResolved);
setIsCorrect(ok); setIsCorrect(ok);
setShowFeedback(true); setShowFeedback(true);
+40 -5
View File
@@ -25,14 +25,50 @@ function checkFreitext(userAnswer, correctAnswer) {
return normalizeText(userAnswer) === normalizeText(correctAnswer); return normalizeText(userAnswer) === normalizeText(correctAnswer);
} }
/**
* Resolve correct answer(s) to keys (A/B/C/D).
* Handles both:
* - "A" or "A,C" (key-based)
* - "Falsch" or "Stuttgart" (text-based resolved to matching key)
*/
function parseCorrectAnswers(question) { function parseCorrectAnswers(question) {
const raw = (question['Richtige Antwort'] || '').trim(); const raw = (question['Richtige Antwort'] || '').trim();
const typ = getTyp(question); const typ = getTyp(question);
if (typ === 'freitext') return [raw]; if (typ === 'freitext') return [raw];
return raw
.split(',') const parts = raw.split(',').map((s) => s.trim()).filter(Boolean);
.map((s) => s.trim().toUpperCase()) const validKeys = new Set(['A', 'B', 'C', 'D']);
.filter(Boolean);
// Check if all parts are valid keys already
const allAreKeys = parts.length > 0 && parts.every((p) => validKeys.has(p.toUpperCase()));
if (allAreKeys) {
return parts.map((p) => p.toUpperCase());
}
// Otherwise: try to match each part against the answer texts
const resolved = [];
for (const part of parts) {
const partLower = part.toLowerCase().trim();
const keys = ['A', 'B', 'C', 'D'];
let found = false;
for (const k of keys) {
const answerText = (question[`Antwort ${k}`] || '').trim();
if (answerText && answerText.toLowerCase() === partLower) {
resolved.push(k);
found = true;
break;
}
}
// If it looks like a key anyway (single char), use it
if (!found && part.length === 1 && validKeys.has(part.toUpperCase())) {
resolved.push(part.toUpperCase());
}
}
if (resolved.length > 0) return resolved;
// Fallback: return as-is uppercase
return parts.map((p) => p.toUpperCase());
} }
function countOptions(question) { function countOptions(question) {
@@ -104,7 +140,6 @@ function sanitizeQuestion(question) {
kategorie: extractFieldValue(question['Kategorie'], ''), kategorie: extractFieldValue(question['Kategorie'], ''),
}; };
// Rewrite image URL to use proxy
const bild = question['Bild']; const bild = question['Bild'];
if (bild && Array.isArray(bild) && bild.length > 0 && bild[0].url) { if (bild && Array.isArray(bild) && bild.length > 0 && bild[0].url) {
sanitized.bild = `/api/img?url=${encodeURIComponent(bild[0].url)}`; sanitized.bild = `/api/img?url=${encodeURIComponent(bild[0].url)}`;