Skip to content

Commit 7727daa

Browse files
authored
Merge pull request #703 from topcoder-platform/dev
SRM endpoints
2 parents fd7577c + 16683b5 commit 7727daa

File tree

8 files changed

+405
-0
lines changed

8 files changed

+405
-0
lines changed

.circleci/config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ workflows:
9191
only:
9292
- dev
9393
- hotfix/budget-update
94+
- CORE-140
9495

9596
- "build-qa":
9697
context: org-global

config/default.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,6 @@ module.exports = {
128128
INTERNAL_CACHE_TTL: process.env.INTERNAL_CACHE_TTL || 1800,
129129
GRPC_CHALLENGE_SERVER_HOST: process.env.GRPC_DOMAIN_CHALLENGE_SERVER_HOST || "localhost",
130130
GRPC_CHALLENGE_SERVER_PORT: process.env.GRPC_DOMAIN_CHALLENGE_SERVER_PORT || 8888,
131+
GRPC_ACL_SERVER_HOST: process.env.GRPC_ACL_SERVER_HOST || "localhost",
132+
GRPC_ACL_SERVER_PORT: process.env.GRPC_ACL_SERVER_PORT || 8889,
131133
};

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"@grpc/grpc-js": "^1.8.12",
4444
"@opensearch-project/opensearch": "^2.2.0",
4545
"@topcoder-framework/domain-challenge": "^0.24.0",
46+
"@topcoder-framework/domain-acl": "^0.24.0",
4647
"@topcoder-framework/lib-common": "^0.24.0",
4748
"aws-sdk": "^2.1145.0",
4849
"axios": "^0.19.0",

src/common/srm-helper.js

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
const _ = require("lodash");
2+
const moment = require("moment");
3+
4+
const SRMScheduleKeyMappings = _.reduce(
5+
[
6+
"roundId",
7+
"name",
8+
"shortName",
9+
"contestName",
10+
"roundType",
11+
"status",
12+
"registrationStartTime",
13+
"registrationEndTime",
14+
"codingStartTime",
15+
"codingEndTime",
16+
"intermissionStartTime",
17+
"intermissionEndTime",
18+
"challengeStartTime",
19+
"challengeEndTime",
20+
"systestStartTime",
21+
"systestEndTime",
22+
],
23+
(acc, field) => ({ ...acc, [_.toLower(field)]: field }),
24+
{}
25+
);
26+
27+
const PracticeProblemsKeyMappings = _.reduce(
28+
[
29+
"problemId",
30+
"componentId",
31+
"roomId",
32+
"roundId",
33+
"divisionId",
34+
"problemName",
35+
"problemType",
36+
"difficulty",
37+
"status",
38+
"points",
39+
"myPoints",
40+
],
41+
(acc, field) => ({ ...acc, [_.toLower(field)]: field }),
42+
{}
43+
);
44+
45+
/**
46+
* Get schedule query
47+
* @param {Object} filter the query filter
48+
* @param {Array<String>} filter.statuses the statues
49+
* @param {Date} filter.registrationStartTimeAfter the start of the registration time
50+
* @param {Date=} filter.registrationStartTimeBefore the end of the registration time
51+
* @param {String} filter.sortBy the sort field
52+
* @param {String} filter.sortOrder the sort order
53+
* @param {Number} filter.page the sort order
54+
* @param {Number} filter.perPage the sort order
55+
*/
56+
function getSRMScheduleQuery(filter) {
57+
const offset = (filter.page - 1) * filter.perPage;
58+
let sortBy = filter.sortBy;
59+
if (filter.sortBy === "registrationStartTime") {
60+
sortBy = "reg.start_time";
61+
} else if (filter.sortBy === "codingStartTime") {
62+
sortBy = "coding.start_time";
63+
} else if (filter.sortBy === "challengeStartTime") {
64+
sortBy = "challenge.start_time";
65+
}
66+
const statuses = _.join(
67+
_.map(filter.statuses, (s) => `'${_.toUpper(s)}'`),
68+
","
69+
);
70+
const registrationTimeFilter = `reg.start_time >= '${moment(
71+
filter.registrationStartTimeAfter
72+
).format("yyyy-MM-DD HH:mm:ss")}'${
73+
filter.registrationStartTimeBefore
74+
? ` AND reg.start_time <= '${moment(filter.registrationStartTimeBefore).format(
75+
"yyyy-MM-DD HH:mm:ss"
76+
)}'`
77+
: ""
78+
}`;
79+
80+
const query = `SELECT
81+
SKIP ${offset}
82+
FIRST ${filter.perPage}
83+
r.round_id AS roundId
84+
, r.name AS name
85+
, r.short_name AS shortName
86+
, c.name AS contestName
87+
, rt.round_type_desc AS roundType
88+
, r.status AS status
89+
, reg.start_time AS registrationStartTime
90+
, reg.end_time AS registrationEndTime
91+
, coding.start_time AS codingStartTime
92+
, coding.end_time AS codingEndTime
93+
, intermission.start_time AS intermissionStartTime
94+
, intermission.end_time AS intermissionEndTime
95+
, challenge.start_time AS challengeStartTime
96+
, challenge.end_time AS challengeEndTime
97+
, systest.start_time AS systestStartTime
98+
, systest.end_time AS systestEndTime
99+
FROM
100+
informixoltp:contest AS c
101+
INNER JOIN informixoltp:round AS r ON r.contest_id = c.contest_id
102+
INNER JOIN informixoltp:round_type_lu AS rt ON rt.round_type_id = r.round_type_id
103+
LEFT JOIN informixoltp:round_segment AS reg ON reg.round_id = r.round_id AND reg.segment_id = 1
104+
LEFT JOIN informixoltp:round_segment AS coding ON coding.round_id = r.round_id AND coding.segment_id = 2
105+
LEFT JOIN informixoltp:round_segment AS intermission ON intermission.round_id = r.round_id AND intermission.segment_id = 3
106+
LEFT JOIN informixoltp:round_segment AS challenge ON challenge.round_id = r.round_id AND challenge.segment_id = 4
107+
LEFT JOIN informixoltp:round_segment AS systest ON systest.round_id = r.round_id AND systest.segment_id = 5
108+
WHERE
109+
r.round_type_id in (1,2,10) AND
110+
UPPER(r.status) in (${statuses}) AND
111+
${registrationTimeFilter}
112+
ORDER BY ${sortBy} ${filter.sortOrder}`;
113+
return query;
114+
}
115+
116+
/**
117+
* Get schedule query
118+
* @param {Object} criteria the query criteria
119+
* @param {String} criteria.userId the user id
120+
* @param {String} criteria.sortBy the sort field
121+
* @param {String} criteria.sortOrder the sort order
122+
* @param {Number} criteria.page the sort order
123+
* @param {Number} criteria.perPage the sort order
124+
* @param {String=} criteria.difficulty the sort order
125+
* @param {String=} criteria.status the sort order
126+
* @param {Number=} criteria.pointsLowerBound the sort order
127+
* @param {Number=} criteria.pointsUpperBound the statues
128+
* @param {String=} criteria.problemName the start of the registration time
129+
*/
130+
function getPracticeProblemsQuery(criteria) {
131+
const offset = (criteria.page - 1) * criteria.perPage;
132+
let sortBy = criteria.sortBy;
133+
if (criteria.sortBy === "problemId") {
134+
sortBy = "p.problem_id";
135+
} else if (criteria.sortBy === "problemName") {
136+
sortBy = "p.name";
137+
} else if (criteria.sortBy === "problemType") {
138+
sortBy = "ptl.problem_type_desc";
139+
} else if (criteria.sortBy === "points") {
140+
sortBy = "rc.points";
141+
} else if (criteria.sortBy === "difficulty") {
142+
sortBy = "p.proposed_difficulty_id";
143+
} else if (criteria.sortBy === "status") {
144+
sortBy = "pcs.status_id";
145+
} else if (criteria.sortBy === "myPoints") {
146+
sortBy = "NVL(pcs.points, 0)";
147+
}
148+
const filters = [];
149+
if (criteria.difficulty) {
150+
if (criteria.difficulty === "easy") {
151+
filters.push(`p.proposed_difficulty_id=1`);
152+
} else if (criteria.difficulty === "medium") {
153+
filters.push(`p.proposed_difficulty_id=2`);
154+
} else if (criteria.difficulty === "hard") {
155+
filters.push(`p.proposed_difficulty_id=3`);
156+
}
157+
}
158+
if (criteria.status) {
159+
if (criteria.status === "new") {
160+
filters.push("NVL(pcs.status_id, 0) < 120");
161+
} else if (criteria.status === "viewed") {
162+
filters.push("pcs.status_id >= 120 AND pcs.status_id != 150");
163+
} else if (criteria.status === "solved") {
164+
filters.push("pcs.status_id = 150");
165+
}
166+
}
167+
if (criteria.pointsLowerBound) {
168+
filters.push(`rc.points >= ${criteria.pointsLowerBound}`);
169+
}
170+
if (criteria.pointsUpperBound) {
171+
filters.push(`rc.points <= ${criteria.pointsUpperBound}`);
172+
}
173+
if (criteria.problemName) {
174+
filters.push(
175+
`lower(p.name) like '%${_.toLower(_.replace(criteria.problemName, /[^a-z0-9]/gi, ""))}%'`
176+
);
177+
}
178+
179+
const queryCount = `SELECT count(*) AS count`;
180+
181+
const querySelect = `SELECT
182+
SKIP ${offset}
183+
FIRST ${criteria.perPage}
184+
p.problem_id AS problemId
185+
, c.component_id AS componentId
186+
, ro.room_id AS roomId
187+
, rc.round_id AS roundId
188+
, rc.division_id AS divisionId
189+
, p.name AS problemName
190+
, ptl.problem_type_desc AS problemType
191+
, CASE WHEN (p.problem_type_id = 1 AND p.proposed_difficulty_id = 1) THEN 'Easy'::nvarchar(50)
192+
WHEN (p.problem_type_id = 1 AND p.proposed_difficulty_id = 2) THEN 'Medium'::nvarchar(50)
193+
WHEN (p.problem_type_id = 1 AND p.proposed_difficulty_id = 3) THEN 'Hard'::nvarchar(50)
194+
END AS difficulty
195+
, rc.points AS points
196+
, CASE WHEN NVL(pcs.status_id, 0) < 120 THEN 'New'::nvarchar(50)
197+
WHEN pcs.status_id = 150 THEN 'Solved'::nvarchar(50)
198+
WHEN pcs.status_id >= 120 AND pcs.status_id != 150 THEN 'Viewed'::nvarchar(50)
199+
END AS status
200+
, NVL(pcs.points, 0) AS myPoints`;
201+
202+
const queryFrom = `FROM informixoltp:problem p
203+
INNER JOIN informixoltp:problem_type_lu ptl ON ptl.problem_type_id = p.problem_type_id
204+
INNER JOIN informixoltp:component c ON c.problem_id = p.problem_id
205+
INNER JOIN informixoltp:round_component rc ON rc.component_id = c.component_id
206+
INNER JOIN informixoltp:round r ON r.round_id = rc.round_id AND r.status = 'A' AND r.round_type_id = 3
207+
INNER JOIN informixoltp:room ro ON ro.round_id = rc.round_id AND ro.room_type_id = 3
208+
LEFT JOIN informixoltp:component_state pcs ON pcs.round_id = rc.round_id AND pcs.component_id = c.component_id AND pcs.coder_id = ${criteria.userId}`;
209+
210+
const queryWhere = filters.length ? `WHERE ${_.join(filters, " AND ")}` : "";
211+
212+
const queryOrder = `ORDER BY ${sortBy} ${criteria.sortOrder}`;
213+
214+
const query = `${querySelect} ${queryFrom} ${queryWhere} ${queryOrder}`;
215+
const countQuery = `${queryCount} ${queryFrom} ${queryWhere}`;
216+
return { query, countQuery };
217+
}
218+
219+
function convertSRMScheduleQueryOutput(queryOutput) {
220+
return transformDatabaseResponse(queryOutput, SRMScheduleKeyMappings);
221+
}
222+
223+
function convertPracticeProblemsQueryOutput(queryOutput) {
224+
return transformDatabaseResponse(queryOutput, PracticeProblemsKeyMappings);
225+
}
226+
227+
function transformDatabaseResponse(databaseResponse, keyMappings) {
228+
const transformedData = [];
229+
230+
if (databaseResponse && databaseResponse.rows && Array.isArray(databaseResponse.rows)) {
231+
databaseResponse.rows.forEach((row) => {
232+
const record = {};
233+
if (row.fields && Array.isArray(row.fields)) {
234+
row.fields.forEach((field) => {
235+
const lowercaseKey = field.key.toLowerCase();
236+
const mappedKey = keyMappings[lowercaseKey] || lowercaseKey;
237+
record[mappedKey] = field.value;
238+
});
239+
}
240+
transformedData.push(record);
241+
});
242+
}
243+
return transformedData;
244+
}
245+
246+
module.exports = {
247+
getSRMScheduleQuery,
248+
convertSRMScheduleQueryOutput,
249+
getPracticeProblemsQuery,
250+
convertPracticeProblemsQueryOutput,
251+
};

src/controllers/ChallengeController.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,27 @@ async function advancePhase(req, res) {
126126
res.send(await service.advancePhase(req, req.authUser, req.params.challengeId, req.body));
127127
}
128128

129+
/**
130+
* Get SRM Schedule
131+
* @param {Object} req the request
132+
* @param {Object} res the response
133+
*/
134+
async function getSRMSchedule(req, res) {
135+
const result = await service.getSRMSchedule(req, req.query);
136+
res.send(result);
137+
}
138+
139+
/**
140+
* Get Practice Problems
141+
* @param {Object} req the request
142+
* @param {Object} res the response
143+
*/
144+
async function getPracticeProblems(req, res) {
145+
const result = await service.getPracticeProblems(req, req.authUser, req.query);
146+
helper.setResHeaders(req, res, result);
147+
res.send(result.result);
148+
}
149+
129150
module.exports = {
130151
searchChallenges,
131152
createChallenge,
@@ -135,4 +156,6 @@ module.exports = {
135156
getChallengeStatistics,
136157
sendNotifications,
137158
advancePhase,
159+
getSRMSchedule,
160+
getPracticeProblems,
138161
};

src/routes.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,19 @@ module.exports = {
4040
method: "createRequest",
4141
},
4242
},
43+
"/challenges/srms/schedule": {
44+
get: {
45+
controller: "ChallengeController",
46+
method: "getSRMSchedule",
47+
},
48+
},
49+
"/challenges/srms/practice/problems": {
50+
get: {
51+
controller: "ChallengeController",
52+
method: "getPracticeProblems",
53+
auth: "jwt",
54+
},
55+
},
4356
"/challenges/health": {
4457
get: {
4558
controller: "HealthController",

0 commit comments

Comments
 (0)