11import { default as db } from '$lib/server/database' ;
2+
3+ import { getContestTaskPairs } from '$lib/services/contest_task_pairs' ;
4+
5+ import { ContestType } from '$lib/types/contest' ;
6+ import type { Task , TaskGrade } from '$lib/types/task' ;
7+ import type {
8+ ContestTaskPair ,
9+ ContestTaskPairKey ,
10+ TaskMapByContestTaskPair ,
11+ } from '$lib/types/contest_task_pair' ;
12+
213import { classifyContest } from '$lib/utils/contest' ;
3- import type { TaskGrade } from '$lib/types/task' ;
4- import type { Task , Tasks } from '$lib/types/task' ;
14+ import { createContestTaskPairKey } from '$lib/utils/contest_task_pair' ;
515
616// See:
717// https://www.prisma.io/docs/concepts/components/prisma-client/filtering-and-sorting
@@ -11,6 +21,77 @@ export async function getTasks(): Promise<Task[]> {
1121 return tasks ;
1222}
1323
24+ /**
25+ * Fetches and merges tasks based on contest-task pairs.
26+ *
27+ * @returns A promise that resolves to a map of merged tasks keyed by contest-task pair.
28+ *
29+ * @note This function merges tasks with the same task_id but different contest_id
30+ * from the contest-task pairs table. It enriches existing tasks with
31+ * contest-specific information (contest_type, task_table_index, etc.).
32+ * @note Time Complexity: O(N + M)
33+ * - N: number of tasks from the database
34+ * - M: number of contest-task pairs
35+ * - Map operations (has, get, set) are O(1)
36+ * @example
37+ * const mergedTasksMap = await getMergedTasksMap();
38+ * const task = mergedTasksMap.get(createContestTaskPairKey('tessoku-book', 'typical90_s'));
39+ */
40+ export async function getMergedTasksMap ( ) : Promise < TaskMapByContestTaskPair > {
41+ const tasks = await getTasks ( ) ;
42+ const contestTaskPairs = await getContestTaskPairs ( ) ;
43+
44+ const baseTaskMap = new Map < ContestTaskPairKey , Task > (
45+ tasks . map ( ( task ) => [ createContestTaskPairKey ( task . contest_id , task . task_id ) , task ] ) ,
46+ ) ;
47+ // Unique task_id in database
48+ const taskMap = new Map ( tasks . map ( ( task ) => [ task . task_id , task ] ) ) ;
49+
50+ // Filter task(s) only the same task_id but different contest_id
51+ const additionalTaskMap = contestTaskPairs
52+ . filter ( ( pair ) => ! baseTaskMap . has ( createContestTaskPairKey ( pair . contestId , pair . taskId ) ) )
53+ . flatMap ( ( pair ) => {
54+ const task = taskMap . get ( pair . taskId ) ;
55+ const contestType = classifyContest ( pair . contestId ) ;
56+
57+ if ( ! task || ! contestType || ! pair . taskTableIndex ) {
58+ return [ ] ;
59+ }
60+
61+ return [ createMergedTask ( task , pair , contestType ) ] ;
62+ } ) ;
63+
64+ return new Map ( [ ...baseTaskMap , ...additionalTaskMap ] ) ;
65+ }
66+
67+ /**
68+ * Creates a merged task from the original task and contest-task pair.
69+ *
70+ * @param task The original task to be enriched with contest-specific information.
71+ * @param pair The contest-task pair containing contestId, taskTableIndex and taskId.
72+ * @param contestType The type of contest (e.g., ABC, ARC) derived from contest_id.
73+ * @returns A tuple [key, mergedTask] where:
74+ * - key: the unique identifier for the contestId:taskId pair
75+ * - mergedTask: the task with contest-specific fields updated
76+ */
77+ function createMergedTask (
78+ task : Task ,
79+ pair : ContestTaskPair ,
80+ contestType : ContestType ,
81+ ) : [ ContestTaskPairKey , Task ] {
82+ const key = createContestTaskPairKey ( pair . contestId , pair . taskId ) ;
83+
84+ const mergedTask : Task = {
85+ ...task ,
86+ contest_type : contestType ,
87+ contest_id : pair . contestId ,
88+ task_table_index : pair . taskTableIndex ,
89+ title : task . title . replace ( task . task_table_index , pair . taskTableIndex ) ,
90+ } ;
91+
92+ return [ key , mergedTask ] ;
93+ }
94+
1495/**
1596 * Fetches tasks with the specified task IDs.
1697 * @param selectedTaskIds - An array of task IDs to filter the tasks.
0 commit comments