Skip to content

Commit f051646

Browse files
committed
Implement websocket based submit result
1 parent 2ecc8fc commit f051646

File tree

2 files changed

+130
-60
lines changed

2 files changed

+130
-60
lines changed

src/net/websocket.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import conf from '@/config';
22
import WebSocket from 'ws';
33

4-
export default function subscribeChannel(id: number, terminateFunc: (data: any) => boolean, doFunc: (data: any) => void) {
4+
let eventCnt = 0;
5+
6+
export default function subscribeChannel(id: number, terminateFunc: (data: any) => boolean, doFunc: (data: any) => void, termCheckAgainFunc?: (data: any) => boolean) {
57
return new Promise(resolve => {
68
const data = {
79
event: 'pusher:subscribe',
@@ -25,16 +27,27 @@ export default function subscribeChannel(id: number, terminateFunc: (data: any)
2527
socket.addEventListener('message', (event) => {
2628
const parsedObj = JSON.parse(event.data.toString());
2729
const data = JSON.parse(parsedObj.data);
28-
if(parsedObj.event === 'update') {
29-
if(terminateFunc(data)) {
30-
clearTimeout(timeout);
31-
socket.close();
32-
resolve(data);
33-
}
30+
if (parsedObj.event === 'update') {
31+
eventCnt++;
3432
doFunc(data);
33+
if (terminateFunc(data)) {
34+
if (termCheckAgainFunc && termCheckAgainFunc(data)) {
35+
const tmpEventCnt = eventCnt;
36+
setTimeout(() => {
37+
if (eventCnt !== tmpEventCnt) return;
38+
clearTimeout(timeout);
39+
socket.close();
40+
resolve(data);
41+
}, 5000);
42+
} else {
43+
clearTimeout(timeout);
44+
socket.close();
45+
resolve(data);
46+
}
47+
}
3548
}
36-
49+
3750
});
3851
})
39-
52+
4053
}

src/shell/commands/submit.ts

Lines changed: 108 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,102 @@ import fs from "fs/promises";
55
import conf from "@/config";
66
import subscribeChannel from "@/net/websocket";
77

8+
const result_names = ["기다리는 중", "재채점을 기다리는 중", "채점 준비 중", "채점 중", "맞았습니다!!", "출력 형식이 잘못되었습니다", "틀렸습니다", "시간 초과", "메모리 초과", "출력 초과", "런타임 에러", "컴파일 에러", "채점 불가", "삭제된 제출", "%(remain)초 후 채점 시작", "맞았습니다!!", "런타임 에러 이유를 찾는 중"];
9+
const result_colors = [chalk.gray, chalk.gray, chalk.yellow, chalk.yellow, chalk.green, chalk.red, chalk.red, chalk.red, chalk.red, chalk.red, chalk.blue, chalk.blue, chalk.bgBlack, chalk.bgBlack, chalk.yellow, chalk.greenBright, chalk.yellow];
10+
const result_progress: any = {};
11+
12+
// modified version of https://ddo7jzca0m2vt.cloudfront.net/js/status.js
13+
function display_solution(solution_id: number, ans: any) {
14+
const result = ans['result'];
15+
if (result == 11) ans['compile_error'] = true;
16+
if (result == 10) ans['runtime_error'] = true;
17+
let has_detail = false;
18+
if (result == 4) {
19+
if (ans['subtask_score']) {
20+
has_detail = true;
21+
}
22+
if (ans['ac'] && ans['tot'] && ans['ac'] > 0 && ans['tot'] > 0) {
23+
has_detail = true;
24+
}
25+
}
26+
ans['result_name'] = result_names[result];
27+
let progress = 0;
28+
if (ans['progress']) {
29+
progress = parseInt(ans['progress']);
30+
if (result_progress[solution_id] > progress) {
31+
return;
32+
}
33+
ans['result_name'] += " (" + progress + "%";
34+
35+
result_progress[solution_id] = progress;
36+
ans['result_name'] += ')';
37+
}
38+
if (result == 6 && ans['feedback']) {
39+
ans['result_name'] += ' [' + ans['feedback'] + ']';
40+
}
41+
if (result == 10 && ans['rte_reason']) {
42+
ans['result_name'] += ' (' + ans['rte_reason'] + ')';
43+
}
44+
if (result == 14) {
45+
let remain = ans['remain'] || 0;
46+
ans['result_name'] = ans['result_name'].replace("%(remain)", remain);
47+
}
48+
let r = "";
49+
if (ans['compile_error']) {
50+
r += '<a href="https://www.acmicpc.net/ceinfo/' + solution_id + '">';
51+
}
52+
if (ans['runtime_error']) {
53+
r += '<a href="https://help.acmicpc.net/judge/rte">';
54+
}
55+
if (has_detail) {
56+
r += '<a href="https://www.acmicpc.net/source/' + solution_id + '#points">';
57+
}
58+
if (ans['partial_score']) {
59+
let score = (Math.round(ans['partial_score'] * 100) / 100);
60+
r += score;
61+
r += '점';
62+
if (ans['custom_result']) {
63+
r += " (" + ans['custom_result'] + ")";
64+
}
65+
} else if (ans['subtask_score']) {
66+
let score = (ans['subtask_score']);
67+
r += score;
68+
r += '점';
69+
if (ans['custom_result']) {
70+
r += " (" + ans['custom_result'] + ")";
71+
}
72+
} else {
73+
if (ans['custom_result']) {
74+
r += ans['custom_result'];
75+
} else {
76+
r += ans['result_name'];
77+
if (ans['ac'] && ans['tot'] && ans['ac'] > 0 && ans['tot'] > 0) {
78+
r += ' (' + ans['ac'] + '/' + ans['tot'] + ')';
79+
}
80+
}
81+
}
82+
if (ans['compile_error']) r += '</a>';
83+
if (ans['runtime_error']) r += '</a>';
84+
if (has_detail) r += '</a>';
85+
86+
// unwrap r so that `<a href="url">text</a>` become `text (url)`
87+
r = r.replace(/<a href="([^"]+)">([^<]+)<\/a>/g, '$2 ($1)');
88+
89+
let out = ``;
90+
out += result_colors[result](r);
91+
92+
if('memory' in ans) out += ` | Memory: ${ans['memory']} KB`;
93+
if('time' in ans) out += ` | Time: ${ans['time']} ms`;
94+
if (result >= 4 && result_progress[solution_id]) {
95+
if(result > 4 && result <= 10) out += ` | 실패 지점: ${result_progress[solution_id]}%`;
96+
delete result_progress[solution_id];
97+
}
98+
99+
process.stdout.clearLine(0);
100+
process.stdout.cursorTo(0);
101+
process.stdout.write(out);
102+
}
103+
8104
export default function submit(that: BJShell, arg: string[]) {
9105
return async () => {
10106
const info = await checkInfo(that);
@@ -14,64 +110,25 @@ export default function submit(that: BJShell, arg: string[]) {
14110
try {
15111
console.log(`===== 제출: ${question!.qnum}. ${question!.title} =====`);
16112
const filepath = await getFilePath(that);
17-
if(!filepath) return;
113+
if (!filepath) return;
18114
const code = await fs.readFile(filepath, "utf-8");
19115
const subId = await that.user.submit(code);
20116
if (subId === -1) return;
21117
console.log(`문제를 제출했습니다!`);
22-
// const initResult = await that.user.submitStatus(subId);
23-
const finalResult = await subscribeChannel(subId, d => d?.result >= 4, (d) => {
24-
console.log(d)
25-
});
26-
console.log("final", finalResult)
27-
// const info =
28-
// finalResult.result === 4
29-
// ? `${chalk.green("ok")} | Time: ${
30-
// finalResult.time
31-
// } ms | Memory: ${finalResult.memory} KB`
32-
// : `${chalk.red(result.result_name)}`;
33-
// console.log(info);
34-
const username = await that.user.getUsername();
35-
const langcode = that.findLang()?.num;
118+
const ans = await that.user.submitStatus(subId);
119+
if (ans === null || isNaN(parseInt(ans.result))) {
120+
console.log(`제출번호 ${subId} 결과를 가져오는데 실패했습니다.`);
121+
return;
122+
}
123+
display_solution(subId, ans);
124+
const finalAns = await subscribeChannel(subId, d => d.result >= 4, d => display_solution(subId, d),
125+
d => d.result == 10 || d.result == 14 || d.result == 16);
126+
if (finalAns === null) console.log(`경고: 제출 결과를 가져오는데 시간이 너무 오래 걸립니다.`);
36127
console.log(
37-
`=> ${conf.URL}status?problem_id=${
128+
`\n=> ${conf.URL}status?problem_id=${
38129
question!.qnum
39-
}&user_id=${username}&language_id=${langcode}&result_id=-1`
130+
}&user_id=${await that.user.getUsername()}&language_id=${that.findLang()?.num}&result_id=-1\n`
40131
);
41-
42-
// for (let sec = 0; sec < 60; sec++) {
43-
// const result = await that.user.submitStatus(subId);
44-
// if (result === null) {
45-
// console.log(`제출번호 ${subId} 결과를 가져오는데 실패했습니다.`);
46-
// return;
47-
// }
48-
// const result_num = parseInt(result.result);
49-
// if (isNaN(result_num)) {
50-
// console.log(`제출번호 ${subId} 결과를 파싱하는데 실패했습니다.`);
51-
// return;
52-
// }
53-
// process.stdout.clearLine(0);
54-
// process.stdout.cursorTo(0);
55-
// if (result_num >= 4) {
56-
// const info =
57-
// result_num === 4
58-
// ? `${chalk.green(result.result_name)} | Time: ${
59-
// result.time
60-
// } ms | Memory: ${result.memory} KB`
61-
// : `${chalk.red(result.result_name)}`;
62-
// console.log(info);
63-
// const username = await that.user.getUsername();
64-
// const langcode = that.findLang()?.num;
65-
// console.log(
66-
// `=> ${conf.URL}status?problem_id=${
67-
// question!.qnum
68-
// }&user_id=${username}&language_id=${langcode}&result_id=-1`
69-
// );
70-
// break;
71-
// }
72-
// process.stdout.write(`${result.result_name} (${sec} s passed)`); // end the line
73-
// await new Promise((resolve) => setTimeout(resolve, 1000));
74-
// }
75132
} catch (e) {
76133
if (e instanceof Error) console.log(e.message);
77134
else console.log(e);

0 commit comments

Comments
 (0)