mirror of
https://github.com/renbaoshuo/S2OJ.git
synced 2024-11-22 18:08:42 +00:00
feat(remote_judger/loj): judge details
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
72242edbfc
commit
cea0e82db8
@ -5,6 +5,7 @@ import sleep from '../utils/sleep';
|
|||||||
import { IBasicProvider, RemoteAccount, USER_AGENT } from '../interface';
|
import { IBasicProvider, RemoteAccount, USER_AGENT } from '../interface';
|
||||||
import Logger from '../utils/logger';
|
import Logger from '../utils/logger';
|
||||||
import { normalize, VERDICT } from '../verdict';
|
import { normalize, VERDICT } from '../verdict';
|
||||||
|
import { crlf, LF } from 'crlf-normalize';
|
||||||
|
|
||||||
proxy(superagent);
|
proxy(superagent);
|
||||||
const logger = new Logger('remote/loj');
|
const logger = new Logger('remote/loj');
|
||||||
@ -303,12 +304,149 @@ export default class LibreojProvider implements IBasicProvider {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const parse = (str: string) => crlf(str, LF);
|
||||||
|
|
||||||
|
const getSubtaskStatusDisplayText = (testcases: any): string => {
|
||||||
|
let result: string = null;
|
||||||
|
|
||||||
|
for (const testcase of testcases) {
|
||||||
|
if (!testcase.testcaseHash) {
|
||||||
|
result = 'Skipped';
|
||||||
|
|
||||||
|
break;
|
||||||
|
} else if (
|
||||||
|
body.progress.testcaseResult[testcase.testcaseHash].status !==
|
||||||
|
'Accepted'
|
||||||
|
) {
|
||||||
|
result = body.progress.testcaseResult[testcase.testcaseHash].status;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result ||= 'Accepted';
|
||||||
|
result =
|
||||||
|
VERDICT[
|
||||||
|
Object.keys(VERDICT).find(k => normalize(result).includes(k))
|
||||||
|
];
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTestcaseBlock = (id: string, index: number): string => {
|
||||||
|
const testcase = body.progress.testcaseResult[id];
|
||||||
|
|
||||||
|
if (!testcase) return '';
|
||||||
|
|
||||||
|
const status =
|
||||||
|
VERDICT[
|
||||||
|
Object.keys(VERDICT).find(k =>
|
||||||
|
normalize(testcase.status).includes(k)
|
||||||
|
)
|
||||||
|
];
|
||||||
|
let test_info = '';
|
||||||
|
|
||||||
|
test_info += `<test num="${
|
||||||
|
index + 1
|
||||||
|
}" info="${status}" time="${Math.round(testcase.time || 0)}" memory="${
|
||||||
|
testcase.memory
|
||||||
|
}">`;
|
||||||
|
|
||||||
|
if (testcase.input) {
|
||||||
|
if (typeof testcase.input === 'string') {
|
||||||
|
test_info += `<in>${parse(testcase.input)}</in>\n`;
|
||||||
|
} else {
|
||||||
|
test_info += `<in>${parse(testcase.input.data)}\n\n(${
|
||||||
|
testcase.input.omittedLength
|
||||||
|
} bytes omitted)</in>\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testcase.userOutput) {
|
||||||
|
if (typeof testcase.userOutput === 'string') {
|
||||||
|
test_info += `<out>${parse(testcase.userOutput)}</out>\n`;
|
||||||
|
} else {
|
||||||
|
test_info += `<out>${parse(testcase.userOutput.data)}\n\n(${
|
||||||
|
testcase.userOutput.omittedLength
|
||||||
|
} bytes omitted)</out>\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testcase.output) {
|
||||||
|
if (typeof testcase.output === 'string') {
|
||||||
|
test_info += `<ans>${parse(testcase.output)}</ans>\n`;
|
||||||
|
} else {
|
||||||
|
test_info += `<ans>${parse(testcase.output.data)}\n\n(${
|
||||||
|
testcase.output.omittedLength
|
||||||
|
} bytes omitted)</ans>\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testcase.checkerMessage) {
|
||||||
|
if (typeof testcase.checkerMessage === 'string') {
|
||||||
|
test_info += `<res>${parse(testcase.checkerMessage)}</res>\n`;
|
||||||
|
} else {
|
||||||
|
test_info += `<res>${parse(testcase.checkerMessage.data)}\n\n(${
|
||||||
|
testcase.checkerMessage.omittedLength
|
||||||
|
} bytes omitted)</res>\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test_info += '</test>';
|
||||||
|
|
||||||
|
return test_info;
|
||||||
|
};
|
||||||
|
|
||||||
|
let details = '';
|
||||||
|
|
||||||
|
details += `<info-block>REMOTE_SUBMISSION_ID = ${id}\nVERDICT = ${status}</info-block>`;
|
||||||
|
|
||||||
|
// Samples
|
||||||
|
if (body.progress.samples) {
|
||||||
|
details += `<subtask title="Samples" info="${getSubtaskStatusDisplayText(
|
||||||
|
body.progress.samples
|
||||||
|
)}" num="0">${body.progress.samples
|
||||||
|
.map((item, index) =>
|
||||||
|
item.testcaseHash
|
||||||
|
? getTestcaseBlock(item.testcaseHash, index)
|
||||||
|
: `<test num="${index + 1}" info="Skipped"></test>`
|
||||||
|
)
|
||||||
|
.join('\n')}</subtask>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests
|
||||||
|
if (body.progress.subtasks.length === 1) {
|
||||||
|
details += `<tests>${body.progress.subtasks[0].testcases
|
||||||
|
.map((item, index) =>
|
||||||
|
item.testcaseHash
|
||||||
|
? getTestcaseBlock(item.testcaseHash, index)
|
||||||
|
: `<test num="${index + 1}" info="Skipped"></test>`
|
||||||
|
)
|
||||||
|
.join('\n')}</tests>`;
|
||||||
|
} else {
|
||||||
|
details += `<tests>${body.progress.subtasks
|
||||||
|
.map(
|
||||||
|
(subtask, index) =>
|
||||||
|
`<subtask num="${index + 1}" info="${getSubtaskStatusDisplayText(
|
||||||
|
subtask.testcases
|
||||||
|
)}">${subtask.testcases
|
||||||
|
.map((item, index) =>
|
||||||
|
item.testcaseHash
|
||||||
|
? getTestcaseBlock(item.testcaseHash, index)
|
||||||
|
: `<test num="${index + 1}" info="Skipped"></test>`
|
||||||
|
)
|
||||||
|
.join('\n')}</subtask>`
|
||||||
|
)
|
||||||
|
.join('\n')}</tests>`;
|
||||||
|
}
|
||||||
|
|
||||||
return await end({
|
return await end({
|
||||||
id,
|
id,
|
||||||
status: body.meta.status,
|
status: body.meta.status,
|
||||||
score: body.meta.score,
|
score: body.meta.score,
|
||||||
time: body.meta.timeUsed,
|
time: body.meta.timeUsed,
|
||||||
memory: body.meta.memoryUsed,
|
memory: body.meta.memoryUsed,
|
||||||
|
details: `<div>${details}</div>`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -465,6 +465,7 @@ class JudgmentDetailsPrinter {
|
|||||||
echo '</div>';
|
echo '</div>';
|
||||||
} elseif ($node->nodeName == 'subtask') {
|
} elseif ($node->nodeName == 'subtask') {
|
||||||
$subtask_info = $node->getAttribute('info');
|
$subtask_info = $node->getAttribute('info');
|
||||||
|
$subtask_title = $node->getAttribute('title');
|
||||||
$subtask_num = $node->getAttribute('num');
|
$subtask_num = $node->getAttribute('num');
|
||||||
$subtask_score = $node->getAttribute('score');
|
$subtask_score = $node->getAttribute('score');
|
||||||
$subtask_time = $this->_get_attr($node, 'time', -1);
|
$subtask_time = $this->_get_attr($node, 'time', -1);
|
||||||
@ -482,7 +483,11 @@ class JudgmentDetailsPrinter {
|
|||||||
|
|
||||||
echo '<div class="row">';
|
echo '<div class="row">';
|
||||||
echo '<div class="col-sm-4">';
|
echo '<div class="col-sm-4">';
|
||||||
echo '<h3 class="fs-5">', 'Subtask #', $subtask_num, ': ', '</h3>';
|
if ($subtask_title !== '') {
|
||||||
|
echo '<h3 class="fs-5">', $subtask_title, ': ', '</h3>';
|
||||||
|
} else {
|
||||||
|
echo '<h3 class="fs-5">', 'Subtask #', $subtask_num, ': ', '</h3>';
|
||||||
|
}
|
||||||
echo '</div>';
|
echo '</div>';
|
||||||
|
|
||||||
if ($this->styler->show_score && $subtask_score !== '') {
|
if ($this->styler->show_score && $subtask_score !== '') {
|
||||||
@ -494,7 +499,7 @@ class JudgmentDetailsPrinter {
|
|||||||
echo htmlspecialchars($subtask_info);
|
echo htmlspecialchars($subtask_info);
|
||||||
echo '</div>';
|
echo '</div>';
|
||||||
} else {
|
} else {
|
||||||
echo '<div class="col-sm-4">';
|
echo '<div class="col-sm-4 uoj-status-text">';
|
||||||
echo $this->styler->getTestInfoIcon($subtask_info);
|
echo $this->styler->getTestInfoIcon($subtask_info);
|
||||||
echo htmlspecialchars($subtask_info);
|
echo htmlspecialchars($subtask_info);
|
||||||
echo '</div>';
|
echo '</div>';
|
||||||
@ -541,6 +546,9 @@ class JudgmentDetailsPrinter {
|
|||||||
$accordion_parent .= "_collapse_subtask_{$this->subtask_num}_accordion";
|
$accordion_parent .= "_collapse_subtask_{$this->subtask_num}_accordion";
|
||||||
}
|
}
|
||||||
$accordion_collapse = "{$accordion_parent}_collapse_test_{$test_num}";
|
$accordion_collapse = "{$accordion_parent}_collapse_test_{$test_num}";
|
||||||
|
if ($this->subtask_num != null) {
|
||||||
|
$accordion_collapse .= "_in_subtask_{$this->subtask_num}";
|
||||||
|
}
|
||||||
if (!$this->styler->shouldFadeDetails($test_info)) {
|
if (!$this->styler->shouldFadeDetails($test_info)) {
|
||||||
echo '<div class="card-header uoj-submission-result-item bg-transparent rounded-0 border-0" data-bs-toggle="collapse" data-bs-parent="#', $accordion_parent, '" data-bs-target="#', $accordion_collapse, '">';
|
echo '<div class="card-header uoj-submission-result-item bg-transparent rounded-0 border-0" data-bs-toggle="collapse" data-bs-parent="#', $accordion_parent, '" data-bs-target="#', $accordion_collapse, '">';
|
||||||
} else {
|
} else {
|
||||||
@ -762,7 +770,7 @@ class SubmissionDetailsStyler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function shouldFadeDetails($info) {
|
public function shouldFadeDetails($info) {
|
||||||
return $this->fade_all_details || $info == 'Extra Test Passed';
|
return $this->fade_all_details || $info == 'Extra Test Passed' || $info == 'Skipped';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class CustomTestSubmissionDetailsStyler {
|
class CustomTestSubmissionDetailsStyler {
|
||||||
|
Loading…
Reference in New Issue
Block a user