mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
added new requirements for lessons and units
Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
@@ -43,7 +43,7 @@ const {
|
||||
const coursesCtrl = require("./courses.controller"); // canAccessUnit / canAccessLesson / shared uuid handlers
|
||||
const { gradeSubmission } = require("../../utils/courses/grading.util");
|
||||
const { shuffleOptions, shuffleQuestions, getAttemptStatus } = require("../../utils/courses/quiz_security.util");
|
||||
const { upsertLessonRead } = require("../../services/reading_progress.service");
|
||||
const { recomputeCascade, recordWatchProgress, recordManualComplete } = require("../../services/completion_requirements.service");
|
||||
|
||||
const notDeleted = { deletedAt: null };
|
||||
|
||||
@@ -343,8 +343,11 @@ exports.saveUnitQuizDraft = async (req, res) => {
|
||||
|
||||
// ─── STANDALONE LESSON PROGRESS ───────────────────────────────────────────────
|
||||
// POST /client/lessons/:uuid/progress Body: { status, unit_uuid? }
|
||||
// Writes lesson_reading_progress with course_id NULL. When unit_uuid is given
|
||||
// (unit context, still no course) the parent unit row is derived + upserted too.
|
||||
// Evaluated + written via completion_requirements.service#recomputeCascade with
|
||||
// courseId null, which persists to lesson_reading_progress/unit_reading_progress
|
||||
// (the tables that tolerate a null course_id) instead of course_reading_progress.
|
||||
// When unit_uuid is given (unit context, still no course) the parent unit is
|
||||
// re-evaluated + upserted too, against any configured CompletionRequirement rows.
|
||||
|
||||
exports.upsertStandaloneLessonProgress = async (req, res) => {
|
||||
try {
|
||||
@@ -369,10 +372,12 @@ exports.upsertStandaloneLessonProgress = async (req, res) => {
|
||||
unitId = unit.unit_id;
|
||||
}
|
||||
|
||||
const result = await upsertLessonRead(userId, {
|
||||
const result = await recomputeCascade(userId, {
|
||||
courseId: null,
|
||||
unitId,
|
||||
unitUuid,
|
||||
lessonId: lesson.lesson_id,
|
||||
lessonUuid: lesson.uuid,
|
||||
lessonStatus: status,
|
||||
});
|
||||
|
||||
@@ -389,6 +394,82 @@ exports.upsertStandaloneLessonProgress = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// ─── STANDALONE WATCH PROGRESS ─────────────────────────────────────────────────
|
||||
// POST /client/lessons/:uuid/watch-progress Body: { percent, unit_uuid?, block_id?, block_type? }
|
||||
// block_id/block_type identify which block on the lesson's page sent this update — needed
|
||||
// to drive watch_video/listen_audio; omit them and only watch_percent (if configured) is touched.
|
||||
exports.upsertStandaloneWatchProgress = async (req, res) => {
|
||||
try {
|
||||
const { uuid } = req.params;
|
||||
const userId = req.user.user_id;
|
||||
const percent = Number(req.body.percent);
|
||||
if (!Number.isFinite(percent)) return R.error(res, "percent must be a number.", 400);
|
||||
const unitUuid = req.body.unit_uuid ?? null;
|
||||
const blockId = req.body.block_id ?? null;
|
||||
const blockType = req.body.block_type ?? null;
|
||||
|
||||
const lesson = await Lesson.findOne({ where: { uuid, ...notDeleted }, attributes: ["lesson_id", "uuid"] });
|
||||
if (!lesson) return R.error(res, "Lesson not found.", 404);
|
||||
if (!await coursesCtrl.canAccessLesson(userId, lesson.lesson_id)) {
|
||||
return R.error(res, "You do not have access to this lesson.", 403);
|
||||
}
|
||||
|
||||
let unitId = null;
|
||||
if (unitUuid) {
|
||||
const unit = await Unit.findOne({ where: { uuid: unitUuid, ...notDeleted }, attributes: ["unit_id"] });
|
||||
if (!unit) return R.error(res, "Unit not found.", 404);
|
||||
unitId = unit.unit_id;
|
||||
}
|
||||
|
||||
const result = await recordWatchProgress(userId, {
|
||||
lessonId: lesson.lesson_id, lessonUuid: lesson.uuid,
|
||||
unitId, unitUuid,
|
||||
courseId: null, courseUuid: null,
|
||||
percent, blockId, blockType,
|
||||
});
|
||||
|
||||
return R.success(res, "Watch progress updated.", result, 200);
|
||||
} catch (err) {
|
||||
console.error("[CLIENT][LESSONS][STANDALONE WATCH PROGRESS]", err);
|
||||
return R.error(res, "Could not update watch progress.", 500);
|
||||
}
|
||||
};
|
||||
|
||||
// ─── STANDALONE MARK COMPLETE ──────────────────────────────────────────────────
|
||||
// POST /client/lessons/:uuid/mark-complete Body: { unit_uuid? }
|
||||
exports.markStandaloneLessonComplete = async (req, res) => {
|
||||
try {
|
||||
const { uuid } = req.params;
|
||||
const userId = req.user.user_id;
|
||||
const unitUuid = req.body.unit_uuid ?? null;
|
||||
|
||||
const lesson = await Lesson.findOne({ where: { uuid, ...notDeleted }, attributes: ["lesson_id", "uuid"] });
|
||||
if (!lesson) return R.error(res, "Lesson not found.", 404);
|
||||
if (!await coursesCtrl.canAccessLesson(userId, lesson.lesson_id)) {
|
||||
return R.error(res, "You do not have access to this lesson.", 403);
|
||||
}
|
||||
|
||||
let unitId = null;
|
||||
if (unitUuid) {
|
||||
const unit = await Unit.findOne({ where: { uuid: unitUuid, ...notDeleted }, attributes: ["unit_id"] });
|
||||
if (!unit) return R.error(res, "Unit not found.", 404);
|
||||
unitId = unit.unit_id;
|
||||
}
|
||||
|
||||
const result = await recordManualComplete(userId, {
|
||||
entityType: "lesson", entityId: lesson.lesson_id,
|
||||
lessonUuid: lesson.uuid,
|
||||
unitId, unitUuid,
|
||||
courseId: null, courseUuid: null,
|
||||
});
|
||||
|
||||
return R.success(res, "Lesson marked complete.", result, 200);
|
||||
} catch (err) {
|
||||
console.error("[CLIENT][LESSONS][STANDALONE MARK COMPLETE]", err);
|
||||
return R.error(res, "Could not mark lesson complete.", 500);
|
||||
}
|
||||
};
|
||||
|
||||
// ─── Shared UUID handlers re-exported for the standalone routes ───────────────
|
||||
|
||||
exports.getUnitByUuid = coursesCtrl.getUnitByUuid;
|
||||
|
||||
Reference in New Issue
Block a user