Skip to content

Commit 36922a0

Browse files
committed
Merge branch 'ty/scrum-155-generator-refinement' of https://github.com/UTSC-CSCC01-Software-Engineering-I/term-group-project-c01w25-project-course-matrix into ty/scrum-155-generator-refinement
2 parents 104f0a2 + 64a4596 commit 36922a0

File tree

9 files changed

+669
-547
lines changed

9 files changed

+669
-547
lines changed

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

Lines changed: 211 additions & 175 deletions
Large diffs are not rendered by default.

course-matrix/backend/src/controllers/aiController.ts

Lines changed: 163 additions & 143 deletions
Large diffs are not rendered by default.

course-matrix/backend/src/controllers/restrictionsController.ts

Lines changed: 155 additions & 141 deletions
Large diffs are not rendered by default.
Lines changed: 85 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
import {z, ZodType} from 'zod';
1+
import { z, ZodType } from "zod";
22

33
export type TimetableForm = {
4-
name: string; date: Date; semester: string; search: string;
5-
courses: {id: number; code: string; name: string;}[];
4+
name: string;
5+
date: Date;
6+
semester: string;
7+
search: string;
8+
courses: { id: number; code: string; name: string }[];
69
restrictions: RestrictionForm[];
710
};
811

@@ -18,89 +21,106 @@ export type RestrictionForm = {
1821

1922
export const daysOfWeek = [
2023
{
21-
id: 'MO',
22-
label: 'Monday',
24+
id: "MO",
25+
label: "Monday",
2326
},
2427
{
25-
id: 'TU',
26-
label: 'Tuesday',
28+
id: "TU",
29+
label: "Tuesday",
2730
},
2831
{
29-
id: 'WE',
30-
label: 'Wednesday',
32+
id: "WE",
33+
label: "Wednesday",
3134
},
3235
{
33-
id: 'TH',
34-
label: 'Thursday',
36+
id: "TH",
37+
label: "Thursday",
3538
},
3639
{
37-
id: 'FR',
38-
label: 'Friday',
40+
id: "FR",
41+
label: "Friday",
3942
},
4043
] as const;
4144

42-
export const DayOfWeekEnum = z.enum(['MO', 'TU', 'WE', 'TH', 'FR']);
45+
export const DayOfWeekEnum = z.enum(["MO", "TU", "WE", "TH", "FR"]);
4346

44-
export const SemesterEnum = z.enum(['Summer 2025', 'Fall 2025', 'Winter 2026']);
47+
export const SemesterEnum = z.enum(["Summer 2025", "Fall 2025", "Winter 2026"]);
4548

4649
export const CourseSchema = z.object({
47-
id: z.number().describe('The id of the course'),
48-
code:
49-
z.string()
50-
.max(8, 'Invalid course code')
51-
.min(1, 'Course code is required')
52-
.describe(
53-
'The course code. Formatted like: CSCA08H3. Course codes cannot be provided without the H3 at the end.',
54-
),
55-
name: z.string().describe('The name of the course'),
50+
id: z.number().describe("The id of the course"),
51+
code: z
52+
.string()
53+
.max(8, "Invalid course code")
54+
.min(1, "Course code is required")
55+
.describe(
56+
"The course code. Formatted like: CSCA08H3. Course codes cannot be provided without the H3 at the end.",
57+
),
58+
name: z.string().describe("The name of the course"),
5659
});
5760

5861
export const RestrictionSchema = z.object({
59-
type:
60-
z.enum([
61-
'Restrict Before', 'Restrict After', 'Restrict Between',
62-
'Restrict Day', 'Days Off', 'Max Gap'
63-
])
64-
.describe(
65-
'The type of restriction being applied. Restrict before restricts all times before \'endTime\', Restrict Before restricts all times after \'startTime\', Restrict Between restricts all times between \'startTime\' and \'endTime\', Restrict Day restricts the entirety of each day in field \'days\', and Days Off enforces as least \'numDays\' days off per week.',
66-
),
67-
days: z.array(DayOfWeekEnum)
68-
.default(['MO', 'TU', 'WE', 'TH', 'FR'])
69-
.describe('Specific days of the week this restriction applies to'),
70-
numDays:
71-
z.number()
72-
.positive()
73-
.max(4, 'Cannot block all days of the week')
74-
.optional()
75-
.describe(
76-
'If type is Days Off, then this field is used and describes min number of days off per week. For example, if set to 2, and \'type\' is Days Off, then this means we want at least 2 days off per week.',
77-
),
78-
maxGap:
79-
z.number()
80-
.positive()
81-
.max(23, 'Cannot have a gap of an entire day for between courses')
82-
.optional()
83-
.describe(
84-
'If type is Max Gap, then this field is used to describe the maximum gap between courses, in hours. For example, if set to 3, then 2 entires must not be further than 3 hours apart. '),
85-
startTime: z.string().optional().describe(
86-
'If type is Restrict After, or Restrict Between, then this field describes the start time of the restricted time. Formatted HH:mm:ss',
87-
),
88-
endTime: z.string().optional().describe(
89-
'If type is Restrict Before, or Restrict Between, then this field describes the end time of the restricted time. Formatted HH:mm:ss',
90-
),
91-
disabled: z.boolean().optional().describe(
92-
'Whether this restriction is currently disabled'),
62+
type: z
63+
.enum([
64+
"Restrict Before",
65+
"Restrict After",
66+
"Restrict Between",
67+
"Restrict Day",
68+
"Days Off",
69+
"Max Gap",
70+
])
71+
.describe(
72+
"The type of restriction being applied. Restrict before restricts all times before 'endTime', Restrict Before restricts all times after 'startTime', Restrict Between restricts all times between 'startTime' and 'endTime', Restrict Day restricts the entirety of each day in field 'days', and Days Off enforces as least 'numDays' days off per week.",
73+
),
74+
days: z
75+
.array(DayOfWeekEnum)
76+
.default(["MO", "TU", "WE", "TH", "FR"])
77+
.describe("Specific days of the week this restriction applies to"),
78+
numDays: z
79+
.number()
80+
.positive()
81+
.max(4, "Cannot block all days of the week")
82+
.optional()
83+
.describe(
84+
"If type is Days Off, then this field is used and describes min number of days off per week. For example, if set to 2, and 'type' is Days Off, then this means we want at least 2 days off per week.",
85+
),
86+
maxGap: z
87+
.number()
88+
.positive()
89+
.max(23, "Cannot have a gap of an entire day for between courses")
90+
.optional()
91+
.describe(
92+
"If type is Max Gap, then this field is used to describe the maximum gap between courses, in hours. For example, if set to 3, then 2 entires must not be further than 3 hours apart. ",
93+
),
94+
startTime: z
95+
.string()
96+
.optional()
97+
.describe(
98+
"If type is Restrict After, or Restrict Between, then this field describes the start time of the restricted time. Formatted HH:mm:ss",
99+
),
100+
endTime: z
101+
.string()
102+
.optional()
103+
.describe(
104+
"If type is Restrict Before, or Restrict Between, then this field describes the end time of the restricted time. Formatted HH:mm:ss",
105+
),
106+
disabled: z
107+
.boolean()
108+
.optional()
109+
.describe("Whether this restriction is currently disabled"),
93110
});
94111

95112
export const TimetableFormSchema = z.object({
96-
name: z.string()
97-
.max(100, 'Name cannot exceed 100 characters')
98-
.min(1, 'Name cannot be empty')
99-
.describe('Title of timetable'),
100-
date: z.string().describe('Creation time of timetable'),
113+
name: z
114+
.string()
115+
.max(100, "Name cannot exceed 100 characters")
116+
.min(1, "Name cannot be empty")
117+
.describe("Title of timetable"),
118+
date: z.string().describe("Creation time of timetable"),
101119
semester: SemesterEnum,
102-
search: z.string().optional().describe(
103-
'Keeps track of search query. Only used in UI.'),
120+
search: z
121+
.string()
122+
.optional()
123+
.describe("Keeps track of search query. Only used in UI."),
104124
courses: z.array(CourseSchema),
105125
restrictions: z.array(RestrictionSchema),
106126
});

course-matrix/backend/src/services/getValidSchedules.ts

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
1-
import {CategorizedOfferingList, Offering} from '../types/generatorTypes';
2-
import {canInsertList, getFrequencyTable, getMinHour,} from '../utils/generatorHelpers';
1+
import { CategorizedOfferingList, Offering } from "../types/generatorTypes";
2+
import {
3+
canInsertList,
4+
getFrequencyTable,
5+
getMinHour,
6+
} from "../utils/generatorHelpers";
37

48
// Function to generate all valid schedules based on offerings and restrictions
59

610
export function getValidSchedules(
7-
validSchedules: Offering[][],
8-
courseOfferingsList: CategorizedOfferingList[], curList: Offering[],
9-
cur: number, len: number, maxdays: number, maxhours: number) {
11+
validSchedules: Offering[][],
12+
courseOfferingsList: CategorizedOfferingList[],
13+
curList: Offering[],
14+
cur: number,
15+
len: number,
16+
maxdays: number,
17+
maxhours: number,
18+
) {
1019
// Base case: if all courses have been considered
1120
if (cur == len) {
1221
const freq: Map<string, number> = getFrequencyTable(curList);
1322

1423
// If the number of unique days is within the allowed limit, add the current
1524
// schedule to the list, also checks if max gap is being violated
1625
if (freq.size <= maxdays && getMinHour(curList) <= maxhours) {
17-
console.log()
18-
validSchedules.push([...curList]); // Push a copy of the current list
26+
console.log();
27+
validSchedules.push([...curList]); // Push a copy of the current list
1928
}
2029
return;
2130
}
@@ -24,16 +33,22 @@ export function getValidSchedules(
2433

2534
// Recursively attempt to add offerings for the current course
2635
for (const [groupKey, offerings] of Object.entries(
27-
offeringsForCourse.offerings,
28-
)) {
36+
offeringsForCourse.offerings,
37+
)) {
2938
if (canInsertList(offerings, curList)) {
3039
const count = offerings.length;
31-
curList.push(...offerings); // Add offering to the current list
40+
curList.push(...offerings); // Add offering to the current list
3241

3342
// Recursively generate schedules for the next course
3443
getValidSchedules(
35-
validSchedules, courseOfferingsList, curList, cur + 1, len, maxdays,
36-
maxhours);
44+
validSchedules,
45+
courseOfferingsList,
46+
curList,
47+
cur + 1,
48+
len,
49+
maxdays,
50+
maxhours,
51+
);
3752

3853
// Backtrack: remove the last offering if no valid schedule was found
3954
for (let i = 0; i < count; i++) curList.pop();

course-matrix/frontend/src/pages/TimetableBuilder/Calendar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ const Calendar = React.memo<CalendarProps>(
262262
end_time: restriction.endTime,
263263
disabled: restriction.disabled,
264264
num_days: restriction.numDays,
265-
max_gap: restriction.maxGap
265+
max_gap: restriction.maxGap,
266266
};
267267
const { error: restrictionError } =
268268
await createRestriction(restrictionObject);
@@ -327,7 +327,7 @@ const Calendar = React.memo<CalendarProps>(
327327
end_time: restriction.endTime,
328328
disabled: restriction.disabled,
329329
num_days: restriction.numDays,
330-
max_gap: restriction.maxGap
330+
max_gap: restriction.maxGap,
331331
};
332332
const { error: restrictionError } =
333333
await createRestriction(restrictionObject);

course-matrix/frontend/src/pages/TimetableBuilder/CreateCustomSetting.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,10 @@ const CreateCustomSetting = ({
344344
name="maxGap"
345345
render={({ field }) => (
346346
<FormItem>
347-
<FormLabel>Max gap allowed between lectures/tutorials/practicals (hours)</FormLabel>
347+
<FormLabel>
348+
Max gap allowed between
349+
lectures/tutorials/practicals (hours)
350+
</FormLabel>
348351
<FormControl>
349352
<Input
350353
type="number"

course-matrix/frontend/src/pages/TimetableBuilder/TimetableBuilder.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ const TimetableBuilder = () => {
255255
: undefined,
256256
type: restriction?.type,
257257
numDays: restriction?.num_days,
258-
maxGap: restriction?.max_gap
258+
maxGap: restriction?.max_gap,
259259
}) as z.infer<typeof RestrictionSchema>,
260260
);
261261
form.setValue("restrictions", parsedRestrictions);

course-matrix/frontend/src/utils/type-utils.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
// Declare any useful typescript types here
22

3-
export type MakeOptionalExcept<T, K extends keyof T> =
4-
Partial<Omit<T, K>>&Pick<T, K>;
3+
export type MakeOptionalExcept<T, K extends keyof T> = Partial<Omit<T, K>> &
4+
Pick<T, K>;
55

66
export type Event = {
7-
id: number; event_name: string; event_date: string; event_start: string;
7+
id: number;
8+
event_name: string;
9+
event_date: string;
10+
event_start: string;
811
event_end: string;
912
offering_id: number;
1013
};
1114

1215
export type Offering = {
13-
id: number; created_at: Date; updated_at: Date; course_id: number;
16+
id: number;
17+
created_at: Date;
18+
updated_at: Date;
19+
course_id: number;
1420
meeting_section: string;
1521
offering: string;
1622
day: string;
@@ -27,17 +33,25 @@ export type Offering = {
2733
};
2834

2935
export type TimetableEvents = {
30-
courseEvents: Event[]; userEvents: Event[];
36+
courseEvents: Event[];
37+
userEvents: Event[];
3138
};
3239

3340
export type Timetable = {
34-
id: number; created_at: Date; updated_at: Date; semester: string;
41+
id: number;
42+
created_at: Date;
43+
updated_at: Date;
44+
semester: string;
3545
timetable_title: string;
3646
user_id: string;
3747
};
3848

3949
export type Restriction = {
40-
id: number; user_id: string; created_at: Date; updated_at: Date; type: string;
50+
id: number;
51+
user_id: string;
52+
created_at: Date;
53+
updated_at: Date;
54+
type: string;
4155
days: string;
4256
start_time: string;
4357
end_time: string;

0 commit comments

Comments
 (0)