mirror of
https://github.com/renbaoshuo/S2OJ.git
synced 2024-12-22 04:01:52 +00:00
feat(remote_judger): include source code in result
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
5fac2aa2ec
commit
8defcd448f
@ -7,7 +7,6 @@ import { IBasicProvider, RemoteAccount, USER_AGENT } from '../interface';
|
||||
import Logger from '../utils/logger';
|
||||
import { normalize, VERDICT } from '../verdict';
|
||||
import htmlspecialchars from '../utils/htmlspecialchars';
|
||||
import { stripHtml } from 'string-strip-html';
|
||||
|
||||
proxy(superagent);
|
||||
const logger = new Logger('remote/loj');
|
||||
@ -131,11 +130,13 @@ const LANGS_MAP = {
|
||||
},
|
||||
};
|
||||
|
||||
const parse = (str: string) => crlf(str, LF);
|
||||
|
||||
export function getAccountInfoFromEnv(): RemoteAccount | null {
|
||||
const {
|
||||
LOJ_HANDLE,
|
||||
LOJ_TOKEN,
|
||||
LOJ_ENDPOINT = 'https://api.loj.ac.cn/api',
|
||||
LOJ_ENDPOINT = 'https://api.loj.ac/api',
|
||||
LOJ_PROXY,
|
||||
} = process.env;
|
||||
|
||||
@ -155,7 +156,7 @@ export function getAccountInfoFromEnv(): RemoteAccount | null {
|
||||
|
||||
export default class LibreojProvider implements IBasicProvider {
|
||||
constructor(public account: RemoteAccount) {
|
||||
this.account.endpoint ||= 'https://api.loj.ac.cn/api';
|
||||
this.account.endpoint ||= 'https://api.loj.ac/api';
|
||||
}
|
||||
|
||||
static constructFromAccountData(data) {
|
||||
@ -316,17 +317,29 @@ export default class LibreojProvider implements IBasicProvider {
|
||||
.send({ submissionId: String(id), locale: 'zh_CN' })
|
||||
.retry(3);
|
||||
|
||||
let files = [];
|
||||
|
||||
if (body.content?.code) {
|
||||
files.push({
|
||||
name: 'answer.code',
|
||||
content: parse(body.content.code),
|
||||
});
|
||||
}
|
||||
|
||||
if (body.meta.problem.displayId != problem_id) {
|
||||
return await end({
|
||||
id,
|
||||
error: true,
|
||||
status: 'Judgment Failed',
|
||||
message: 'Submission does not match current problem.',
|
||||
result: { files },
|
||||
});
|
||||
}
|
||||
|
||||
if (!body.progress) {
|
||||
await next({ status: 'Waiting for Remote Judge' });
|
||||
await next({
|
||||
status: 'Waiting for Remote Judge',
|
||||
});
|
||||
|
||||
continue;
|
||||
}
|
||||
@ -352,6 +365,7 @@ export default class LibreojProvider implements IBasicProvider {
|
||||
id,
|
||||
status: 'Compile Error',
|
||||
message: stripVTControlCharacters(body.progress.compile.message),
|
||||
result: { files },
|
||||
});
|
||||
}
|
||||
|
||||
@ -361,11 +375,10 @@ export default class LibreojProvider implements IBasicProvider {
|
||||
id,
|
||||
status: 'Judgment Failed',
|
||||
message: 'Error occurred on remote online judge.',
|
||||
result: { files },
|
||||
});
|
||||
}
|
||||
|
||||
const parse = (str: string) => crlf(str, LF);
|
||||
|
||||
const getSubtaskStatusDisplayText = (testcases: any): string => {
|
||||
let result: string = null;
|
||||
|
||||
@ -479,13 +492,6 @@ export default class LibreojProvider implements IBasicProvider {
|
||||
'</remote-result-table>' +
|
||||
'</remote-result-container>';
|
||||
|
||||
if (result_show_source) {
|
||||
details +=
|
||||
`<remote-source-code language="${body.content.language}">` +
|
||||
htmlspecialchars(parse(body.content.code)) +
|
||||
'</remote-source-code>';
|
||||
}
|
||||
|
||||
// Samples
|
||||
if (body.progress.samples) {
|
||||
details += `<subtask title="Samples" info="${getSubtaskStatusDisplayText(
|
||||
@ -534,6 +540,7 @@ export default class LibreojProvider implements IBasicProvider {
|
||||
time: body.meta.timeUsed,
|
||||
memory: body.meta.memoryUsed,
|
||||
details: `<div>${details}</div>`,
|
||||
result: { files },
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
|
@ -73,7 +73,7 @@ const LANGS_MAP = {
|
||||
name: 'Pascal',
|
||||
comment: '//',
|
||||
},
|
||||
};
|
||||
} as const;
|
||||
|
||||
const API_LANGS_MAP = {
|
||||
C: {
|
||||
@ -385,7 +385,10 @@ export default class LuoguProvider implements IBasicProvider {
|
||||
count++;
|
||||
|
||||
try {
|
||||
let result, data, body;
|
||||
let result,
|
||||
data,
|
||||
body,
|
||||
files = [];
|
||||
|
||||
if (this.account.type == 'luogu-api') {
|
||||
result = await this.get(`/judge/result?id=${id}`);
|
||||
@ -395,6 +398,17 @@ export default class LuoguProvider implements IBasicProvider {
|
||||
result = await this.safeGet(`/record/${id}?_contentOnly=1`);
|
||||
body = result.body;
|
||||
data = body.currentData.record;
|
||||
|
||||
if (data?.sourceCode) {
|
||||
files.push({
|
||||
name: 'answer.code',
|
||||
content: data.sourceCode,
|
||||
lang:
|
||||
Object.keys(LANGS_MAP).find(
|
||||
lang => LANGS_MAP[lang].id == data.language
|
||||
) || '/',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (result.status == 204) {
|
||||
@ -409,6 +423,7 @@ export default class LuoguProvider implements IBasicProvider {
|
||||
id,
|
||||
status: 'Judgment Failed',
|
||||
message: 'Failed to fetch submission details.',
|
||||
result: { files },
|
||||
});
|
||||
}
|
||||
|
||||
@ -421,6 +436,7 @@ export default class LuoguProvider implements IBasicProvider {
|
||||
error: true,
|
||||
status: 'Judgment Failed',
|
||||
message: 'Submission does not match current problem.',
|
||||
result: { files },
|
||||
});
|
||||
}
|
||||
|
||||
@ -453,6 +469,7 @@ export default class LuoguProvider implements IBasicProvider {
|
||||
id,
|
||||
status: 'Compile Error',
|
||||
message: data.detail.compileResult.message,
|
||||
result: { files },
|
||||
});
|
||||
}
|
||||
|
||||
@ -558,6 +575,7 @@ export default class LuoguProvider implements IBasicProvider {
|
||||
this.account.type != 'luogu-api'
|
||||
? `<div>${details}</div>`
|
||||
: details,
|
||||
result: { files },
|
||||
});
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
@ -568,7 +586,7 @@ export default class LuoguProvider implements IBasicProvider {
|
||||
|
||||
return await end({
|
||||
error: true,
|
||||
id: `R${id}`,
|
||||
id,
|
||||
status: 'Judgment Failed',
|
||||
message: 'Failed to fetch submission details.',
|
||||
});
|
||||
|
@ -122,6 +122,7 @@ class VJudge {
|
||||
`<info-block>ID = ${payload.id || 'None'}</info-block>` +
|
||||
`<error>${htmlspecialchars(payload.message)}</error>` +
|
||||
'</div>',
|
||||
...(payload.result || {}),
|
||||
}),
|
||||
judge_time,
|
||||
});
|
||||
@ -143,6 +144,7 @@ class VJudge {
|
||||
payload.id || 'None'
|
||||
}\nVERDICT = ${payload.status}</info-block>` +
|
||||
'</div>',
|
||||
...(payload.result || {}),
|
||||
}),
|
||||
judge_time,
|
||||
});
|
||||
|
@ -391,7 +391,7 @@ class UOJRemoteProblem {
|
||||
$curl->setHeader('Content-Type', 'application/json');
|
||||
|
||||
$res = retry_loop(function () use (&$curl, $id) {
|
||||
$curl->post('https://api.loj.ac.cn/api/problem/getProblem', json_encode([
|
||||
$curl->post('https://api.loj.ac/api/problem/getProblem', json_encode([
|
||||
'displayId' => (int)$id,
|
||||
'localizedContentsOfLocale' => 'zh_CN',
|
||||
'samples' => true,
|
||||
|
@ -172,6 +172,13 @@ class UOJSubmission {
|
||||
}
|
||||
}
|
||||
|
||||
$post_result = json_decode($post_result, true);
|
||||
|
||||
if (isset($post_result['files'])) {
|
||||
$files = $post_result['files'];
|
||||
unset($post_result['files']);
|
||||
}
|
||||
|
||||
$set_q = [
|
||||
'status' => 'Judged',
|
||||
'status_details' => '',
|
||||
@ -179,14 +186,15 @@ class UOJSubmission {
|
||||
|
||||
if ($submission->info['status'] == 'Judged, Judging') { // for UOJ-OI
|
||||
$result = json_decode($submission->info['result'], true);
|
||||
$result['final_result'] = json_decode($post_result, true);
|
||||
$result['final_result'] = $post_result;
|
||||
$result['final_result']['details'] = uojTextEncode($result['final_result']['details']);
|
||||
|
||||
$set_q += [
|
||||
'result' => json_encode($result, JSON_UNESCAPED_UNICODE),
|
||||
];
|
||||
} elseif ($submission->info['status'] == 'Judging') {
|
||||
$result = json_decode($post_result, true);
|
||||
$result = $post_result;
|
||||
|
||||
if (isset($result['details'])) {
|
||||
$result['details'] = uojTextEncode($result['details']);
|
||||
} else {
|
||||
@ -239,6 +247,30 @@ class UOJSubmission {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($files)) {
|
||||
$zip_file = new ZipArchive();
|
||||
$zip_file->open(UOJContext::storagePath() . $submission->getContent('file_name'), ZipArchive::CREATE);
|
||||
$tot_size = 0;
|
||||
$language = '/';
|
||||
|
||||
foreach ($files as $file) {
|
||||
$zip_file->addFromString($file['name'], $file['content']);
|
||||
|
||||
if (isset($file['lang'])) {
|
||||
$language = UOJLang::getUpgradedLangCode($file['lang']);
|
||||
}
|
||||
|
||||
$tot_size += $zip_file->statName($file['name'])['size'];
|
||||
}
|
||||
|
||||
$set_q += [
|
||||
'tot_size' => $tot_size,
|
||||
'language' => $language,
|
||||
];
|
||||
|
||||
$zip_file->close();
|
||||
}
|
||||
|
||||
if ($submission->isLatest()) {
|
||||
DB::update([
|
||||
"update submissions",
|
||||
@ -359,7 +391,7 @@ class UOJSubmission {
|
||||
])
|
||||
])
|
||||
], DB::for_update()
|
||||
]);
|
||||
]);
|
||||
DB::update(["update submissions", "set", $cfg['set_q'], "where", [$cond]]);
|
||||
});
|
||||
} else {
|
||||
|
@ -158,23 +158,6 @@ trait UOJSubmissionLikeTrait {
|
||||
$card_footer_class = 'text-end mt-2';
|
||||
}
|
||||
|
||||
if ($content['remote_submission_id']) {
|
||||
echo <<<EOD
|
||||
<div class="{$card_class}">
|
||||
<div class="{$card_header_class}">
|
||||
远程提交
|
||||
</div>
|
||||
<div class="{$card_body_class}">
|
||||
远程提交 ID: {$content['remote_submission_id']}
|
||||
<br>
|
||||
源代码请在「详细信息」选项卡查看。
|
||||
</div>
|
||||
</div>
|
||||
EOD;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$zip_file = new ZipArchive();
|
||||
if ($zip_file->open(UOJContext::storagePath() . $content['file_name'], ZipArchive::RDONLY) !== true) {
|
||||
echo <<<EOD
|
||||
|
Loading…
Reference in New Issue
Block a user