feat(exercises): enhance gap exercise creation with contextual prompts and add migration for lesson 43
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const LESSON_TITLE = 'Kultur: Höflichkeit, Familie, Alltag';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
async up(queryInterface) {
|
||||||
|
await queryInterface.sequelize.transaction(async (transaction) => {
|
||||||
|
// The old task asked for a soft refusal to an invitation, but expected
|
||||||
|
// the unrelated cultural keyword "respeto". Test the matching phrase.
|
||||||
|
await queryInterface.sequelize.query(`
|
||||||
|
UPDATE community.vocab_grammar_exercise AS exercise
|
||||||
|
SET question_data = '{"type":"gap_fill","text":"{gap}","gaps":1}'::jsonb,
|
||||||
|
answer_data = '{"type":"gap_fill","answers":["Dili lang sa karon."]}'::jsonb,
|
||||||
|
explanation = 'Das Kernmuster lautet: "Dili lang sa karon."'
|
||||||
|
FROM community.vocab_course_lesson AS lesson
|
||||||
|
WHERE exercise.lesson_id = lesson.id
|
||||||
|
AND (lesson.id = 40 OR (lesson.course_id = 1 AND lesson.lesson_number = 43))
|
||||||
|
AND exercise.title = :lessonTitle || ': Kernmuster ergänzen'
|
||||||
|
AND exercise.question_data->>'type' = 'gap_fill'
|
||||||
|
AND exercise.answer_data->'answers' @> '["respeto"]'::jsonb;
|
||||||
|
`, { replacements: { lessonTitle: LESSON_TITLE }, transaction });
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async down() {
|
||||||
|
// Do not restore the semantically mismatched task.
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -598,7 +598,24 @@ function buildGapExercise(lessonTitle, pattern) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildContextGapExercise(lesson, didactics, pattern) {
|
function buildContextGapExercise(lesson, didactics, pattern) {
|
||||||
const gapExercise = buildGapExercise(lesson.title, pattern);
|
const speakingPrompt = Array.isArray(didactics.speakingPrompts) ? didactics.speakingPrompts[0] : null;
|
||||||
|
const cue = normalizeText(speakingPrompt?.cue || '').toLowerCase();
|
||||||
|
// A contextual prompt must test a pattern that actually occurs in its
|
||||||
|
// scenario. Otherwise a cultural keyword such as "respeto" can be paired
|
||||||
|
// with an invitation-declining situation.
|
||||||
|
const scenarioPattern = didactics.corePatterns
|
||||||
|
.map((entry) => corePatternTarget(entry))
|
||||||
|
.find((candidate) => candidate && cue.includes(normalizeText(candidate).toLowerCase()));
|
||||||
|
const gapExercise = scenarioPattern
|
||||||
|
? {
|
||||||
|
exerciseTypeId: 1,
|
||||||
|
title: `${lesson.title}: Kernmuster ergänzen`,
|
||||||
|
instruction: 'Fülle die Lücke mit dem passenden Bisaya-Ausdruck.',
|
||||||
|
questionData: { type: 'gap_fill', text: '{gap}', gaps: 1 },
|
||||||
|
answerData: { type: 'gap_fill', answers: [scenarioPattern] },
|
||||||
|
explanation: `Das Kernmuster lautet: "${scenarioPattern}".`
|
||||||
|
}
|
||||||
|
: buildGapExercise(lesson.title, pattern);
|
||||||
if (!gapExercise) return null;
|
if (!gapExercise) return null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user