good morning

Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
2026-08-06 08:11:05 +08:00
parent 141b7ab592
commit dcae93eb35
15 changed files with 81 additions and 204 deletions
-53
View File
@@ -2535,59 +2535,6 @@ exports.getLessonsFlat = async (req, res) => {
}
};
// One row per unit quiz (a quiz is always unit-scoped, unit_id unique on
// unit_quizzes) — `courses[]` is the deduped set of courses the parent unit
// is attached to, batch-fetched the same way as getUnitsFlat/getLessonsFlat.
// Used by the pass_quiz task requirement picker — same "no content yet"
// convention as read_*: question_count === 0 is flagged the same way
// duration_seconds === 0 is for content requirements.
exports.getQuizzesFlat = async (req, res) => {
try {
const rows = await sequelize.query(`
SELECT
q.quiz_id, q.uuid, q.title, u.title AS unit_title,
(SELECT CAST(COUNT(*) AS INTEGER) FROM quiz_questions qq
WHERE qq.quiz_id = q.quiz_id AND qq."deletedAt" IS NULL) AS question_count
FROM unit_quizzes q
JOIN units u ON u.unit_id = q.unit_id AND u."deletedAt" IS NULL
WHERE q."deletedAt" IS NULL
ORDER BY u.title ASC, q.title ASC
`, { type: sequelize.QueryTypes.SELECT });
const quizIds = rows.map((r) => r.quiz_id);
const courseLinkRows = quizIds.length ? await sequelize.query(`
SELECT q.quiz_id, c.uuid, c.title
FROM unit_quizzes q
JOIN course_units cu ON cu.unit_id = q.unit_id
JOIN courses c ON c.course_id = cu.course_id AND c."deletedAt" IS NULL
WHERE q.quiz_id IN (:quizIds)
ORDER BY c.title ASC
`, { replacements: { quizIds }, type: sequelize.QueryTypes.SELECT }) : [];
const coursesByQuiz = new Map();
for (const row of courseLinkRows) {
const list = coursesByQuiz.get(row.quiz_id) ?? [];
list.push({ uuid: row.uuid, title: row.title });
coursesByQuiz.set(row.quiz_id, list);
}
const data = rows.map((r) => ({
uuid: r.uuid,
title: r.title || `${r.unit_title} Quiz`,
unit_title: r.unit_title ?? "",
courses: coursesByQuiz.get(r.quiz_id) ?? [],
question_count: Number(r.question_count ?? 0),
// duration_seconds doesn't apply to quizzes — ContentPicker's "no
// content" check keys off duration_seconds === 0, so surface the same
// signal under that name rather than adding a second code path.
duration_seconds: Number(r.question_count ?? 0),
}));
return R.success(res, "Quizzes retrieved.", data);
} catch (err) {
console.error("[QUIZ][GET FLAT]", err);
return R.error(res, "Could not retrieve quizzes.", 500);
}
};
// ══════════════════════════════════════════════════════════════════════════════
// COURSE INSTRUCTORS
// ══════════════════════════════════════════════════════════════════════════════