From 09a10ff83042cb3437588a5669f8b7787210ff5a Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Fri, 17 Apr 2026 09:30:36 +0200 Subject: [PATCH] feat(bisaya-course, vocabService): expand numerical didactics and enhance lesson progress serialization - Added new glosses for the numbers sixty, seventy, and eighty in the Bisaya didactics, enriching the numerical curriculum. - Updated the _serializeLessonProgress method to include an options parameter for suppressing lesson review due notifications based on SRS item status. - Introduced a new method to check for due SRS items, improving the handling of lesson progress and review scheduling. --- backend/scripts/update-bisaya-didactics.js | 3 +++ backend/services/vocabService.js | 28 ++++++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/backend/scripts/update-bisaya-didactics.js b/backend/scripts/update-bisaya-didactics.js index 4f0b135..ec407b5 100644 --- a/backend/scripts/update-bisaya-didactics.js +++ b/backend/scripts/update-bisaya-didactics.js @@ -356,6 +356,9 @@ export const LESSON_DIDACTICS = { { target: 'Traysenta', gloss: 'dreißig' }, { target: 'Kwarenta', gloss: 'vierzig' }, { target: 'Singkwenta', gloss: 'fünfzig' }, + { target: 'Sesenta', gloss: 'sechzig' }, + { target: 'Setenta', gloss: 'siebzig' }, + { target: 'Otsenta', gloss: 'achtzig' }, { target: 'Nobenta', gloss: 'neunzig' } ], grammarFocus: [ diff --git a/backend/services/vocabService.js b/backend/services/vocabService.js index 1d3bd5d..d8eb19f 100644 --- a/backend/services/vocabService.js +++ b/backend/services/vocabService.js @@ -428,7 +428,7 @@ export default class VocabService { return nextState; } - _serializeLessonProgress(progress, lessonData = null) { + _serializeLessonProgress(progress, lessonData = null, options = {}) { if (!progress) { return null; } @@ -439,7 +439,8 @@ export default class VocabService { const lessonState = this._sanitizeLessonState(plainProgress.lessonState); const reviewStage = this._clampInteger(lessonState.reviewStage, { min: 0, max: 3 }); const reviewNextDueAt = this._normalizeIsoDate(lessonState.reviewNextDueAt); - const reviewDue = Boolean(reviewNextDueAt && reviewStage < 3 && new Date(reviewNextDueAt).getTime() <= Date.now()); + const suppressLessonReviewDue = Boolean(options.suppressLessonReviewDue); + const reviewDue = !suppressLessonReviewDue && Boolean(reviewNextDueAt && reviewStage < 3 && new Date(reviewNextDueAt).getTime() <= Date.now()); return { ...plainProgress, @@ -452,10 +453,24 @@ export default class VocabService { reviewStage, reviewNextDueAt, reviewDue, - reviewCompleted: reviewStage >= 3 + reviewCompleted: reviewStage >= 3, + reviewSuppressedBySrs: suppressLessonReviewDue }; } + async _courseHasDueSrsItems(userId, courseId) { + const due = await VocabSrsItem.count({ + where: { + userId, + courseId: Number(courseId), + nextDueAt: { + [Op.lte]: new Date() + } + } + }); + return due > 0; + } + async _getUserByHashedId(hashedUserId) { const user = await User.findOne({ where: { hashedId: hashedUserId } }); if (!user) { @@ -2736,9 +2751,11 @@ export default class VocabService { plainLesson.grammarExercises || [] ); + const suppressLessonReviewDue = await this._courseHasDueSrsItems(user.id, plainLesson.courseId); + plainLesson.didactics = this._buildLessonDidactics(plainLesson); plainLesson.pedagogy = this._buildLessonPedagogy(plainLesson); - plainLesson.progress = this._serializeLessonProgress(progress, plainLesson); + plainLesson.progress = this._serializeLessonProgress(progress, plainLesson, { suppressLessonReviewDue }); return plainLesson; } @@ -3265,7 +3282,8 @@ export default class VocabService { order: [[{ model: VocabCourseLesson, as: 'lesson' }, 'lessonNumber', 'ASC']] }); - return progress.map((entry) => this._serializeLessonProgress(entry, entry.lesson)); + const suppressLessonReviewDue = await this._courseHasDueSrsItems(user.id, courseId); + return progress.map((entry) => this._serializeLessonProgress(entry, entry.lesson, { suppressLessonReviewDue })); } async updateLessonProgress(hashedUserId, lessonId, { completed, score, timeSpentMinutes, lessonState }) {