From e15683f65cd24792c7c0f646a886017974ed9492 Mon Sep 17 00:00:00 2001 From: patmik Date: Tue, 5 May 2026 13:44:11 +0200 Subject: [PATCH 1/4] feat: add course editions preview action --- src/actions/v2/get-course-editions-preview.ts | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 src/actions/v2/get-course-editions-preview.ts diff --git a/src/actions/v2/get-course-editions-preview.ts b/src/actions/v2/get-course-editions-preview.ts new file mode 100644 index 0000000..3d3124a --- /dev/null +++ b/src/actions/v2/get-course-editions-preview.ts @@ -0,0 +1,93 @@ +import redis from "@/lib/redis"; +import { getOrSetRedis } from "@/lib/redis/get-set"; +import { fetchUsosApi } from "@/lib/usos"; +import { createHash } from "crypto"; + +interface UsosCourseEdition { + start_time?: string | null; + end_time?: string | null; + name?: { pl?: string; en?: string } | null; + type?: string | null; + group_number?: string | null; + lecturer_ids?: string[] | null; + classtype_name?: { pl?: string; en?: string } | null; +} + +export interface CoursePreviewDTO { + id: string; + startTime: string; + endTime: string; + name: string; + type: string; + groupNumber: string; + lecturerIds: string[]; + classTypeName: string; +} + +interface BatchCoursePreviewParams { + courseEditionIds: string[]; + termId: string; + start: string; + days: number; +} + +export async function getBatchCoursePreviewAction( + params: BatchCoursePreviewParams, +): Promise> { + const { courseEditionIds, termId, start, days } = params; + + if (!courseEditionIds.length) return {}; + + const sortedIds = [...courseEditionIds].sort().join(","); + const hash = createHash("sha256") + .update(`${sortedIds}-${termId}-${start}-${days}`) + .digest("hex"); + + return getOrSetRedis({ + redis, + key: `usos:course_editions:${hash}`, + ttlSeconds: 60 * 60 * 12, + fetcher: async () => { + const response = await fetchUsosApi>( + "tt/course_editions", + { + course_edition_ids: courseEditionIds.join(","), + term_id: termId, + start: start, + days: days.toString(), + fields: + "start_time|end_time|name|type|group_number|lecturer_ids|classtype_name", + }, + ); + + if (!response) return {}; + + const normalizedData: Record = {}; + + for (const [editionId, events] of Object.entries(response)) { + if (!Array.isArray(events)) continue; + + const mapped = events.map((event) => ({ + id: editionId, + startTime: event.start_time ?? "", + endTime: event.end_time ?? "", + name: event.name?.pl ?? event.name?.en ?? "", + type: event.type ?? "", + groupNumber: event.group_number ?? "", + lecturerIds: event.lecturer_ids ?? [], + classTypeName: + event.classtype_name?.pl ?? event.classtype_name?.en ?? "", + })); + + mapped.sort( + (a, b) => + new Date(a.startTime).getTime() - new Date(b.startTime).getTime(), + ); + + normalizedData[editionId] = mapped; + } + + return normalizedData; + }, + }); +} From fa6c8a52288494b11d84e5810a749531c1439107 Mon Sep 17 00:00:00 2001 From: patmik Date: Tue, 5 May 2026 14:09:33 +0200 Subject: [PATCH 2/4] fix: add missing braces update imports, fix problem with type and comply with prettier --- src/actions/v2/get-course-editions-preview.ts | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/actions/v2/get-course-editions-preview.ts b/src/actions/v2/get-course-editions-preview.ts index 3d3124a..c9af030 100644 --- a/src/actions/v2/get-course-editions-preview.ts +++ b/src/actions/v2/get-course-editions-preview.ts @@ -1,7 +1,7 @@ import redis from "@/lib/redis"; import { getOrSetRedis } from "@/lib/redis/get-set"; import { fetchUsosApi } from "@/lib/usos"; -import { createHash } from "crypto"; +import { createHash } from "node:crypto"; interface UsosCourseEdition { start_time?: string | null; @@ -24,7 +24,7 @@ export interface CoursePreviewDTO { classTypeName: string; } -interface BatchCoursePreviewParams { +interface BatchCoursePreviewParameters { courseEditionIds: string[]; termId: string; start: string; @@ -32,15 +32,18 @@ interface BatchCoursePreviewParams { } export async function getBatchCoursePreviewAction( - params: BatchCoursePreviewParams, + parameters: BatchCoursePreviewParameters, ): Promise> { - const { courseEditionIds, termId, start, days } = params; + const { courseEditionIds, termId, start, days } = parameters; - if (!courseEditionIds.length) return {}; + if (courseEditionIds.length === 0) { + return {}; + } - const sortedIds = [...courseEditionIds].sort().join(","); + const sortedIds = [...courseEditionIds].toSorted().join(","); + const daysString = days.toString(); const hash = createHash("sha256") - .update(`${sortedIds}-${termId}-${start}-${days}`) + .update(`${sortedIds}-${termId}-${start}-${daysString}`) .digest("hex"); return getOrSetRedis({ @@ -53,19 +56,23 @@ export async function getBatchCoursePreviewAction( { course_edition_ids: courseEditionIds.join(","), term_id: termId, - start: start, - days: days.toString(), + start, + days: daysString, fields: "start_time|end_time|name|type|group_number|lecturer_ids|classtype_name", }, ); - if (!response) return {}; + if (response == null) { + return {}; + } const normalizedData: Record = {}; for (const [editionId, events] of Object.entries(response)) { - if (!Array.isArray(events)) continue; + if (!Array.isArray(events)) { + continue; + } const mapped = events.map((event) => ({ id: editionId, From 8757341c4bb4ae5bff9dff7d1ad1d09e617c4701 Mon Sep 17 00:00:00 2001 From: patmik Date: Tue, 5 May 2026 14:24:27 +0200 Subject: [PATCH 3/4] refractor: move normalization out of the getBatchCoursePreviewAction --- src/actions/v2/get-course-editions-preview.ts | 65 ++++++++++--------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/src/actions/v2/get-course-editions-preview.ts b/src/actions/v2/get-course-editions-preview.ts index c9af030..03ba32e 100644 --- a/src/actions/v2/get-course-editions-preview.ts +++ b/src/actions/v2/get-course-editions-preview.ts @@ -31,6 +31,38 @@ interface BatchCoursePreviewParameters { days: number; } +function normalizeCourseEditions( + response: Record, +): Record { + const normalizedData: Record = {}; + + for (const [editionId, events] of Object.entries(response)) { + if (!Array.isArray(events)) { + continue; + } + + const mapped = events.map((event) => ({ + id: editionId, + startTime: event.start_time ?? "", + endTime: event.end_time ?? "", + name: event.name?.pl ?? event.name?.en ?? "", + type: event.type ?? "", + groupNumber: event.group_number ?? "", + lecturerIds: event.lecturer_ids ?? [], + classTypeName: event.classtype_name?.pl ?? event.classtype_name?.en ?? "", + })); + + mapped.sort( + (a, b) => + new Date(a.startTime).getTime() - new Date(b.startTime).getTime(), + ); + + normalizedData[editionId] = mapped; + } + + return normalizedData; +} + export async function getBatchCoursePreviewAction( parameters: BatchCoursePreviewParameters, ): Promise> { @@ -63,38 +95,7 @@ export async function getBatchCoursePreviewAction( }, ); - if (response == null) { - return {}; - } - - const normalizedData: Record = {}; - - for (const [editionId, events] of Object.entries(response)) { - if (!Array.isArray(events)) { - continue; - } - - const mapped = events.map((event) => ({ - id: editionId, - startTime: event.start_time ?? "", - endTime: event.end_time ?? "", - name: event.name?.pl ?? event.name?.en ?? "", - type: event.type ?? "", - groupNumber: event.group_number ?? "", - lecturerIds: event.lecturer_ids ?? [], - classTypeName: - event.classtype_name?.pl ?? event.classtype_name?.en ?? "", - })); - - mapped.sort( - (a, b) => - new Date(a.startTime).getTime() - new Date(b.startTime).getTime(), - ); - - normalizedData[editionId] = mapped; - } - - return normalizedData; + return normalizeCourseEditions(response); }, }); } From 5b5c9899c51797be79d0d6782ec20a3ad9e76b26 Mon Sep 17 00:00:00 2001 From: patmik Date: Tue, 5 May 2026 17:20:11 +0200 Subject: [PATCH 4/4] fix: correct formating for right version of prettier --- src/actions/v2/get-course-editions-preview.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/actions/v2/get-course-editions-preview.ts b/src/actions/v2/get-course-editions-preview.ts index 03ba32e..b6d004e 100644 --- a/src/actions/v2/get-course-editions-preview.ts +++ b/src/actions/v2/get-course-editions-preview.ts @@ -1,7 +1,8 @@ +import { createHash } from "node:crypto"; + import redis from "@/lib/redis"; import { getOrSetRedis } from "@/lib/redis/get-set"; import { fetchUsosApi } from "@/lib/usos"; -import { createHash } from "node:crypto"; interface UsosCourseEdition { start_time?: string | null;