From c13339f77160cdfb5405f5892128380b3ffb4694 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Tue, 21 Jul 2026 09:20:56 +0200 Subject: [PATCH] =?UTF-8?q?Erweitere=20die=20Funktionalit=C3=A4t=20zur=20V?= =?UTF-8?q?erarbeitung=20von=20Antwortvarianten;=20f=C3=BCge=20Unterst?= =?UTF-8?q?=C3=BCtzung=20f=C3=BCr=20Klammerinhalte=20und=20alternative=20P?= =?UTF-8?q?hrasen=20hinzu.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../socialnetwork/VocabPracticeDialog.vue | 18 ++++++++-- .../views/social/VocabLessonReviewView.vue | 33 +++++++++++++++++-- frontend/src/views/social/VocabLessonView.vue | 21 ++++++++++-- 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/frontend/src/dialogues/socialnetwork/VocabPracticeDialog.vue b/frontend/src/dialogues/socialnetwork/VocabPracticeDialog.vue index 382afda..fc2fa2e 100755 --- a/frontend/src/dialogues/socialnetwork/VocabPracticeDialog.vue +++ b/frontend/src/dialogues/socialnetwork/VocabPracticeDialog.vue @@ -1016,17 +1016,31 @@ export default { expandAnswerVariants(answer) { const base = String(answer || '').trim(); if (!base) return []; + const variants = new Set([base]); + + const withoutParentheses = base.replace(/\s*\([^)]*\)\s*/g, ' ').replace(/\s+/g, ' ').trim(); + if (withoutParentheses) { + variants.add(withoutParentheses); + } + + const parenMatches = base.match(/\(([^)]+)\)/g) || []; + parenMatches.forEach((chunk) => { + const content = chunk.replace(/[()]/g, '').trim(); + if (content) { + variants.add(content); + } + }); // Handle full-answer alternatives like "A / B" as separate valid answers. // Word-level slash expansion alone does not cover this reliably. - const phraseAlternatives = this.splitPhraseAlternatives(base); + const phraseAlternatives = Array.from(variants).flatMap((entry) => this.splitPhraseAlternatives(entry)); if (phraseAlternatives.length >= 2) { const expanded = phraseAlternatives.flatMap((part) => this.expandSingleAnswerVariants(part)); return [...new Set(expanded)]; } - return this.expandSingleAnswerVariants(base); + return this.expandSingleAnswerVariants(base).concat(withoutParentheses ? this.expandSingleAnswerVariants(withoutParentheses) : []); }, computeWeight(item) { const st = this.perId[item.id] || { c: 0, w: 0, streak: 0, lastAsked: 0 }; diff --git a/frontend/src/views/social/VocabLessonReviewView.vue b/frontend/src/views/social/VocabLessonReviewView.vue index 38c4978..2e25703 100755 --- a/frontend/src/views/social/VocabLessonReviewView.vue +++ b/frontend/src/views/social/VocabLessonReviewView.vue @@ -127,6 +127,32 @@ export default { }; return normalizeComparableWithNumberWords(s, baseNormalize); }, + splitPhraseAlternatives(answer) { + const base = String(answer || '').trim(); + if (!base) return []; + const parts = base.split(/\s*[\/|;]\s*/).map((part) => part.trim()).filter(Boolean); + return parts.length >= 2 && parts.length <= 6 ? parts : [base]; + }, + expandAnswerVariants(answer) { + const base = String(answer || '').trim(); + if (!base) return []; + + const variants = new Set([base]); + const withoutParentheses = base.replace(/\s*\([^)]*\)\s*/g, ' ').replace(/\s+/g, ' ').trim(); + if (withoutParentheses) { + variants.add(withoutParentheses); + } + + const parenMatches = base.match(/\(([^)]+)\)/g) || []; + parenMatches.forEach((chunk) => { + const content = chunk.replace(/[()]/g, '').trim(); + if (content) { + variants.add(content); + } + }); + + return [...new Set(Array.from(variants).flatMap((entry) => this.splitPhraseAlternatives(entry)))]; + }, getItemKey(item) { return `${String(item?.gloss || '').trim()}|${String(item?.target || '').trim()}`; }, @@ -190,9 +216,10 @@ export default { }, async checkCurrent() { if (!this.currentItem) return; - const isCorrect = this.mode === 'multiple_choice' - ? this.normalize(this.selectedOption) === this.normalize(this.currentItem.gloss) - : this.normalize(this.typedAnswer) === this.normalize(this.currentItem.target); + const expectedRaw = this.mode === 'multiple_choice' ? this.currentItem.gloss : this.currentItem.target; + const expectedAnswers = this.expandAnswerVariants(expectedRaw).map((entry) => this.normalize(entry)); + const userAnswer = this.mode === 'multiple_choice' ? this.selectedOption : this.typedAnswer; + const isCorrect = expectedAnswers.includes(this.normalize(userAnswer)); this.feedbackCorrect = isCorrect; if (isCorrect) { diff --git a/frontend/src/views/social/VocabLessonView.vue b/frontend/src/views/social/VocabLessonView.vue index c2c7d74..018f571 100755 --- a/frontend/src/views/social/VocabLessonView.vue +++ b/frontend/src/views/social/VocabLessonView.vue @@ -4353,14 +4353,31 @@ export default { expandAnswerVariants(answer) { const base = String(answer || '').trim(); if (!base) return []; + const variants = new Set([base]); - const phraseAlternatives = this.splitPhraseAlternatives(base); + const withoutParentheses = base.replace(/\s*\([^)]*\)\s*/g, ' ').replace(/\s+/g, ' ').trim(); + if (withoutParentheses) { + variants.add(withoutParentheses); + } + + const parenMatches = base.match(/\(([^)]+)\)/g) || []; + parenMatches.forEach((chunk) => { + const content = chunk.replace(/[()]/g, '').trim(); + if (content) { + variants.add(content); + } + }); + + const phraseAlternatives = Array.from(variants).flatMap((entry) => this.splitPhraseAlternatives(entry)); if (phraseAlternatives.length >= 2) { const expanded = phraseAlternatives.flatMap((part) => this.expandSingleAnswerVariants(part)); return [...new Set(expanded)]; } - return this.expandSingleAnswerVariants(base); + return [...new Set( + this.expandSingleAnswerVariants(base) + .concat(withoutParentheses ? this.expandSingleAnswerVariants(withoutParentheses) : []) + )]; }, reportSrsReviewForCurrentQuestion(isCorrect) { if (!this.currentVocabQuestion?.vocab || !this.courseId) {