Skip to content

Commit d5eac38

Browse files
authored
feat: add course editions preview action (#356)
1 parent 12cff12 commit d5eac38

1 file changed

Lines changed: 102 additions & 0 deletions

File tree

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { createHash } from "node:crypto";
2+
3+
import redis from "@/lib/redis";
4+
import { getOrSetRedis } from "@/lib/redis/get-set";
5+
import { fetchUsosApi } from "@/lib/usos";
6+
7+
interface UsosCourseEdition {
8+
start_time?: string | null;
9+
end_time?: string | null;
10+
name?: { pl?: string; en?: string } | null;
11+
type?: string | null;
12+
group_number?: string | null;
13+
lecturer_ids?: string[] | null;
14+
classtype_name?: { pl?: string; en?: string } | null;
15+
}
16+
17+
export interface CoursePreviewDTO {
18+
id: string;
19+
startTime: string;
20+
endTime: string;
21+
name: string;
22+
type: string;
23+
groupNumber: string;
24+
lecturerIds: string[];
25+
classTypeName: string;
26+
}
27+
28+
interface BatchCoursePreviewParameters {
29+
courseEditionIds: string[];
30+
termId: string;
31+
start: string;
32+
days: number;
33+
}
34+
35+
function normalizeCourseEditions(
36+
response: Record<string, UsosCourseEdition[]>,
37+
): Record<string, CoursePreviewDTO[]> {
38+
const normalizedData: Record<string, CoursePreviewDTO[]> = {};
39+
40+
for (const [editionId, events] of Object.entries(response)) {
41+
if (!Array.isArray(events)) {
42+
continue;
43+
}
44+
45+
const mapped = events.map((event) => ({
46+
id: editionId,
47+
startTime: event.start_time ?? "",
48+
endTime: event.end_time ?? "",
49+
name: event.name?.pl ?? event.name?.en ?? "",
50+
type: event.type ?? "",
51+
groupNumber: event.group_number ?? "",
52+
lecturerIds: event.lecturer_ids ?? [],
53+
classTypeName: event.classtype_name?.pl ?? event.classtype_name?.en ?? "",
54+
}));
55+
56+
mapped.sort(
57+
(a, b) =>
58+
new Date(a.startTime).getTime() - new Date(b.startTime).getTime(),
59+
);
60+
61+
normalizedData[editionId] = mapped;
62+
}
63+
64+
return normalizedData;
65+
}
66+
67+
export async function getBatchCoursePreviewAction(
68+
parameters: BatchCoursePreviewParameters,
69+
): Promise<Record<string, CoursePreviewDTO[]>> {
70+
const { courseEditionIds, termId, start, days } = parameters;
71+
72+
if (courseEditionIds.length === 0) {
73+
return {};
74+
}
75+
76+
const sortedIds = [...courseEditionIds].toSorted().join(",");
77+
const daysString = days.toString();
78+
const hash = createHash("sha256")
79+
.update(`${sortedIds}-${termId}-${start}-${daysString}`)
80+
.digest("hex");
81+
82+
return getOrSetRedis({
83+
redis,
84+
key: `usos:course_editions:${hash}`,
85+
ttlSeconds: 60 * 60 * 12,
86+
fetcher: async () => {
87+
const response = await fetchUsosApi<Record<string, UsosCourseEdition[]>>(
88+
"tt/course_editions",
89+
{
90+
course_edition_ids: courseEditionIds.join(","),
91+
term_id: termId,
92+
start,
93+
days: daysString,
94+
fields:
95+
"start_time|end_time|name|type|group_number|lecturer_ids|classtype_name",
96+
},
97+
);
98+
99+
return normalizeCourseEditions(response);
100+
},
101+
});
102+
}

0 commit comments

Comments
 (0)