CodingSouls团队项目第二阶段冲刺(8)-个人概况
团队项目第二阶段冲刺第8天:
完善评测功能:
<% this.title = '提交记录 #' + info.submissionId %> <% include util %> <% include header %> <script src="<%- lib('textfit/2.3.1/textFit.min.js') %>"></script> <style> .single-subtask { box-shadow: none !important; } .single-subtask > .title { display: none; } .single-subtask > .content { padding: 0 !important; } .accordion > .content > .accordion { margin-top: 0; margin-bottom: 0; } .accordion > .content > .accordion > .content { margin-top: 0; margin-bottom: 14px; } .accordion > .content > .accordion > .content > :last-child { margin-bottom: -10px !important; } </style> <span id="submission_content"> <div class="padding" id="vueAppFuckSafari"> <table class="ui very basic center aligned table" id="status_table"> <thead> <tr> <th>编号</th> <th>题目</th> <th>状态</th> <th v-if="displayConfig.showScore">分数</th> <th v-if="displayConfig.showUsage">总时间</th> <th v-if="displayConfig.showUsage">内存</th> <th v-if="displayConfig.showCode">代码 / 答案文件</th> <th>提交者</th> <th>提交时间</th> <th v-if="showRejudge">重新评测</th> </tr> </thead> <tbody> <tr is="submission-item" v-bind:data="roughData" :config="displayConfig" :show-rejudge="showRejudge" :progress="getProgress()" :compiling="detailResult && !detailResult.compile"></tr> </tbody> </table> <code-box no-escape v-bind:content="code"> <% if (formattedCode !== null) { %> <a onclick="toggleFormattedCode()" class="ui button" style="position: absolute; top: 0px; right: -4px; border-top-left-radius: 0; border-bottom-right-radius: 0; "> <template v-if="currentFormatted"> 显示原始代码 </template> <template v-if="!currentFormatted"> 格式化代码 </template> </a> <% } %> </code-box> <code-box v-if="detailResult && detailResult.compile" no-escape title="编译信息" v-bind:content="detailResult.compile.message"></code-box> <code-box v-if="detailResult" title="系统信息" v-bind:content="detailResult.systemMessage"></code-box> <div class="ui styled fluid accordion" :class="singleSubtask ? 'single-subtask' : '' " v-if="detailResult && detailResult.judge && detailResult.judge.subtasks"> <template v-for="subtask, $index in detailResult.judge.subtasks"> <div class="title" :class="singleSubtask ? 'active' : ''"> <div class="ui grid"> <div class="three wide column"> <i class="dropdown icon"></i> 子任务 #{{ $index + 1 }} </div> <div class="four wide column"> <status-label :status="getSubtaskResult(subtask)" :indetail="true" :progress="getProgress($index)"></status-label> </div> <div class="three wide column" v-if="subtask.score != null"> 得分:<span style="font-weight: normal; ">{{ Math.trunc(subtask.score) }}</span> </div> </div> </div> <div class="content" :class="singleSubtask ? 'active' : ''"> <div class="accordion"> <template v-for="curCase, $caseIndex in subtask.cases"> <div class="title" :class="checkTestcaseOK(curCase) || curCase.errorMessage ? '' : 'unexpandable'"> <div class="ui grid"> <div class="three wide column"> <i class="dropdown icon"></i> 测试点 #{{ $caseIndex + 1 }} </div> <div class="four wide column"> <status-label :status="getTestcaseStatus(curCase)" :indetail="true"></status-label> </div> <template v-if="checkTestcaseOK(curCase)"> <div class="three wide column"> 得分:<span style="font-weight: normal; ">{{ Math.trunc(curCase.result.scoringRate * 100) }}</span> </div> <div class="three wide column" v-if="curCase.result.time != null && curCase.result.time !== NaN"> 用时:<span style="font-weight: normal; ">{{ curCase.result.time }} ms</span> </div> <div class="three wide column" v-if="curCase.result.memory != null && curCase.result.memory !== NaN"> 内存:<span style="font-weight: normal; ">{{ curCase.result.memory }} KiB</span> </div> </template> </div> </div> <div class="content"> <template v-if="checkTestcaseOK(curCase)"> <code-box v-if="curCase.result.input" :title="'输入文件(<span style=\'font-family: monospace; \'>'+ curCase.result.input.name +'</span>)'" :content="curCase.result.input.content" :download="<%= serializejs(stdoj.utils.makeUrl(['problem', info.problemId, 'testdata', 'download'])) %> + '/' + curCase.result.input.name"></code-box> <code-box v-if="curCase.result.output" :title="'答案文件(<span style=\'font-family: monospace; \'>'+ curCase.result.output.name +'</span>)'" :content="curCase.result.output.content" :download="<%= serializejs(stdoj.utils.makeUrl(['problem', info.problemId, 'testdata', 'download'])) %> + '/' + curCase.result.output.name"></code-box> <code-box title="用户输出" :content="curCase.result.userOutput"></code-box> <code-box title="标准错误流" :content="curCase.result.userError"></code-box> <code-box title="Special Judge 信息" :content="curCase.result.spjMessage"></code-box> <code-box title="系统信息" :content="curCase.result.systemMessage"></code-box> </template> <code-box title="错误信息" :content="curCase.errorMessage"></code-box> </div> </template> </div> </div> </template> </div> </div> <script src="<%- lib('vue/2.5.21/vue.min.js') %>"></script> <script src="<%- lib('socket.io/2.2.0/socket.io.js') %>"></script> <script src="<%- lib('jsondiffpatch/0.2.5/jsondiffpatch.min.js') %>"></script> <% include submissions_item %> <script type="text/x-template" id="codeBoxTemplate"> <div style="margin-top: 0px; margin-bottom: 14px; " v-if="content != null && content !== ''"> <p v-if="title" class="transition visible"> <strong v-html="title"></strong> <a v-if="download" style="color: #000; " :href="download"><i class="download icon"></i></a> </p> <div class="ui existing segment"> <slot></slot> <pre v-if="!noEscape" style="margin-top: 0; margin-bottom: 0; "><code>{{ content }}</code></pre> <pre v-if="noEscape" style="margin-top: 0; margin-bottom: 0; "><code v-html="content"></code></pre> </div> </div> </script> <script> Vue.component("code-box", { template: "#codeBoxTemplate", props: { title: String, content: String, noEscape: { type: Boolean, default: false }, download: String } }); const socketUrl = "/detail"; const displayConfig = <%- serializejs(displayConfig) %>; const token = <%- serializejs(socketToken) %>; const TestcaseResultType = {}; (function (TestcaseResultType) { TestcaseResultType[TestcaseResultType["Accepted"] = 1] = "Accepted"; TestcaseResultType[TestcaseResultType["WrongAnswer"] = 2] = "WrongAnswer"; TestcaseResultType[TestcaseResultType["PartiallyCorrect"] = 3] = "PartiallyCorrect"; TestcaseResultType[TestcaseResultType["MemoryLimitExceeded"] = 4] = "MemoryLimitExceeded"; TestcaseResultType[TestcaseResultType["TimeLimitExceeded"] = 5] = "TimeLimitExceeded"; TestcaseResultType[TestcaseResultType["OutputLimitExceeded"] = 6] = "OutputLimitExceeded"; TestcaseResultType[TestcaseResultType["FileError"] = 7] = "FileError"; TestcaseResultType[TestcaseResultType["RuntimeError"] = 8] = "RuntimeError"; TestcaseResultType[TestcaseResultType["JudgementFailed"] = 9] = "JudgementFailed"; TestcaseResultType[TestcaseResultType["InvalidInteraction"] = 10] = "InvalidInteraction"; })(TestcaseResultType); const statusToString = {}; statusToString[TestcaseResultType.Accepted] = "Accepted"; statusToString[TestcaseResultType.WrongAnswer] = "Wrong Answer"; statusToString[TestcaseResultType.PartiallyCorrect] = "Partially Correct"; statusToString[TestcaseResultType.MemoryLimitExceeded] = "Memory Limit Exceeded"; statusToString[TestcaseResultType.TimeLimitExceeded] = "Time Limit Exceeded"; statusToString[TestcaseResultType.OutputLimitExceeded] = "Output Limit Exceeded"; statusToString[TestcaseResultType.RuntimeError] = "Runtime Error"; statusToString[TestcaseResultType.FileError] = "File Error"; statusToString[TestcaseResultType.JudgementFailed] = "Judgement Failed"; statusToString[TestcaseResultType.InvalidInteraction] = "Invalid Interaction"; const TaskStatus = {}; (function (TaskStatus) { TaskStatus[TaskStatus["Waiting"] = 0] = "Waiting"; TaskStatus[TaskStatus["Running"] = 1] = "Running"; TaskStatus[TaskStatus["Done"] = 2] = "Done"; TaskStatus[TaskStatus["Failed"] = 3] = "Failed"; TaskStatus[TaskStatus["Skipped"] = 4] = "Skipped"; })(TaskStatus); const unformattedCode = <%- serializejs(code) %>; const formattedCode = <%- serializejs(formattedCode) %>; function toggleFormattedCode() { if (vueApp.currentFormatted) { vueApp.currentFormatted = false; vueApp.code = unformattedCode; } else { vueApp.currentFormatted = true; vueApp.code = formattedCode; } } const vueApp = new Vue({ el: '#vueAppFuckSafari', data: { roughData: { info: <%- serializejs(info) %>, result: <%- serializejs(roughResult) %>, running: false, displayConfig: displayConfig }, code: <%- serializejs(preferFormattedCode && formattedCode !== null) -%> ? formattedCode : unformattedCode, currentFormatted: <%- serializejs(preferFormattedCode && formattedCode !== null) -%>, detailResult: <%- serializejs(detailResult) %>, displayConfig: displayConfig, }, computed: { singleSubtask() { return this.detailResult.judge.subtasks.length === 1; }, showRejudge() { return this.displayConfig.showRejudge && (!this.roughData.running); } }, methods: { getStatusString(statusCode) { return statusToString[statusCode]; }, firstNonAC(t) { if (t.every(function(v){ return v === TestcaseResultType.Accepted;})) { return TestcaseResultType.Accepted; } else { return t.find(function(r) { return r !== TestcaseResultType.Accepted;}); } }, getSubtaskResult(t) { if (t.cases.some(function(c){ return c.status === TaskStatus.Running;})) { return "Running"; } else if (t.cases.some(function(c) { return c.status === TaskStatus.Waiting;})) { return "Waiting"; } else if (t.cases.every(function(c){ return c.status === TaskStatus.Done || c.status === TaskStatus.Skipped;})) { return this.getStatusString(this.firstNonAC(t.cases.filter(function(c) { return c.result; }) .map(function(c) { return c.result.type;}))); } else { return "System Error"; } }, getTestcaseStatus(c) { if (c.status === TaskStatus.Done) { return this.getStatusString(c.result.type); } else if (c.status === TaskStatus.Waiting) { return "Waiting"; } else if (c.status === TaskStatus.Running) { return "Running"; } else if (c.status === TaskStatus.Skipped) { return "Skipped"; } else { return "System Error"; } }, checkTestcaseOK(c) { return c.status === TaskStatus.Done; }, getProgress(index) { if (!this.detailResult || !this.detailResult.judge || !this.detailResult.judge.subtasks) return { finished: 0, total: 0 }; var isPending = status => [TaskStatus.Waiting, TaskStatus.Running].includes(status); var subtaskProgress = [], allFinished = 0, allTotal = 0; for (var i in this.detailResult.judge.subtasks) { var subtaskFinished = 0, subtaskTotal = 0; for (var j in this.detailResult.judge.subtasks[i].cases) { subtaskTotal++, allTotal++; if (!isPending(this.detailResult.judge.subtasks[i].cases[j].status)) subtaskFinished++, allFinished++; } subtaskProgress.push({ finished: subtaskFinished, total: subtaskTotal }); } var allProgress = { finished: allFinished, total: allTotal }; return typeof index === 'undefined' ? allProgress : subtaskProgress[index]; } }, mounted() { $(document).ready(function(){ $('.ui.accordion').accordion({ selector: { trigger: '.title:not(.unexpandable)' } })}); }, updated() { $('.ui.accordion').off().accordion({ selector: { trigger: '.title:not(.unexpandable)' } }); } }); if (token != null) { const loadSocketIO = function () { let currentVersion = 0; const socket = io(socketUrl); socket.on('connect', function () { socket.on('start', function () { vueApp.roughData.running = true; console.log("Judge start!"); vueApp.detailResult = {}; }); socket.on('update', function (p) { console.log("Delta: ", p); if (p.from === currentVersion) { currentVersion = p.to; jsondiffpatch.patch(vueApp.detailResult, p.delta); vueApp.detailResult = JSON.parse(JSON.stringify(vueApp.detailResult));// WTF? vueApp.roughData.result = p.roughResult; } else { // Some packets are dropped. Let's reset. socket.close(); setTimeout(loadSocketIO, 0); } }); socket.on('finish', function (p) { console.log("Judge finished"); vueApp.roughData.running = false; vueApp.roughData.result = p.roughResult; vueApp.detailResult = p.result; socket.close(); }); socket.emit('join', token, function (data) { console.log("join! ", data); if (data && data.ok) { if (data.finished) { vueApp.roughData.result = data.roughResult; if (!data.result) location.reload(true); vueApp.detailResult = data.result; socket.close(); } else { if (data.running) { // vueApp.roughData.running = true; vueApp.detailResult = data.current.content; // vueApp.roughData.result = data.roughResult; currentVersion = data.current.version; } } } else { alert("ERROR: " + JSON.stringify(data)); } }); }); }; loadSocketIO(); } </script> <script> document.addEventListener('keydown', function (event) { if ((event.ctrlKey || event.metaKey) && event.key === 'a') { var sel = window.getSelection(); var rg = document.createRange() rg.selectNodeContents(document.querySelector('code')); sel.removeAllRanges(); sel.addRange(rg); event.preventDefault(); } }); </script> </span> <% include footer %>