diff --git a/src/stories/hubbles_law/database.ts b/src/stories/hubbles_law/database.ts index 5cfeb1b..697b95f 100644 --- a/src/stories/hubbles_law/database.ts +++ b/src/stories/hubbles_law/database.ts @@ -367,22 +367,50 @@ export async function getClassMeasurements(studentID: number, // The advantage of this over the function above is that it saves bandwidth, // since we aren't sending the data itself. -// This is intended to be used with cases where we need to frequently check the class size, -// e.g. the beginning of stage 4 in the Hubble story +// This is intended to be used with cases where we need to frequently check the number of measurements export async function getClassMeasurementCount(studentID: number, classID: number | null, excludeWithNull: boolean = false, ): Promise { - const cls = classID !== null ? await findClassById(classID) : null; - const asyncClass = cls?.asynchronous ?? true; - let data: HubbleMeasurement[] | null; - console.log(classID, asyncClass); - if (classID === null || asyncClass) { - data = await getHubbleMeasurementsForAsyncStudent(studentID, classID, excludeWithNull); + const cls = classID !== null ? await findClassById(classID) : null; + const asyncClass = cls?.asynchronous ?? true; + let data: HubbleMeasurement[] | null; + if (classID === null || asyncClass) { + data = await getHubbleMeasurementsForAsyncStudent(studentID, classID, excludeWithNull); + } else { + data = await getHubbleMeasurementsForSyncStudent(studentID, classID, excludeWithNull); + } + return data?.length ?? 0; +} + +// Similar to the function above, this is intended for cases where we need to frequently check +// how many students have completed their measurements, such as the beginning of Stage 4 in the Hubble story +export async function getStudentsWithCompleteMeasurementsCount(studentID: number, + classID: number | null, +): Promise { + const cls = classID !== null ? await findClassById(classID) : null; + const asyncClass = cls?.asynchronous ?? true; + let data: HubbleMeasurement[] | null; + if (classID === null || asyncClass) { + data = await getHubbleMeasurementsForAsyncStudent(studentID, classID, true); + } else { + data = await getHubbleMeasurementsForSyncStudent(studentID, classID, true); + } + const counts: Record = {}; + data?.forEach(measurement => { + if (measurement.student_id in counts) { + counts[measurement.student_id] += 1; } else { - data = await getHubbleMeasurementsForSyncStudent(studentID, classID, excludeWithNull); + counts[measurement.student_id] = 1; } - return data?.length ?? 0; + }); + let num = 0; + for (const id in counts) { + if (counts[id] >= 5) { + num += 1; + } + } + return num; } async function getHubbleStudentDataForAsyncStudent(studentID: number, classID: number | null): Promise { diff --git a/src/stories/hubbles_law/router.ts b/src/stories/hubbles_law/router.ts index 46783c4..61c6761 100644 --- a/src/stories/hubbles_law/router.ts +++ b/src/stories/hubbles_law/router.ts @@ -33,7 +33,8 @@ import { removeSampleHubbleMeasurement, getAllNthSampleHubbleMeasurements, tryToMergeClass, - getClassMeasurementCount + getClassMeasurementCount, + getStudentsWithCompleteMeasurementsCount } from "./database"; import { @@ -284,10 +285,41 @@ router.get("/class-measurements/size/:studentID/:classID", async (req, res) => { const count = await getClassMeasurementCount(studentID, classID, completeOnly); res.status(200).json({ student_id: studentID, + class_id: classID, measurement_count: count, }); }); +router.get("/class-measurements/students-completed/:studentID/:classID", async (req, res) => { + res.header("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1 + res.header("Pragma", "no-cache"); // HTTP 1.0 + res.header("Expires", "0"); // Proxies + const studentID = parseInt(req.params.studentID); + const isValidStudent = (await findStudentById(studentID)) !== null; + if (!isValidStudent) { + res.status(404).json({ + message: "Invalid student ID", + }); + return; + } + + const classID = parseInt(req.params.classID); + const isValidClass = (await findClassById(classID)) !== null; + if (!isValidClass) { + res.status(404).json({ + message: "Invalid class ID", + }); + return; + } + + const count = await getStudentsWithCompleteMeasurementsCount(studentID, classID); + res.status(200).json({ + student_id: studentID, + class_id: classID, + students_completed_measurements: count, + }); +}); + router.get(["/class-measurements/:studentID/:classID", "/stage-3-data/:studentID/:classID"], async (req, res) => { const lastCheckedStr = req.query.last_checked as string; let lastChecked: number | null = parseInt(lastCheckedStr);