From fdf2b8f8aaa83c958831c564c39d34303b7b722f Mon Sep 17 00:00:00 2001 From: Carifio24 Date: Tue, 29 Oct 2024 17:34:31 -0400 Subject: [PATCH] Update class measurement functions to use new merge groups. --- src/stories/hubbles_law/database.ts | 76 +++++++++++++---------------- src/stories/hubbles_law/router.ts | 22 ++++++--- 2 files changed, 51 insertions(+), 47 deletions(-) diff --git a/src/stories/hubbles_law/database.ts b/src/stories/hubbles_law/database.ts index f149d2a..e655d74 100644 --- a/src/stories/hubbles_law/database.ts +++ b/src/stories/hubbles_law/database.ts @@ -7,6 +7,7 @@ import { HubbleStudentData } from "./models/hubble_student_data"; import { HubbleClassData } from "./models/hubble_class_data"; import { IgnoreStudent } from "../../models/ignore_student"; import { logger } from "../../logger"; +import { HubbleClassMergeGroup } from "./models/hubble_class_merge_group"; const galaxyAttributes = ["id", "ra", "decl", "z", "type", "name", "element"]; @@ -207,7 +208,9 @@ export async function getStudentHubbleMeasurements(studentID: number): Promise { +async function getHubbleMeasurementsForStudentClass(studentID: number, classID: number, excludeWithNull: boolean = false): Promise { + + const classIDs = await getMergedIDsForClass(classID); const studentWhereConditions: WhereOptions = []; const classDataStudentIDs = await getClassDataIDsForStudent(studentID); @@ -317,6 +320,28 @@ async function getClassIDsForSyncClass(classID: number): Promise { return classIDs; } +async function getMergedIDsForClass(classID: number): Promise { + // TODO: Currently this uses two queries: + // The first to get the merge group (if there is one) + // Then a second to get all of the classes in the merge group + // Maybe we can just write some SQL to make these one query? + const mergeGroup = await HubbleClassMergeGroup.findOne({ + where: { + class_id: classID + } + }); + if (mergeGroup === null) { + return [classID]; + } + + const mergeEntries = await HubbleClassMergeGroup.findAll({ + where: { + group_id: mergeGroup.group_id, + } + }); + return mergeEntries.map(entry => entry.class_id); +} + export async function getClassDataIDsForStudent(studentID: number): Promise { const state = (await StoryState.findOne({ where: { student_id: studentID }, @@ -329,69 +354,38 @@ export async function getClassDataIDsForStudent(studentID: number): Promise { - const classIDs = await getClassIDsForSyncClass(classID); - return getHubbleMeasurementsForStudentClasses(studentID, classIDs, excludeWithNull); -} - -async function getHubbleMeasurementsForAsyncStudent(studentID: number, classID: number | null, excludeWithNull: boolean = false): Promise { - const classIDs = await getClassIDsForAsyncStudent(studentID, classID); - return getHubbleMeasurementsForStudentClasses(studentID, classIDs, excludeWithNull); -} - export async function getClassMeasurements(studentID: number, - classID: number | null, + classID: number, lastChecked: number | null = null, excludeWithNull: boolean = false, ): 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, excludeWithNull); - } else { - data = await getHubbleMeasurementsForSyncStudent(studentID, classID, excludeWithNull); - } - if (data != null && lastChecked != null) { + let data = await getHubbleMeasurementsForStudentClass(studentID, classID, excludeWithNull); + if (data.length > 0 && lastChecked != null) { const lastModified = Math.max(...data.map(meas => meas.last_modified.getTime())); if (lastModified <= lastChecked) { - data = null; + data = []; } } - return data ?? []; + return data; } // 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 number of measurements export async function getClassMeasurementCount(studentID: number, - classID: number | null, + classID: number, excludeWithNull: boolean = false, ): 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, excludeWithNull); - } else { - data = await getHubbleMeasurementsForSyncStudent(studentID, classID, excludeWithNull); - } + const data = await getClassMeasurements(studentID, classID, null, 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, + classID: number, ): 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 data = await getHubbleMeasurementsForStudentClass(studentID, classID, true); const counts: Record = {}; data?.forEach(measurement => { if (measurement.student_id in counts) { diff --git a/src/stories/hubbles_law/router.ts b/src/stories/hubbles_law/router.ts index 108b20c..e721ac0 100644 --- a/src/stories/hubbles_law/router.ts +++ b/src/stories/hubbles_law/router.ts @@ -44,7 +44,7 @@ import { import { Express, Router } from "express"; import { Sequelize } from "sequelize"; -import { findClassById, findStudentById } from "../../database"; +import { classForStudentStory, findClassById, findStudentById } from "../../database"; import { SyncMergedHubbleClasses, initializeModels } from "./models"; import { setUpHubbleAssociations } from "./associations"; @@ -345,8 +345,10 @@ router.get(["/class-measurements/:studentID/:classID", "/stage-3-data/:studentID classID = 159; } - const invalidStudent = (await findStudentById(studentID)) === null; - const invalidClass = (await findClassById(classID)) === null; + const student = await findStudentById(studentID); + const invalidStudent = student === null; + const cls = await findClassById(classID); + const invalidClass = cls === null; if (invalidStudent || invalidClass) { const invalidItems = []; if (invalidStudent) { invalidItems.push("student"); } @@ -358,7 +360,7 @@ router.get(["/class-measurements/:studentID/:classID", "/stage-3-data/:studentID return; } - const measurements = await getClassMeasurements(studentID, classID, lastChecked, completeOnly); + const measurements = await getClassMeasurements(student.id, cls.id, lastChecked, completeOnly); res.status(200).json({ student_id: studentID, class_id: classID, @@ -372,12 +374,20 @@ router.get(["/class-measurements/:studentID", "stage-3-measurements/:studentID"] const isValidStudent = (await findStudentById(studentID)) !== null; if (!isValidStudent) { res.status(404).json({ - message: "Invalid student ID" + message: "Invalid student ID", + }); + return; + } + + const cls = await classForStudentStory(studentID, "hubbles_law"); + if (cls === null) { + res.status(404).json({ + message: `Student ${studentID} is not in a class signed up for the Hubble's Law story`, }); return; } - const measurements = await getClassMeasurements(studentID, null); + const measurements = await getClassMeasurements(studentID, cls.id); res.status(200).json({ student_id: studentID, class_id: null,