feat(remote_judger): include source code in result
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Baoshuo Ren 2023-03-26 09:38:54 +08:00
parent 5fac2aa2ec
commit 8defcd448f
Signed by: baoshuo
GPG Key ID: 00CB9680AB29F51A
6 changed files with 79 additions and 37 deletions

View File

@ -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);

View File

@ -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.',
});

View File

@ -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,
});

View File

@ -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,

View File

@ -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 {

View File

@ -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