mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
testing 127.0.0.1 issue
Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
@@ -189,8 +189,7 @@ exports.getMyInProgressCourses = async (req, res) => {
|
||||
if (!pending.length) return R.success(res, 'No courses in progress.', []);
|
||||
|
||||
const result = await Promise.all(pending.map(async (row) => {
|
||||
const courseId = row.course_id;
|
||||
const readingDone = row.status === 'completed';
|
||||
const courseId = row.course_id;
|
||||
|
||||
const [lessons_total, lessons_completed] = await Promise.all([
|
||||
Lesson.count({
|
||||
@@ -207,8 +206,13 @@ exports.getMyInProgressCourses = async (req, res) => {
|
||||
}),
|
||||
]);
|
||||
|
||||
let pending_quizzes = [];
|
||||
let pending_assessment = null;
|
||||
// "Reading done" is derived independently from lesson counts — row.status now also
|
||||
// requires the course assessment to be passed, so it can't be used as the reading gate.
|
||||
const readingDone = lessons_total > 0 && lessons_completed === lessons_total;
|
||||
|
||||
let pending_quizzes = [];
|
||||
let pending_assessment = null;
|
||||
let assessment_configured = true;
|
||||
|
||||
if (readingDone) {
|
||||
const unitQuizzes = await UnitQuiz.findAll({
|
||||
@@ -244,6 +248,7 @@ exports.getMyInProgressCourses = async (req, res) => {
|
||||
attributes: ['assessment_id', 'title', 'is_required', 'passing_score'],
|
||||
where: { course_id: courseId },
|
||||
});
|
||||
assessment_configured = !!assessment;
|
||||
if (assessment) {
|
||||
const [hasPassed, attemptCount] = await Promise.all([
|
||||
QuizAttempt.findOne({ where: { user_id: userId, assessment_id: assessment.assessment_id, passed: true } }),
|
||||
@@ -262,12 +267,13 @@ exports.getMyInProgressCourses = async (req, res) => {
|
||||
}
|
||||
|
||||
return {
|
||||
course_id: courseId,
|
||||
title: row.course.title,
|
||||
reading_status: row.status,
|
||||
course_id: courseId,
|
||||
title: row.course.title,
|
||||
reading_status: readingDone ? 'completed' : 'in_progress',
|
||||
assessment_configured,
|
||||
lessons_total,
|
||||
lessons_completed,
|
||||
last_accessed_at: row.last_accessed_at,
|
||||
last_accessed_at: row.last_accessed_at,
|
||||
pending_quizzes,
|
||||
pending_assessment,
|
||||
};
|
||||
|
||||
@@ -299,6 +299,13 @@ exports.getCourse = async (req, res) => {
|
||||
required: false,
|
||||
}],
|
||||
},
|
||||
{
|
||||
model: mdl_Category,
|
||||
as: "categories",
|
||||
through: { attributes: [] },
|
||||
required: false,
|
||||
attributes: ["id", "name", "slug"],
|
||||
},
|
||||
],
|
||||
order: [
|
||||
[{ model: Unit, as: "units" }, "order_index", "ASC"],
|
||||
|
||||
@@ -132,7 +132,7 @@ const isMember = async (userId, groupId) => {
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// REPLACEMENT: getGroupTaskList in task.controller.js (client)
|
||||
//
|
||||
// GET /client/groups/:groupId/task-lists/:taskListId?status=ongoing|done|overdue
|
||||
// GET /client/groups/:groupId/task-lists/:taskListId?status=ongoing|completed|overdue
|
||||
//
|
||||
// has_completed is now computed per-task as: ALL of the task's requirements
|
||||
// individually have a completion signal — matching RequirementsStatusPanel's
|
||||
@@ -147,17 +147,17 @@ const isMember = async (userId, groupId) => {
|
||||
// requirement_id (+ reference_id)
|
||||
//
|
||||
// Task bucket:
|
||||
// done → every requirement passes its check above
|
||||
// completed → every requirement passes its check above
|
||||
// (a task with zero requirements is vacuously "ongoing", per
|
||||
// earlier spec — zero requirements should not normally happen)
|
||||
// overdue → not done AND task.deadline < now
|
||||
// overdue → not completed AND task.deadline < now
|
||||
// ongoing → otherwise
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
exports.getGroupTaskList = async (req, res) => {
|
||||
try {
|
||||
const { groupId, taskListId } = req.params;
|
||||
const { status } = req.query; // optional: 'ongoing' | 'done' | 'overdue'
|
||||
const { status } = req.query; // optional: 'ongoing' | 'completed' | 'overdue'
|
||||
const userId = req.user.user_id;
|
||||
|
||||
const member = await isMember(userId, groupId);
|
||||
@@ -259,7 +259,7 @@ exports.getGroupTaskList = async (req, res) => {
|
||||
|
||||
let bucket;
|
||||
if (has_completed) {
|
||||
bucket = 'done';
|
||||
bucket = 'completed';
|
||||
} else if (task.deadline && new Date(task.deadline).getTime() < now) {
|
||||
bucket = 'overdue';
|
||||
} else {
|
||||
@@ -287,7 +287,7 @@ exports.getGroupTaskList = async (req, res) => {
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// REPLACEMENT: getGroupTaskLists in task.controller.js (client) — plural
|
||||
//
|
||||
// GET /client/groups/:groupId/task-lists?status=ongoing|done|overdue
|
||||
// GET /client/groups/:groupId/task-lists?status=ongoing|completed|overdue
|
||||
//
|
||||
// Updated to match getGroupTaskList (singular): has_completed per task now
|
||||
// means ALL of that task's requirements individually have a completion signal
|
||||
@@ -295,8 +295,8 @@ exports.getGroupTaskList = async (req, res) => {
|
||||
//
|
||||
// TaskList bucket (based on per-task has_completed, computed below):
|
||||
// TaskList has zero tasks → Ongoing (nothing to do yet)
|
||||
// ALL tasks have has_completed → Done
|
||||
// NOT all done AND any incomplete
|
||||
// ALL tasks have has_completed → Completed
|
||||
// NOT all completed AND any incomplete
|
||||
// task has deadline < now → Overdue
|
||||
// Otherwise → Ongoing
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
@@ -304,7 +304,7 @@ exports.getGroupTaskList = async (req, res) => {
|
||||
exports.getGroupTaskLists = async (req, res) => {
|
||||
try {
|
||||
const { groupId } = req.params;
|
||||
const { status } = req.query; // optional: 'ongoing' | 'done' | 'overdue'
|
||||
const { status } = req.query; // optional: 'ongoing' | 'completed' | 'overdue'
|
||||
const userId = req.user.user_id;
|
||||
|
||||
const member = await isMember(userId, groupId);
|
||||
@@ -423,7 +423,7 @@ exports.getGroupTaskLists = async (req, res) => {
|
||||
} else {
|
||||
const allDone = tasks.every((t) => t.has_completed);
|
||||
if (allDone) {
|
||||
bucket = 'done';
|
||||
bucket = 'completed';
|
||||
} else {
|
||||
const anyOverdue = tasks.some((t) =>
|
||||
!t.has_completed && t.deadline && new Date(t.deadline).getTime() < now
|
||||
|
||||
@@ -27,6 +27,9 @@ const sequelize = require('../../config/db.config');
|
||||
const { Task, TaskRequirement } = require('../../models/task/task.mdl');
|
||||
const { TaskLinkVisit, TaskProgress } = require('../../models/task/task_progress.mdl');
|
||||
const { mdl_UserGroupMembers } = require('../../models/users/user_groups.mdl');
|
||||
const { Course } = require('../../models/courses/courses.mdl');
|
||||
const CourseAssessment = require('../../models/courses/course_assessment.mdl');
|
||||
const QuizAttempt = require('../../models/courses/quiz_attempt.mdl');
|
||||
const logActivity = require('../../utils/logActivity.util');
|
||||
const R = require('../../utils/response.util');
|
||||
const { hydrateReadTaskProgress } = require('../../services/task_reading_progress_sync.service');
|
||||
@@ -67,8 +70,10 @@ const deriveUnitCompletion = async (userId, unitRequirementId, t) => {
|
||||
|
||||
// ─── Helper: derive course completion ────────────────────────────────────────
|
||||
// Course is complete when ALL read_unit progress rows under this course requirement
|
||||
// for this user are marked completed.
|
||||
const deriveCourseCompletion = async (userId, courseRequirementId, t) => {
|
||||
// for this user are marked completed AND, if the course has a built assessment,
|
||||
// the user has passed it. A course with no assessment yet can never be "complete" —
|
||||
// finishing the reading alone isn't course completion.
|
||||
const deriveCourseCompletion = async (userId, courseRequirementId, courseUuid, t) => {
|
||||
const rows = await TaskProgress.findAll({
|
||||
where: {
|
||||
requirement_id: courseRequirementId,
|
||||
@@ -78,7 +83,28 @@ const deriveCourseCompletion = async (userId, courseRequirementId, t) => {
|
||||
transaction: t,
|
||||
});
|
||||
if (!rows.length) return false;
|
||||
return rows.every((r) => r.completed);
|
||||
const allUnitsRead = rows.every((r) => r.completed);
|
||||
if (!allUnitsRead) return false;
|
||||
|
||||
const course = await Course.findOne({
|
||||
where: { uuid: courseUuid },
|
||||
attributes: ['course_id'],
|
||||
transaction: t,
|
||||
});
|
||||
if (!course) return false;
|
||||
|
||||
const assessment = await CourseAssessment.findOne({
|
||||
where: { course_id: course.course_id },
|
||||
attributes: ['assessment_id'],
|
||||
transaction: t,
|
||||
});
|
||||
if (!assessment) return false;
|
||||
|
||||
const passedAttempt = await QuizAttempt.findOne({
|
||||
where: { user_id: userId, assessment_id: assessment.assessment_id, passed: true },
|
||||
transaction: t,
|
||||
});
|
||||
return !!passedAttempt;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
@@ -352,7 +378,7 @@ exports.updateProgress = async (req, res) => {
|
||||
if (course_requirement_id) {
|
||||
const courseReq = await getRequirement(course_requirement_id, taskId);
|
||||
if (courseReq && courseReq.type === 'read_course') {
|
||||
const courseDone = await deriveCourseCompletion(userId, course_requirement_id, t);
|
||||
const courseDone = await deriveCourseCompletion(userId, course_requirement_id, courseReq.reference_id, t);
|
||||
|
||||
await TaskProgress.upsert(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user