"use strict"; const REF_TYPE_CONFIG = { course: { pk: "course_id" }, unit: { pk: "unit_id" }, lesson: { pk: "lesson_id" }, }; /** * Enriches CoursePrerequisite rows ({ref_type, ref_id, ...}) with the * referenced entity's title (and subscription tier slug, where the entity * has one) — one batched findAll per ref_type instead of a query per row. * Rows whose referenced entity no longer exists (deleted) get title: null * so callers can drop or flag them instead of crashing. */ async function resolvePrerequisiteTitles(prereqs, { Course, Unit, Lesson }) { if (!prereqs?.length) return prereqs ?? []; const models = { course: Course, unit: Unit, lesson: Lesson }; // Only Course/Unit carry a subscription tier gate — Lesson has none. const attrsByType = { course: ["title", "subscription"], unit: ["title", "subscription"], lesson: ["title"] }; const idsByType = { course: new Set(), unit: new Set(), lesson: new Set() }; for (const p of prereqs) { if (idsByType[p.ref_type]) idsByType[p.ref_type].add(p.ref_id); } const infoMaps = {}; for (const [type, { pk }] of Object.entries(REF_TYPE_CONFIG)) { const ids = [...idsByType[type]]; infoMaps[type] = new Map(); if (!ids.length) continue; const rows = await models[type].findAll({ where: { [pk]: ids }, attributes: [pk, ...attrsByType[type]], }); for (const row of rows) infoMaps[type].set(String(row[pk]), row); } return prereqs.map((p) => { const info = infoMaps[p.ref_type]?.get(String(p.ref_id)); return { ...p, title: info?.title ?? null, subscription: info?.subscription ?? null, }; }); } /** * Enriches prerequisite rows (already carrying `title`) with `completed` — * whether the given learner has finished the referenced course/unit/lesson. * "Completed" means: a Certificate exists for a course prereq, or the * matching UnitReadingProgress/LessonReadingProgress row has status * "completed" for a unit/lesson prereq. */ async function resolvePrerequisiteCompletion(prereqs, { Certificate, UnitReadingProgress, LessonReadingProgress }, userId) { if (!prereqs?.length) return prereqs ?? []; const idsByType = { course: new Set(), unit: new Set(), lesson: new Set() }; for (const p of prereqs) { if (idsByType[p.ref_type]) idsByType[p.ref_type].add(p.ref_id); } const completedIds = { course: new Set(), unit: new Set(), lesson: new Set() }; const courseIds = [...idsByType.course]; if (courseIds.length) { const certs = await Certificate.findAll({ where: { user_id: userId, course_id: courseIds }, attributes: ["course_id"], }); certs.forEach((c) => completedIds.course.add(String(c.course_id))); } const unitIds = [...idsByType.unit]; if (unitIds.length) { const rows = await UnitReadingProgress.findAll({ where: { user_id: userId, unit_id: unitIds, status: "completed" }, attributes: ["unit_id"], }); rows.forEach((r) => completedIds.unit.add(String(r.unit_id))); } const lessonIds = [...idsByType.lesson]; if (lessonIds.length) { const rows = await LessonReadingProgress.findAll({ where: { user_id: userId, lesson_id: lessonIds, status: "completed" }, attributes: ["lesson_id"], }); rows.forEach((r) => completedIds.lesson.add(String(r.lesson_id))); } return prereqs.map((p) => ({ ...p, completed: completedIds[p.ref_type]?.has(String(p.ref_id)) ?? false, })); } module.exports = { resolvePrerequisiteTitles, resolvePrerequisiteCompletion };