Skip to content

Commit bebf8c0

Browse files
thomasyzy7dawangkkevin-lann
authored
Ty/scrum 153 timetable generation bug (#104)
Co-authored-by: dawangk <[email protected]> Co-authored-by: Kevin Lan <[email protected]>
1 parent 33ee4a7 commit bebf8c0

File tree

8 files changed

+244
-119
lines changed

8 files changed

+244
-119
lines changed

course-matrix/backend/__tests__/canInsert.test.ts

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { describe, expect, it, test } from "@jest/globals";
22

3-
import { createOffering, canInsert } from "../src/utils/generatorHelpers";
43
import { Offering } from "../src/types/generatorTypes";
4+
import {
5+
canInsert,
6+
canInsertList,
7+
createOffering,
8+
} from "../src/utils/generatorHelpers";
59

610
describe("canInsert function", () => {
711
const offering1: Offering = createOffering({
@@ -115,4 +119,52 @@ describe("canInsert function", () => {
115119

116120
expect(result).toBe(true); // Different day, no overlap
117121
});
122+
123+
it("special case", async () => {
124+
const toInsert: Offering = createOffering({
125+
id: 1069,
126+
course_id: 1271,
127+
day: "TH",
128+
start: "05:00:00",
129+
end: "17:00:00",
130+
});
131+
const offering11: Offering = createOffering({
132+
id: 414,
133+
course_id: 337,
134+
day: "TU",
135+
start: "15:00:00",
136+
end: "16:00:00",
137+
});
138+
const offering12: Offering = createOffering({
139+
id: 415,
140+
course_id: 337,
141+
day: "TH",
142+
start: "15:00:00",
143+
end: "17:00:00",
144+
});
145+
const offering13: Offering = createOffering({
146+
id: 1052,
147+
course_id: 1271,
148+
day: "TU",
149+
start: "10:00:00",
150+
end: "11:00:00",
151+
});
152+
const offering14: Offering = createOffering({
153+
id: 1053,
154+
course_id: 1271,
155+
day: "TU",
156+
start: "09:00:00",
157+
end: "11:00:00",
158+
});
159+
const curList: Offering[] = [
160+
offering11,
161+
offering12,
162+
offering13,
163+
offering14,
164+
];
165+
166+
const result = await canInsertList([toInsert], curList);
167+
168+
expect(result).toBe(false); // Special bug-causing case
169+
});
118170
});

course-matrix/backend/src/constants/availableFunctions.ts

Lines changed: 69 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
1+
import { Request } from "express";
2+
13
import { generateWeeklyCourseEvents } from "../controllers/eventsController";
24
import { supabase } from "../db/setupDb";
3-
import { Request } from "express";
5+
import { RestrictionForm } from "../models/timetable-form";
6+
import getOfferings from "../services/getOfferings";
7+
import { getValidSchedules } from "../services/getValidSchedules";
48
import {
59
GroupedOfferingList,
610
Offering,
711
OfferingList,
812
} from "../types/generatorTypes";
13+
import { convertTimeStringToDate } from "../utils/convert-time-string";
914
import {
1015
categorizeValidOfferings,
16+
getFreq,
1117
getMaxDays,
1218
getValidOfferings,
1319
groupOfferings,
1420
trim,
1521
} from "../utils/generatorHelpers";
16-
import getOfferings from "../services/getOfferings";
17-
import { getValidSchedules } from "../services/getValidSchedules";
18-
import { RestrictionForm } from "../models/timetable-form";
19-
import { convertTimeStringToDate } from "../utils/convert-time-string";
2022

2123
// Add all possible function names here
2224
export type FunctionNames =
@@ -34,10 +36,10 @@ type AvailableFunctions = {
3436
export const availableFunctions: AvailableFunctions = {
3537
getTimetables: async (args: any, req: Request) => {
3638
try {
37-
//Retrieve user_id
39+
// Retrieve user_id
3840
const user_id = (req as any).user.id;
3941

40-
//Retrieve user timetable item based on user_id
42+
// Retrieve user timetable item based on user_id
4143
let timeTableQuery = supabase
4244
.schema("timetable")
4345
.from("timetables")
@@ -49,7 +51,8 @@ export const availableFunctions: AvailableFunctions = {
4951

5052
if (timetableError) return { status: 400, error: timetableError.message };
5153

52-
// If no records were updated due to non-existence timetable or it doesn't belong to the user.
54+
// If no records were updated due to non-existence timetable or it doesn't
55+
// belong to the user.
5356
if (!timetableData || timetableData.length === 0) {
5457
return {
5558
status: 404,
@@ -75,10 +78,10 @@ export const availableFunctions: AvailableFunctions = {
7578
};
7679
}
7780

78-
//Retrieve the authenticated user
81+
// Retrieve the authenticated user
7982
const user_id = (req as any).user.id;
8083

81-
//Retrieve users allowed to access the timetable
84+
// Retrieve users allowed to access the timetable
8285
const { data: timetableUserData, error: timetableUserError } =
8386
await supabase
8487
.schema("timetable")
@@ -93,12 +96,12 @@ export const availableFunctions: AvailableFunctions = {
9396
if (timetableUserError)
9497
return { status: 400, error: timetableUserError.message };
9598

96-
//Validate timetable validity:
99+
// Validate timetable validity:
97100
if (!timetableUserData || timetableUserData.length === 0) {
98101
return { status: 404, error: "Calendar id not found" };
99102
}
100103

101-
//Validate user access
104+
// Validate user access
102105
if (user_id !== timetable_user_id) {
103106
return {
104107
status: 401,
@@ -110,7 +113,7 @@ export const availableFunctions: AvailableFunctions = {
110113
if (timetable_title) updateData.timetable_title = timetable_title;
111114
if (semester) updateData.semester = semester;
112115

113-
//Update timetable title, for authenticated user only
116+
// Update timetable title, for authenticated user only
114117
let updateTimetableQuery = supabase
115118
.schema("timetable")
116119
.from("timetables")
@@ -124,7 +127,8 @@ export const availableFunctions: AvailableFunctions = {
124127

125128
if (timetableError) return { status: 400, error: timetableError.message };
126129

127-
// If no records were updated due to non-existence timetable or it doesn't belong to the user.
130+
// If no records were updated due to non-existence timetable or it doesn't
131+
// belong to the user.
128132
if (!timetableData || timetableData.length === 0) {
129133
return {
130134
status: 404,
@@ -143,7 +147,7 @@ export const availableFunctions: AvailableFunctions = {
143147
// Retrieve the authenticated user
144148
const user_id = (req as any).user.id;
145149

146-
//Retrieve users allowed to access the timetable
150+
// Retrieve users allowed to access the timetable
147151
const { data: timetableUserData, error: timetableUserError } =
148152
await supabase
149153
.schema("timetable")
@@ -157,12 +161,12 @@ export const availableFunctions: AvailableFunctions = {
157161
if (timetableUserError)
158162
return { status: 400, error: timetableUserError.message };
159163

160-
//Validate timetable validity:
164+
// Validate timetable validity:
161165
if (!timetableUserData || timetableUserData.length === 0) {
162166
return { status: 404, error: "Calendar id not found" };
163167
}
164168

165-
//Validate user access
169+
// Validate user access
166170
if (user_id !== timetable_user_id) {
167171
return {
168172
status: 401,
@@ -190,10 +194,11 @@ export const availableFunctions: AvailableFunctions = {
190194
generateTimetable: async (args: any, req: Request) => {
191195
try {
192196
// Extract event details and course information from the request
193-
const { name, date, semester, search, courses, restrictions } = args;
197+
const { name, semester, courses, restrictions } = args;
198+
194199
const courseOfferingsList: OfferingList[] = [];
195200
const validCourseOfferingsList: GroupedOfferingList[] = [];
196-
const maxdays = await getMaxDays(restrictions);
201+
const maxdays = getMaxDays(restrictions);
197202
const validSchedules: Offering[][] = [];
198203
// Fetch offerings for each course
199204
for (const course of courses) {
@@ -203,27 +208,45 @@ export const availableFunctions: AvailableFunctions = {
203208
offerings: (await getOfferings(id, semester)) ?? [],
204209
});
205210
}
206-
207211
const groupedOfferingsList: GroupedOfferingList[] =
208-
await groupOfferings(courseOfferingsList);
209-
210-
// console.log(JSON.stringify(groupedOfferingsList, null, 2));
211-
212-
// Filter out invalid offerings based on the restrictions
213-
for (const { course_id, groups } of groupedOfferingsList) {
214-
validCourseOfferingsList.push({
212+
groupOfferings(courseOfferingsList);
213+
214+
for (const {
215+
course_id,
216+
groups,
217+
lectures,
218+
tutorials,
219+
practicals,
220+
} of groupedOfferingsList) {
221+
const group: Record<string, Offering[]> = getValidOfferings(
222+
groups,
223+
restrictions,
224+
);
225+
let groupedOfferings = {
215226
course_id: course_id,
216-
groups: await getValidOfferings(groups, restrictions),
217-
});
227+
groups: group,
228+
lectures: 0,
229+
tutorials: 0,
230+
practicals: 0,
231+
};
232+
groupedOfferings = getFreq(groupedOfferings);
233+
if (
234+
(lectures != 0 && groupedOfferings.lectures == 0) ||
235+
(tutorials != 0 && groupedOfferings.tutorials == 0) ||
236+
(practicals != 0 && groupedOfferings.practicals == 0)
237+
) {
238+
return {
239+
status: 404,
240+
error: "No valid schedules found. (Restriction)",
241+
};
242+
}
243+
244+
validCourseOfferingsList.push(groupedOfferings);
218245
}
219246

220-
const categorizedOfferings = await categorizeValidOfferings(
247+
const categorizedOfferings = categorizeValidOfferings(
221248
validCourseOfferingsList,
222249
);
223-
224-
// console.log(typeof categorizedOfferings);
225-
// console.log(JSON.stringify(categorizedOfferings, null, 2));
226-
227250
// Generate valid schedules for the given courses and restrictions
228251
await getValidSchedules(
229252
validSchedules,
@@ -241,10 +264,10 @@ export const availableFunctions: AvailableFunctions = {
241264

242265
// ------ CREATE FLOW ------
243266

244-
//Get user id from session authentication to insert in the user_id col
267+
// Get user id from session authentication to insert in the user_id col
245268
const user_id = (req as any).user.id;
246269

247-
//Retrieve timetable title
270+
// Retrieve timetable title
248271
const schedule = trim(validSchedules)[0];
249272
if (!name || !semester) {
250273
return {
@@ -266,7 +289,8 @@ export const availableFunctions: AvailableFunctions = {
266289
if (existingTimetableError) {
267290
return {
268291
status: 400,
269-
error: `Existing timetable with name: ${name}. Please rename timetable.`,
292+
error: `Existing timetable with name: ${name}. Please rename
293+
timetable.`,
270294
};
271295
}
272296

@@ -303,10 +327,10 @@ export const availableFunctions: AvailableFunctions = {
303327
error: "Timetable error" + timetableError.message,
304328
};
305329
}
306-
console.log("1");
330+
307331
// Insert events
308332
for (const offering of schedule) {
309-
//Query course offering information
333+
// Query course offering information
310334
const { data: offeringData, error: offeringError } = await supabase
311335
.schema("course")
312336
.from("offerings")
@@ -327,8 +351,9 @@ export const availableFunctions: AvailableFunctions = {
327351
};
328352
}
329353

330-
//Generate event details
331-
const courseEventName = ` ${offeringData.code} - ${offeringData.meeting_section} `;
354+
// Generate event details
355+
const courseEventName = ` ${offeringData.code} -
356+
${offeringData.meeting_section} `;
332357
const courseDay = offeringData.day;
333358
const courseStartTime = offeringData.start;
334359
const courseEndTime = offeringData.end;
@@ -370,8 +395,9 @@ export const availableFunctions: AvailableFunctions = {
370395
);
371396
}
372397

373-
//Each week lecture will be inputted as a separate events from sememseter start to end date
374-
//Semester start & end dates are inputted by user
398+
// Each week lecture will be inputted as a separate events from
399+
// sememseter start to end date Semester start & end dates are inputted
400+
// by user
375401
const { data: courseEventData, error: courseEventError } =
376402
await supabase
377403
.schema("timetable")

0 commit comments

Comments
 (0)