feat(bisaya-course): add word count validation and exercise audit warnings
Some checks failed
Deploy to production / deploy (push) Has been cancelled

- Introduced a `countWords` function to calculate the number of words in a given text.
- Added `collectExerciseAuditWarnings` function to generate warnings for gap-fill exercises with potentially misleading hints.
- Updated the `createBisayaCourseContent` function to log audit warnings when the VOCAB_STRICT_AUDIT environment variable is set, enhancing the validation process for exercise content.
This commit is contained in:
Torsten Schulz (local)
2026-04-14 14:57:54 +02:00
parent 7e5a46a9bf
commit 78da376c5b

View File

@@ -36,6 +36,44 @@ function normalizeText(value) {
.replace(/\s+/g, ' ');
}
function countWords(value) {
return normalizeText(value).split(' ').filter(Boolean).length;
}
function collectExerciseAuditWarnings(lessonTitle, exerciseData, exerciseNumber) {
const warnings = [];
const questionData = exerciseData?.questionData || {};
const answerData = exerciseData?.answerData || {};
if (questionData.type !== 'gap_fill') return warnings;
const text = String(questionData.text || '');
const answers = Array.isArray(answerData.answers) ? answerData.answers : [];
if (!text || !answers.length) return warnings;
const hints = [];
const hintRegex = /\(([^)]+)\)/g;
let match = hintRegex.exec(text);
while (match) {
hints.push(normalizeText(match[1]));
match = hintRegex.exec(text);
}
for (let i = 0; i < Math.min(answers.length, hints.length); i += 1) {
const answer = normalizeText(answers[i]);
const hint = hints[i];
if (!answer || !hint) continue;
const answerWords = countWords(answer);
const hintWords = countWords(hint);
if (answerWords <= 2 && hintWords >= 4) {
warnings.push(
`[${lessonTitle} #${exerciseNumber}] Gap-Fill-Hinweis wirkt zu lang für eine Kurzantwort: "${answer}" <-> "${hint}"`
);
}
}
return warnings;
}
function normalizeCorePatternEntry(entry) {
if (entry === null || entry === undefined || entry === '') {
return null;
@@ -2206,7 +2244,7 @@ const BISAYA_EXERCISES = {
instruction: 'Fülle die Lücken mit den richtigen Bisaya-Wörtern.',
questionData: {
type: 'gap_fill',
text: '{gap} ka padulong? (Wohin gehst du?) | {gap} imong plano? (Was ist dein Plan?)',
text: '{gap} ka padulong? (wo/wohin) | {gap} imong plano? (was)',
gaps: 2
},
answerData: {
@@ -2221,7 +2259,7 @@ const BISAYA_EXERCISES = {
instruction: 'Fülle die Lücken mit den richtigen Bisaya-Wörtern.',
questionData: {
type: 'gap_fill',
text: 'Moadto ko sa {gap}. (Ich gehe nach Hause.) | Moadto ko sa {gap}. (Ich gehe zum Markt.)',
text: 'Moadto ko sa {gap}. (Haus) | Moadto ko sa {gap}. (Markt)',
gaps: 2
},
answerData: {
@@ -4337,6 +4375,10 @@ async function createBisayaCourseContent() {
// Erstelle Übungen
let exerciseNumber = 1;
for (const exerciseData of exercises) {
if (process.env.VOCAB_STRICT_AUDIT === '1') {
const warnings = collectExerciseAuditWarnings(lesson.title, exerciseData, exerciseNumber);
warnings.forEach((warning) => console.warn(` ⚠️ ${warning}`));
}
const exerciseTypeId = await resolveExerciseTypeId(exerciseData);
await VocabGrammarExercise.create({
lessonId: lesson.id,