feat(bisaya-course, vocabService): expand numerical didactics and enhance lesson progress serialization
All checks were successful
Deploy to production / deploy (push) Successful in 2m54s

- 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.
This commit is contained in:
Torsten Schulz (local)
2026-04-17 09:30:36 +02:00
parent 8be215761d
commit 09a10ff830
2 changed files with 26 additions and 5 deletions

View File

@@ -356,6 +356,9 @@ export const LESSON_DIDACTICS = {
{ target: 'Traysenta', gloss: 'dreißig' }, { target: 'Traysenta', gloss: 'dreißig' },
{ target: 'Kwarenta', gloss: 'vierzig' }, { target: 'Kwarenta', gloss: 'vierzig' },
{ target: 'Singkwenta', gloss: 'fünfzig' }, { target: 'Singkwenta', gloss: 'fünfzig' },
{ target: 'Sesenta', gloss: 'sechzig' },
{ target: 'Setenta', gloss: 'siebzig' },
{ target: 'Otsenta', gloss: 'achtzig' },
{ target: 'Nobenta', gloss: 'neunzig' } { target: 'Nobenta', gloss: 'neunzig' }
], ],
grammarFocus: [ grammarFocus: [

View File

@@ -428,7 +428,7 @@ export default class VocabService {
return nextState; return nextState;
} }
_serializeLessonProgress(progress, lessonData = null) { _serializeLessonProgress(progress, lessonData = null, options = {}) {
if (!progress) { if (!progress) {
return null; return null;
} }
@@ -439,7 +439,8 @@ export default class VocabService {
const lessonState = this._sanitizeLessonState(plainProgress.lessonState); const lessonState = this._sanitizeLessonState(plainProgress.lessonState);
const reviewStage = this._clampInteger(lessonState.reviewStage, { min: 0, max: 3 }); const reviewStage = this._clampInteger(lessonState.reviewStage, { min: 0, max: 3 });
const reviewNextDueAt = this._normalizeIsoDate(lessonState.reviewNextDueAt); 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 { return {
...plainProgress, ...plainProgress,
@@ -452,10 +453,24 @@ export default class VocabService {
reviewStage, reviewStage,
reviewNextDueAt, reviewNextDueAt,
reviewDue, 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) { async _getUserByHashedId(hashedUserId) {
const user = await User.findOne({ where: { hashedId: hashedUserId } }); const user = await User.findOne({ where: { hashedId: hashedUserId } });
if (!user) { if (!user) {
@@ -2736,9 +2751,11 @@ export default class VocabService {
plainLesson.grammarExercises || [] plainLesson.grammarExercises || []
); );
const suppressLessonReviewDue = await this._courseHasDueSrsItems(user.id, plainLesson.courseId);
plainLesson.didactics = this._buildLessonDidactics(plainLesson); plainLesson.didactics = this._buildLessonDidactics(plainLesson);
plainLesson.pedagogy = this._buildLessonPedagogy(plainLesson); plainLesson.pedagogy = this._buildLessonPedagogy(plainLesson);
plainLesson.progress = this._serializeLessonProgress(progress, plainLesson); plainLesson.progress = this._serializeLessonProgress(progress, plainLesson, { suppressLessonReviewDue });
return plainLesson; return plainLesson;
} }
@@ -3265,7 +3282,8 @@ export default class VocabService {
order: [[{ model: VocabCourseLesson, as: 'lesson' }, 'lessonNumber', 'ASC']] 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 }) { async updateLessonProgress(hashedUserId, lessonId, { completed, score, timeSpentMinutes, lessonState }) {