diff --git a/remote_judger/src/providers/loj.ts b/remote_judger/src/providers/loj.ts index c93a1ff..2c007fa 100644 --- a/remote_judger/src/providers/loj.ts +++ b/remote_judger/src/providers/loj.ts @@ -5,6 +5,7 @@ import sleep from '../utils/sleep'; import { IBasicProvider, RemoteAccount, USER_AGENT } from '../interface'; import Logger from '../utils/logger'; import { normalize, VERDICT } from '../verdict'; +import { crlf, LF } from 'crlf-normalize'; proxy(superagent); 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 += ``; + + if (testcase.input) { + if (typeof testcase.input === 'string') { + test_info += `${parse(testcase.input)}\n`; + } else { + test_info += `${parse(testcase.input.data)}\n\n(${ + testcase.input.omittedLength + } bytes omitted)\n`; + } + } + + if (testcase.userOutput) { + if (typeof testcase.userOutput === 'string') { + test_info += `${parse(testcase.userOutput)}\n`; + } else { + test_info += `${parse(testcase.userOutput.data)}\n\n(${ + testcase.userOutput.omittedLength + } bytes omitted)\n`; + } + } + + if (testcase.output) { + if (typeof testcase.output === 'string') { + test_info += `${parse(testcase.output)}\n`; + } else { + test_info += `${parse(testcase.output.data)}\n\n(${ + testcase.output.omittedLength + } bytes omitted)\n`; + } + } + + if (testcase.checkerMessage) { + if (typeof testcase.checkerMessage === 'string') { + test_info += `${parse(testcase.checkerMessage)}\n`; + } else { + test_info += `${parse(testcase.checkerMessage.data)}\n\n(${ + testcase.checkerMessage.omittedLength + } bytes omitted)\n`; + } + } + + test_info += ''; + + return test_info; + }; + + let details = ''; + + details += `REMOTE_SUBMISSION_ID = ${id}\nVERDICT = ${status}`; + + // Samples + if (body.progress.samples) { + details += `${body.progress.samples + .map((item, index) => + item.testcaseHash + ? getTestcaseBlock(item.testcaseHash, index) + : `` + ) + .join('\n')}`; + } + + // Tests + if (body.progress.subtasks.length === 1) { + details += `${body.progress.subtasks[0].testcases + .map((item, index) => + item.testcaseHash + ? getTestcaseBlock(item.testcaseHash, index) + : `` + ) + .join('\n')}`; + } else { + details += `${body.progress.subtasks + .map( + (subtask, index) => + `${subtask.testcases + .map((item, index) => + item.testcaseHash + ? getTestcaseBlock(item.testcaseHash, index) + : `` + ) + .join('\n')}` + ) + .join('\n')}`; + } + return await end({ id, status: body.meta.status, score: body.meta.score, time: body.meta.timeUsed, memory: body.meta.memoryUsed, + details: `
${details}
`, }); } } diff --git a/web/app/libs/uoj-html-lib.php b/web/app/libs/uoj-html-lib.php index 15bb957..a8224fa 100644 --- a/web/app/libs/uoj-html-lib.php +++ b/web/app/libs/uoj-html-lib.php @@ -465,6 +465,7 @@ class JudgmentDetailsPrinter { echo ''; } elseif ($node->nodeName == 'subtask') { $subtask_info = $node->getAttribute('info'); + $subtask_title = $node->getAttribute('title'); $subtask_num = $node->getAttribute('num'); $subtask_score = $node->getAttribute('score'); $subtask_time = $this->_get_attr($node, 'time', -1); @@ -482,7 +483,11 @@ class JudgmentDetailsPrinter { echo '
'; echo '
'; - echo '

', 'Subtask #', $subtask_num, ': ', '

'; + if ($subtask_title !== '') { + echo '

', $subtask_title, ': ', '

'; + } else { + echo '

', 'Subtask #', $subtask_num, ': ', '

'; + } echo '
'; if ($this->styler->show_score && $subtask_score !== '') { @@ -494,7 +499,7 @@ class JudgmentDetailsPrinter { echo htmlspecialchars($subtask_info); echo '
'; } else { - echo '
'; + echo '
'; echo $this->styler->getTestInfoIcon($subtask_info); echo htmlspecialchars($subtask_info); echo '
'; @@ -541,6 +546,9 @@ class JudgmentDetailsPrinter { $accordion_parent .= "_collapse_subtask_{$this->subtask_num}_accordion"; } $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)) { echo '
'; } else { @@ -762,7 +770,7 @@ class SubmissionDetailsStyler { } } 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 {