Skip to content

Commit 2e2a45d

Browse files
kevin-lannthomasyzy7dawangk
authored
Ty/scrum 159 timetable limit (#130)
Co-authored-by: dawangk <[email protected]> Co-authored-by: dawangk <[email protected]> Co-authored-by: kevin-lann <[email protected]>
1 parent 97047f9 commit 2e2a45d

File tree

4 files changed

+67
-18
lines changed

4 files changed

+67
-18
lines changed

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

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ export const availableFunctions: AvailableFunctions = {
4141
try {
4242
// Retrieve user_id
4343
const user_id = (req as any).user.id;
44-
4544
// Retrieve user timetable item based on user_id
4645
let timeTableQuery = supabase
4746
.schema("timetable")
@@ -63,7 +62,11 @@ export const availableFunctions: AvailableFunctions = {
6362
};
6463
}
6564

66-
return { status: 200, data: timetableData };
65+
return {
66+
status: 200,
67+
timetableCount: timetableData.length,
68+
data: timetableData,
69+
};
6770
} catch (error) {
6871
console.log(error);
6972
return { status: 400, error: error };
@@ -198,13 +201,33 @@ export const availableFunctions: AvailableFunctions = {
198201
try {
199202
// Extract event details and course information from the request
200203
const { name, semester, courses, restrictions } = args;
204+
// Get user id from session authentication to insert in the user_id col
205+
const user_id = (req as any).user.id;
206+
201207
if (name.length > 50) {
202208
return {
203209
status: 400,
204210
error: "timetable title is over 50 characters long",
205211
};
206212
}
207213

214+
// Timetables cannot exceed the size of 25.
215+
const { count: timetable_count, error: timetableCountError } =
216+
await supabase
217+
.schema("timetable")
218+
.from("timetables")
219+
.select("*", { count: "exact", head: true })
220+
.eq("user_id", user_id);
221+
222+
console.log(timetable_count);
223+
224+
if ((timetable_count ?? 0) >= 25) {
225+
return {
226+
status: 400,
227+
error: "You have exceeded the limit of 25 timetables",
228+
};
229+
}
230+
208231
const courseOfferingsList: OfferingList[] = [];
209232
const validCourseOfferingsList: GroupedOfferingList[] = [];
210233
const maxdays = getMaxDays(restrictions);
@@ -277,9 +300,6 @@ export const availableFunctions: AvailableFunctions = {
277300

278301
// ------ CREATE FLOW ------
279302

280-
// Get user id from session authentication to insert in the user_id col
281-
const user_id = (req as any).user.id;
282-
283303
// Retrieve timetable title
284304
const schedule = trim(validSchedules)[0];
285305
if (!name || !semester) {

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ export const chat = asyncHandler(async (req: Request, res: Response) => {
244244
- Allowing natural language queries about courses, offerings, and academic programs
245245
- Providing personalized recommendations based on degree requirements and course availability
246246
- Creating, reading, updating, and deleting user timetables based on natural language
247-
247+
248248
## Your Capabilities
249249
- Create new timetables based on provided courses and restrictions
250250
- Update timetable names and semesters
@@ -266,11 +266,12 @@ export const chat = asyncHandler(async (req: Request, res: Response) => {
266266
}/dashboard/timetable?edit=[[TIMETABLE_ID]] , where TIMETABLE_ID is the id of the respective timetable.
267267
- If the user provides a course code of length 6 like CSCA08, then assume they mean CSCA08H3 (H3 appended)
268268
- If the user wants to create a timetable:
269-
1. First call getCourses to get course information on the requested courses,
270-
2. If the user provided a semester, then call getOfferings with the provided courses and semester to ensure the courses are actually offered in the semester.
269+
1. First call getTimetables to refetch most recent info on timetable names + timetableCount. DO NOT assume timetable names and # of timetables is same since last query.
270+
2. Then call getCourses to get course information on the requested courses,
271+
3. If the user provided a semester, then call getOfferings with the provided courses and semester to ensure the courses are actually offered in the semester.
271272
a) If a course is NOT returned by getOFferings, then list it under "Excluded courses" with "reason: not offered in [provided semester]"
272273
b) If no courses have offerings, then do not generate the timetable.
273-
3. Lastly, call generateTimetable with the provided information.
274+
4. Lastly, call generateTimetable with the provided information.
274275
- Do not make up fake courses or offerings.
275276
- If a user asks about a course that you do not know of, acknowledge this.
276277
- You can only edit title of the timetable, nothing else. If a user tries to edit something else, acknowledge this limitation.
@@ -280,12 +281,14 @@ export const chat = asyncHandler(async (req: Request, res: Response) => {
280281
- After a deletion has been cancelled, /timetable confirm will do nothing. If the user wants to delete again after cancelling, they must specify so.
281282
- Do not create multiple timetables for a single user query. Each user query can create at most 1 timetable
282283
- If you try to update or create a timetable but you get an error saying a timetable with the same name already exists, then ask the user to rename
284+
- It is possible for users to delete timetables / update them manually. For this reason, always refetch getTimtables before creation, to get the latest names and timetable count. Do not assume the timetables are the same since the last query.
285+
- If a user asks for the timetable count, always refetch getTimetables. Assume this count could have changed between user queries.
283286
`,
284287
messages,
285288
tools: {
286289
getTimetables: tool({
287290
description:
288-
"Get all the timetables of the currently logged in user.",
291+
"Get all the timetables of the currently logged in user AND the number of timetables of the currently logged in user",
289292
parameters: z.object({}),
290293
execute: async (args) => {
291294
return await availableFunctions.getTimetables(args, req);

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

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,21 @@ export default {
3131
.status(400)
3232
.json({ error: "Timetable Title cannot be over 50 characters long" });
3333
}
34+
// Timetables cannot exceed the size of 25.
35+
const { count: timetable_count, error: timetableCountError } =
36+
await supabase
37+
.schema("timetable")
38+
.from("timetables")
39+
.select("*", { count: "exact", head: true })
40+
.eq("user_id", user_id);
41+
42+
console.log(timetable_count);
43+
44+
if ((timetable_count ?? 0) >= 25) {
45+
return res
46+
.status(400)
47+
.json({ error: "You have exceeded the limit of 25 timetables" });
48+
}
3449

3550
// Check if a timetable with the same title already exist for this user
3651
const { data: existingTimetable, error: existingTimetableError } =
@@ -52,6 +67,7 @@ export default {
5267
.json({ error: "A timetable with this title already exists" });
5368
}
5469
//Create query to insert the user_id and timetable_title into the db
70+
5571
let insertTimetable = supabase
5672
.schema("timetable")
5773
.from("timetables")
@@ -184,7 +200,6 @@ export default {
184200

185201
//Retrieve the authenticated user
186202
const user_id = (req as any).user.id;
187-
188203
//Retrieve users allowed to access the timetable
189204
const { data: timetableUserData, error: timetableUserError } =
190205
await supabase
@@ -194,15 +209,13 @@ export default {
194209
.eq("user_id", user_id)
195210
.eq("id", id)
196211
.maybeSingle();
197-
198212
if (timetableUserError)
199213
return res.status(400).json({ error: timetableUserError.message });
200214

201215
//Validate timetable validity:
202216
if (!timetableUserData || timetableUserData.length === 0) {
203217
return res.status(404).json({ error: "Calendar id not found" });
204218
}
205-
206219
// Check if another timetable with the same title already exist for this user
207220
const { data: existingTimetable, error: existingTimetableError } =
208221
await supabase
@@ -213,7 +226,6 @@ export default {
213226
.eq("timetable_title", timetable_title)
214227
.neq("id", id)
215228
.maybeSingle();
216-
217229
if (existingTimetableError) {
218230
return res.status(400).json({ error: existingTimetableError.message });
219231
}
@@ -223,7 +235,6 @@ export default {
223235
.status(400)
224236
.json({ error: "Another timetable with this title already exists" });
225237
}
226-
227238
let updateData: any = {};
228239
if (timetable_title) updateData.timetable_title = timetable_title;
229240
if (semester) updateData.semester = semester;
@@ -240,13 +251,11 @@ export default {
240251
.eq("id", id)
241252
.select()
242253
.single();
243-
244254
const { data: timetableData, error: timetableError } =
245255
await updateTimetableQuery;
246256

247257
if (timetableError)
248258
return res.status(400).json({ error: timetableError.message });
249-
250259
// If no records were updated due to non-existence timetable or it doesn't belong to the user.
251260
if (!timetableData || timetableData.length === 0) {
252261
return res.status(404).json({
@@ -255,6 +264,7 @@ export default {
255264
}
256265
return res.status(200).json(timetableData);
257266
} catch (error) {
267+
console.error(error);
258268
return res.status(500).send({ error });
259269
}
260270
}),

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import TimetableCard from "./TimetableCard";
1111
import TimetableCreateNewButton from "./TimetableCreateNewButton";
1212
import { useGetTimetablesQuery } from "../../api/timetableApiSlice";
1313
import { TimetableCompareButton } from "./TimetableCompareButton";
14-
import { useState } from "react";
14+
import { useState, useEffect } from "react";
1515
import TimetableErrorDialog from "../TimetableBuilder/TimetableErrorDialog";
1616
import { useGetTimetablesSharedWithMeQuery } from "@/api/sharedApiSlice";
1717
import ViewCalendar from "../TimetableBuilder/ViewCalendar";
@@ -76,6 +76,11 @@ const Home = () => {
7676
.sort(sortTimetablesComparator);
7777

7878
const [errorMessage, setErrorMessage] = useState<string | null>(null);
79+
const [count, setCount] = useState<number>(0);
80+
81+
useEffect(() => {
82+
if (myTimetablesData !== undefined) setCount(myTimetablesData.length);
83+
}, [myTimetablesData]);
7984
const [activeTab, setActiveTab] = useState("Mine");
8085

8186
const [selectedSharedTimetable, setSelectedSharedTimetable] =
@@ -110,6 +115,17 @@ const Home = () => {
110115
<div className="mb-4 flex items-center gap-2 relative group">
111116
<h1 className="text-2xl font-medium tracking-tight">My Timetables</h1>
112117
<Pin size={24} className="text-blue-500" />
118+
119+
<h1
120+
className={`${
121+
count >= 25
122+
? "text-sm font-bold text-red-500"
123+
: "text-sm font-normal text-black"
124+
}`}
125+
>
126+
{" "}
127+
(No. Timetables: {count}/25)
128+
</h1>
113129
</div>
114130
<TimetableErrorDialog
115131
errorMessage={errorMessage}

0 commit comments

Comments
 (0)