From 1745990cdad6c0c5ac6f9f6cb3a549d9c5819671 Mon Sep 17 00:00:00 2001 From: Kenneth Obsequio Date: Wed, 15 Jul 2026 17:01:05 +0800 Subject: [PATCH] fix: throttle request issue per video,audio as indicated requirements in units and lessons Signed-off-by: Kenneth Obsequio --- controllers/client/courses.controller.js | 20 ++++++++++++++++++++ services/completion_requirements.service.js | 12 ++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/controllers/client/courses.controller.js b/controllers/client/courses.controller.js index f249c35..64ea8d8 100644 --- a/controllers/client/courses.controller.js +++ b/controllers/client/courses.controller.js @@ -1350,6 +1350,13 @@ exports.getLessonsByUnitUuid = async (req, res) => { const unitEvaluation = await evaluateEntity({ entityType: "unit", entityId: unit.unit_id, userId, courseId: null }); const is_completed = unitEvaluation.status === "completed"; + // Admin-configured completion requirement for the unit itself (pass_quiz / + // manual_complete / read_all_content) — null when nothing's configured. + const unitRequirement = await CompletionRequirement.findOne({ + where: { entity_type: "unit", entity_id: unit.unit_id }, + attributes: ["type", "min_percent", "button_label"], + }); + return R.success(res, "Unit lessons retrieved.", { unit_id: unit.unit_id, uuid: unit.uuid, @@ -1360,6 +1367,9 @@ exports.getLessonsByUnitUuid = async (req, res) => { courses: plain.courses ?? [], quiz, is_completed, + completion: unitRequirement + ? { type: unitRequirement.type, min_percent: unitRequirement.min_percent, button_label: unitRequirement.button_label } + : null, lessons, }); } catch (err) { @@ -1421,6 +1431,15 @@ exports.getLessonByUuid = async (req, res) => { attributes: ["status", "completed_at"], }); + // Admin-configured completion requirement (if any) — lets the standalone reader + // show what a learner must do to complete this lesson, same info the course-scoped + // reader gets via getCourse's per-lesson `completion` field. null when nothing's + // configured (default read_all_content behavior — nothing to display). + const requirement = await CompletionRequirement.findOne({ + where: { entity_type: "lesson", entity_id: lesson.lesson_id }, + attributes: ["type", "min_percent", "button_label"], + }); + const plain = lesson.toJSON(); const firstUnit = plain.units?.[0] ?? null; const data = { @@ -1433,6 +1452,7 @@ exports.getLessonByUuid = async (req, res) => { objectives: plain.objectives ?? [], status: progress?.status ?? "not_started", completed_at: progress?.completed_at ?? null, + completion: requirement ? { type: requirement.type, min_percent: requirement.min_percent, button_label: requirement.button_label } : null, unit: firstUnit ? { unit_id: firstUnit.unit_id, uuid: firstUnit.uuid, title: firstUnit.title, duration_seconds: firstUnit.duration_seconds, course: firstUnit.courses?.[0] ?? null } : null, // back-compat singular field units: plain.units ?? [], }; diff --git a/services/completion_requirements.service.js b/services/completion_requirements.service.js index 5d0e177..dd84543 100644 --- a/services/completion_requirements.service.js +++ b/services/completion_requirements.service.js @@ -219,11 +219,17 @@ async function recomputeCourseAfterAssessment(userId, courseId, t) { * live block set on every write, not a snapshot taken when configured). * No-ops (returns null) if the lesson has neither type configured. */ +// text-video counts as a video block for watch_video purposes — same