Skip to content

Commit e09bbf2

Browse files
committed
Merge branch 'main' of github.com:CS3219-AY2425S1/cs3219-ay2425s1-project-g16 into anun/chat-gen
2 parents 4808a9e + c1c1d6b commit e09bbf2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+7662
-2405
lines changed

.eslintrc.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@
2121
"simple-import-sort"
2222
],
2323
"rules": {
24+
"@typescript-eslint/array-type": [
25+
"error",
26+
{
27+
"default": "generic",
28+
"readonly": "generic"
29+
}
30+
],
2431
"@typescript-eslint/no-explicit-any": "warn",
2532
"@typescript-eslint/explicit-module-boundary-types": "off",
2633
"@typescript-eslint/explicit-function-return-type": 0,

backend/matching/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { logger } from '@/lib/utils';
55
import server, { io } from '@/server';
66
import { initWorker } from '@/workers';
77

8-
const workers: ChildProcess[] = [];
8+
const workers: Array<ChildProcess> = [];
99

1010
const port = Number.parseInt(EXPRESS_PORT || '8001');
1111

backend/matching/src/services/get-match-items.ts

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import type { IMatchItemsResponse, IMatchType } from '@/types';
33

44
import { createRoom } from './collab';
55
import { getRandomQuestion } from './question';
6-
import { fetchAttemptedQuestions } from './user';
76

87
export async function getMatchItems(
98
searchIdentifier: IMatchType,
@@ -17,26 +16,13 @@ export async function getMatchItems(
1716
throw new Error('Both user IDs are required');
1817
}
1918

20-
let allAttemptedQuestions: number[] = [];
21-
22-
try {
23-
const [attemptedQuestions1, attemptedQuestions2] = await Promise.all([
24-
fetchAttemptedQuestions(userId1),
25-
fetchAttemptedQuestions(userId2),
26-
]);
27-
allAttemptedQuestions = [...new Set([...attemptedQuestions1, ...attemptedQuestions2])];
28-
} catch (error) {
29-
logger.error('Error in getMatchItems: Failed to fetch attempted questions', error);
30-
}
31-
3219
const topics = topic?.split('|') ?? [];
3320
const payload = {
34-
attemptedQuestions: allAttemptedQuestions,
21+
userId1,
22+
userId2,
3523
...(searchIdentifier === 'difficulty' && difficulty ? { difficulty } : {}),
36-
...(searchIdentifier === 'topic' && topic ? { topic: topics } : {}),
37-
...(searchIdentifier === 'exact match' && topic && difficulty
38-
? { topic: topics, difficulty }
39-
: {}),
24+
...(searchIdentifier === 'topic' && topic ? { topics } : {}),
25+
...(searchIdentifier === 'exact match' && topic && difficulty ? { topics, difficulty } : {}),
4026
};
4127

4228
// Get a random question
@@ -55,7 +41,8 @@ export async function getMatchItems(
5541
questionId: question.id,
5642
};
5743
} catch (error) {
58-
logger.error('Error in getMatchItems:', error);
44+
const { name, message, stack, cause } = error as Error;
45+
logger.error(`Error in getMatchItems: ${JSON.stringify({ name, message, stack, cause })}`);
5946
return undefined;
6047
}
6148
}
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import type { IGetRandomQuestionPayload, IQuestion, IServiceResponse } from '@/types';
1+
import type { IGetRandomQuestionPayload, IQuestion } from '@/types';
22

33
import { questionServiceClient, routes } from './_hosts';
44

55
export async function getRandomQuestion(payload: IGetRandomQuestionPayload): Promise<IQuestion> {
6-
const response = await questionServiceClient.post<IServiceResponse<{ question: IQuestion }>>(
6+
const response = await questionServiceClient.post<IQuestion>(
77
routes.QUESTION_SERVICE.GET_RANDOM_QN.path,
88
payload
99
);
1010

11-
if (response.status !== 200 || !response.data.data) {
12-
throw new Error(response.data.error?.message || 'Failed to get a random question');
11+
if (response.status !== 200 || !response.data) {
12+
throw new Error(response.statusText || 'Failed to get a random question');
1313
}
1414

15-
return response?.data?.data?.question ?? undefined;
15+
return response?.data ?? undefined;
1616
}

backend/matching/src/services/user.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { routes, userServiceClient } from './_hosts';
22

3-
export async function fetchAttemptedQuestions(userId: string): Promise<number[]> {
4-
const response = await userServiceClient.post<number[]>(
3+
export async function fetchAttemptedQuestions(userId: string): Promise<Array<number>> {
4+
const response = await userServiceClient.post<Array<number>>(
55
routes.USER_SERVICE.ATTEMPTED_QNS.GET.path,
66
{
77
userId,

backend/matching/src/types/index.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export type IRequestMatchRESTPayload = {
1010
};
1111

1212
export type IRequestMatchWSPayload = {
13-
topic: string | string[];
13+
topic: string | Array<string>;
1414
difficulty: string;
1515
};
1616

@@ -61,9 +61,10 @@ export interface IQuestion {
6161
}
6262

6363
export interface IGetRandomQuestionPayload {
64-
attemptedQuestions: number[];
64+
userId1: string;
65+
userId2: string;
6566
difficulty?: string;
66-
topic?: Array<string>;
67+
topics?: Array<string>;
6768
}
6869

6970
export interface IMatchItemsResponse {

backend/matching/src/workers/matcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ async function processMatch(
7373
]);
7474

7575
// Notify both sockets
76-
const { ...matchItems } = await getMatchItems(
76+
const matchItems = await getMatchItems(
7777
searchIdentifier,
7878
topic,
7979
difficulty,
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CREATE TABLE IF NOT EXISTS "question_attempts" (
2+
"attempt_id" serial PRIMARY KEY NOT NULL,
3+
"question_id" integer NOT NULL,
4+
"user_id_1" uuid NOT NULL,
5+
"user_id_2" uuid,
6+
"code" text NOT NULL,
7+
"timestamp" timestamp (6) with time zone DEFAULT now(),
8+
"language" varchar(50) NOT NULL
9+
);
10+
--> statement-breakpoint
11+
CREATE UNIQUE INDEX IF NOT EXISTS "unique_users_attempt" ON "question_attempts" USING btree ("question_id","user_id_1","user_id_2");
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
{
2+
"id": "afa9ccaa-137c-47d3-acb0-ab1e2208038e",
3+
"prevId": "84b2ca8d-3021-496f-8769-bbc4dada6468",
4+
"version": "7",
5+
"dialect": "postgresql",
6+
"tables": {
7+
"public.admin": {
8+
"name": "admin",
9+
"schema": "",
10+
"columns": {
11+
"id": {
12+
"name": "id",
13+
"type": "uuid",
14+
"primaryKey": true,
15+
"notNull": true,
16+
"default": "gen_random_uuid()"
17+
},
18+
"created_at": {
19+
"name": "created_at",
20+
"type": "timestamp",
21+
"primaryKey": false,
22+
"notNull": false,
23+
"default": "now()"
24+
},
25+
"action": {
26+
"name": "action",
27+
"type": "action",
28+
"typeSchema": "public",
29+
"primaryKey": false,
30+
"notNull": true
31+
}
32+
},
33+
"indexes": {},
34+
"foreignKeys": {},
35+
"compositePrimaryKeys": {},
36+
"uniqueConstraints": {}
37+
},
38+
"public.question_attempts": {
39+
"name": "question_attempts",
40+
"schema": "",
41+
"columns": {
42+
"attempt_id": {
43+
"name": "attempt_id",
44+
"type": "serial",
45+
"primaryKey": true,
46+
"notNull": true
47+
},
48+
"question_id": {
49+
"name": "question_id",
50+
"type": "integer",
51+
"primaryKey": false,
52+
"notNull": true
53+
},
54+
"user_id_1": {
55+
"name": "user_id_1",
56+
"type": "uuid",
57+
"primaryKey": false,
58+
"notNull": true
59+
},
60+
"user_id_2": {
61+
"name": "user_id_2",
62+
"type": "uuid",
63+
"primaryKey": false,
64+
"notNull": false
65+
},
66+
"code": {
67+
"name": "code",
68+
"type": "text",
69+
"primaryKey": false,
70+
"notNull": true
71+
},
72+
"timestamp": {
73+
"name": "timestamp",
74+
"type": "timestamp (6) with time zone",
75+
"primaryKey": false,
76+
"notNull": false,
77+
"default": "now()"
78+
},
79+
"language": {
80+
"name": "language",
81+
"type": "varchar(50)",
82+
"primaryKey": false,
83+
"notNull": true
84+
}
85+
},
86+
"indexes": {
87+
"unique_users_attempt": {
88+
"name": "unique_users_attempt",
89+
"columns": [
90+
{
91+
"expression": "question_id",
92+
"isExpression": false,
93+
"asc": true,
94+
"nulls": "last"
95+
},
96+
{
97+
"expression": "user_id_1",
98+
"isExpression": false,
99+
"asc": true,
100+
"nulls": "last"
101+
},
102+
{
103+
"expression": "user_id_2",
104+
"isExpression": false,
105+
"asc": true,
106+
"nulls": "last"
107+
}
108+
],
109+
"isUnique": true,
110+
"concurrently": false,
111+
"method": "btree",
112+
"with": {}
113+
}
114+
},
115+
"foreignKeys": {},
116+
"compositePrimaryKeys": {},
117+
"uniqueConstraints": {}
118+
},
119+
"public.questions": {
120+
"name": "questions",
121+
"schema": "",
122+
"columns": {
123+
"id": {
124+
"name": "id",
125+
"type": "serial",
126+
"primaryKey": true,
127+
"notNull": true
128+
},
129+
"title": {
130+
"name": "title",
131+
"type": "varchar(255)",
132+
"primaryKey": false,
133+
"notNull": true
134+
},
135+
"difficulty": {
136+
"name": "difficulty",
137+
"type": "varchar(50)",
138+
"primaryKey": false,
139+
"notNull": true
140+
},
141+
"topic": {
142+
"name": "topic",
143+
"type": "varchar(255)[]",
144+
"primaryKey": false,
145+
"notNull": true
146+
},
147+
"description": {
148+
"name": "description",
149+
"type": "text",
150+
"primaryKey": false,
151+
"notNull": true
152+
},
153+
"created_at": {
154+
"name": "created_at",
155+
"type": "timestamp (6) with time zone",
156+
"primaryKey": false,
157+
"notNull": false,
158+
"default": "now()"
159+
},
160+
"updated_at": {
161+
"name": "updated_at",
162+
"type": "timestamp (6) with time zone",
163+
"primaryKey": false,
164+
"notNull": false,
165+
"default": "now()"
166+
}
167+
},
168+
"indexes": {},
169+
"foreignKeys": {},
170+
"compositePrimaryKeys": {},
171+
"uniqueConstraints": {}
172+
}
173+
},
174+
"enums": {
175+
"public.action": {
176+
"name": "action",
177+
"schema": "public",
178+
"values": [
179+
"SEED"
180+
]
181+
}
182+
},
183+
"schemas": {},
184+
"sequences": {},
185+
"_meta": {
186+
"columns": {},
187+
"schemas": {},
188+
"tables": {}
189+
}
190+
}

backend/question/drizzle/meta/_journal.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@
88
"when": 1728143550719,
99
"tag": "0000_initial_schema",
1010
"breakpoints": true
11+
},
12+
{
13+
"idx": 1,
14+
"version": "7",
15+
"when": 1730553826248,
16+
"tag": "0001_attempt_history",
17+
"breakpoints": true
1118
}
1219
]
1320
}

0 commit comments

Comments
 (0)