diff --git a/app.js b/app.js index a91284a..f01e95e 100644 --- a/app.js +++ b/app.js @@ -10,7 +10,6 @@ * * SYZOJ is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public diff --git a/libs/judger.js b/libs/judger.js index e836d78..640236d 100644 --- a/libs/judger.js +++ b/libs/judger.js @@ -2,9 +2,12 @@ const enums = require('./enums'), rp = require('request-promise'), url = require('url'); -module.exports.judge = async function (judge_state, priority) { +const util = require('util'); +module.exports.judge = async function (judge_state, problem, priority) { let type, param, extraFile = null; - switch (judge_state.problem.type) { + console.log("JudgeState: " + util.inspect(judge_state)); + console.log("Problem: " + util.inspect(judge_state)); + switch (problem.type) { case 'submit-answer': type = enums.ProblemType.AnswerSubmission; param = null; @@ -18,10 +21,10 @@ module.exports.judge = async function (judge_state, priority) { param = { language: judge_state.language, code: judge_state.code, - timeLimit: judge_state.problem.time_limit, - memoryLimit: judge_state.problem.memory_limit, - fileIOInput: judge_state.problem.file_io ? judge_state.problem.file_io_input_name : null, - fileIOOutput: judge_state.problem.file_io ? judge_state.problem.file_io_output_name : null + timeLimit: problem.time_limit, + memoryLimit: problem.memory_limit, + fileIOInput: problem.file_io ? problem.file_io_input_name : null, + fileIOOutput: problem.file_io ? problem.file_io_output_name : null }; break; } @@ -29,7 +32,7 @@ module.exports.judge = async function (judge_state, priority) { const req = { content: { taskId: judge_state.id, - testData: judge_state.problem.id.toString(), + testData: problem.id.toString(), type: type, priority: priority, param: param diff --git a/libs/submissions_process.js b/libs/submissions_process.js new file mode 100644 index 0000000..02b644f --- /dev/null +++ b/libs/submissions_process.js @@ -0,0 +1,40 @@ +const getSubmissionInfo = (s, displayConfig) => ({ + taskId: s.id, + user: s.user.username, + userId: s.user_id, + problemName: s.problem.title, + problemId: s.problem_id, + language: displayConfig.hideCode ? null : ((s.language != null && s.language !== '') ? syzoj.config.languages[s.language].show : null), + codeSize: displayConfig.hideCode ? null : syzoj.utils.formatSize(s.code.length), + submitTime: syzoj.utils.formatDate(s.submit_time), +}); + +const getRoughResult = (x, displayConfig) => { + if (displayConfig.hideResult) { + // 0: Waiting 1: Running + if (x.status === "System Error") + return { result: "System Error" }; + if (x.compilation == null || [0, 1].includes(x.compilation.status)) { + return null; + } else { + if (x.compilation.status === 2) { // 2 is TaskStatus.Done + return { result: "Submitted" }; + } else { + return { result: "Compile Error" }; + } + } + } else { + if (x.pending) { + return null; + } else { + return { + result: x.status, + time: displayConfig.hideUsage ? null : x.total_time, + memory: displayConfig.hideUsage ? null : x.max_memory, + score: displayConfig.hideUsage ? null : x.score + }; + } + } +} + +module.exports = { getRoughResult, getSubmissionInfo }; \ No newline at end of file diff --git a/models/contest.js b/models/contest.js index ea19bd7..9365f70 100644 --- a/models/contest.js +++ b/models/contest.js @@ -91,13 +91,28 @@ class Contest extends Model { this.ranklist = await ContestRanklist.fromID(this.ranklist_id); } - async isAllowedEditBy(user) { + async isSupervisior(user) { return user && (user.is_admin || this.holder_id === user.id); } - async isAllowedSeeResultBy(user) { + allowedSeeingOthers() { if (this.type === 'acm') return true; - return (user && (user.is_admin || this.holder_id === user.id)) || !(await this.isRunning()); + else return false; + } + + allowedSeeingScore() { // If not, then the user can only see status + if (this.type === 'ioi') return true; + else return false; + } + + allowedSeeingResult() { // If not, then the user can only see compile progress + if (this.type === 'ioi' || this.type === 'acm') return true; + else return false; + } + + allowedSeeingTestcase() { + if (this.type === 'ioi') return true; + return false; } async getProblems() { @@ -145,12 +160,12 @@ class Contest extends Model { }); } - async isRunning(now) { + isRunning(now) { if (!now) now = syzoj.utils.getCurrentDate(); return now >= this.start_time && now < this.end_time; } - async isEnded(now) { + isEnded(now) { if (!now) now = syzoj.utils.getCurrentDate(); return now >= this.end_time; } diff --git a/models/judge_state.js b/models/judge_state.js index dc94211..8f0d325 100644 --- a/models/judge_state.js +++ b/models/judge_state.js @@ -116,7 +116,7 @@ class JudgeState extends Model { else if (this.type === 0) return this.problem.is_public || (user && (await user.hasPrivilege('manage_problem'))); else if (this.type === 1) { let contest = await Contest.fromID(this.type_info); - if (await contest.isRunning()) { + if (contest.isRunning()) { return (user && this.user_id === user.id) || (user && user.is_admin); } else { return true; @@ -124,51 +124,6 @@ class JudgeState extends Model { } } - async isAllowedSeeCodeBy(user) { - await this.loadRelationships(); - - if (user && user.id === this.problem.user_id) return true; - else if (this.type === 0) return this.problem.is_public || (user && (await user.hasPrivilege('manage_problem'))); - else if (this.type === 1) { - let contest = await Contest.fromID(this.type_info); - if (await contest.isRunning()) { - return (user && this.user_id === user.id) || (user && user.is_admin); - } else { - return true; - } - } - } - - async isAllowedSeeCaseBy(user) { - await this.loadRelationships(); - - if (user && user.id === this.problem.user_id) return true; - else if (this.type === 0) return this.problem.is_public || (user && (await user.hasPrivilege('manage_problem'))); - else if (this.type === 1) { - let contest = await Contest.fromID(this.type_info); - if (await contest.isRunning()) { - return contest.type === 'ioi' || (user && user.is_admin); - } else { - return true; - } - } - } - - async isAllowedSeeDataBy(user) { - await this.loadRelationships(); - - if (user && user.id === this.problem.user_id) return true; - else if (this.type === 0) return this.problem.is_public || (user && (await user.hasPrivilege('manage_problem'))); - else if (this.type === 1) { - let contest = await Contest.fromID(this.type_info); - if (await contest.isRunning()) { - return user && user.is_admin; - } else { - return true; - } - } - } - async updateRelatedInfo(newSubmission) { await syzoj.utils.lock(['JudgeState::updateRelatedInfo', 'problem', this.problem_id], async () => { await syzoj.utils.lock(['JudgeState::updateRelatedInfo', 'user', this.user_id], async () => { @@ -237,7 +192,7 @@ class JudgeState extends Model { await contest.newSubmission(this); } - await Judger.judge(this, 1); + await Judger.judge(this, this.problem, 1); }); } diff --git a/modules/contest.js b/modules/contest.js index 8e52f67..21f6bb7 100644 --- a/modules/contest.js +++ b/modules/contest.js @@ -26,6 +26,9 @@ let Problem = syzoj.model('problem'); let JudgeState = syzoj.model('judge_state'); let User = syzoj.model('user'); +const jwt = require('jsonwebtoken'); +const { getSubmissionInfo, getRoughResult } = require('../libs/submissions_process'); + app.get('/contests', async (req, res) => { try { let where; @@ -118,15 +121,15 @@ app.post('/contest/:id/edit', async (req, res) => { app.get('/contest/:id', async (req, res) => { try { + const curUser = res.locals.user; let contest_id = parseInt(req.params.id); let contest = await Contest.fromID(contest_id); if (!contest) throw new ErrorMessage('无此比赛。'); if (!contest.is_public && (!res.locals.user || !res.locals.user.is_admin)) throw new ErrorMessage('无此比赛。'); - contest.allowedEdit = await contest.isAllowedEditBy(res.locals.user); - contest.running = await contest.isRunning(); - contest.ended = await contest.isEnded(); + contest.running = contest.isRunning(); + contest.ended = contest.isEnded(); contest.subtitle = await syzoj.utils.markdown(contest.subtitle); contest.information = await syzoj.utils.markdown(contest.information); @@ -205,7 +208,8 @@ app.get('/contest/:id', async (req, res) => { res.render('contest', { contest: contest, problems: problems, - hasStatistics: hasStatistics + hasStatistics: hasStatistics, + isSupervisior: await contest.isSupervisior(curUser) }); } catch (e) { syzoj.log(e); @@ -262,28 +266,48 @@ app.get('/contest/:id/submissions', async (req, res) => { try { let contest_id = parseInt(req.params.id); let contest = await Contest.fromID(contest_id); - if (!contest) throw new ErrorMessage('无此比赛。'); - if (!await contest.isAllowedSeeResultBy(res.locals.user)) throw new ErrorMessage('您没有权限进行此操作。'); - contest.ended = await contest.isEnded(); + if (contest.isEnded()) { + res.redirect(syzoj.utils.makeUrl(['submissions'], { contest: contest_id })); + return; + } + const hideResult = !contest.allowedSeeingResult(); + const displayConfig = { + pushType: hideResult ? 'compile' : 'rough', + hideScore: !contest.allowedSeeingScore(), + hideUsage: true, + hideCode: true, + hideResult: hideResult, + hideOthers: !contest.allowedSeeingOthers(), + showDetailResult: contest.allowedSeeingTestcase(), + inContest: true + }; let problems_id = await contest.getProblems(); + const curUser = res.locals.user; - let user = await User.fromName(req.query.submitter || ''); + let user = req.query.submitter && await User.fromName(req.query.submitter); let where = {}; - if (user) where.user_id = user.id; - if (req.query.problem_id) where.problem_id = problems_id[parseInt(req.query.problem_id) - 1]; - where.type = 1; - where.type_info = contest_id; + if (displayConfig.hideOthers) { + if (curUser == null || // Not logined + (user && user.id !== curUser.id)) { // Not querying himself + throw new ErrorMessage("您没有权限执行此操作"); + } + where.user_id = curUser.id; + } else { + if (user) { + where.user_id = user.id; + } + } + + if (!displayConfig.hideScore) { + let minScore = parseInt(req.query.min_score); + let maxScore = parseInt(req.query.max_score); - if (contest.ended || (res.locals.user && res.locals.user.is_admin)) { - if (!((!res.locals.user || !res.locals.user.is_admin) && !contest.ended && contest.type === 'acm')) { - let minScore = parseInt(req.query.min_score); + if (!isNaN(minScore) || !isNaN(maxScore)) { if (isNaN(minScore)) minScore = 0; - let maxScore = parseInt(req.query.max_score); if (isNaN(maxScore)) maxScore = 100; - where.score = { $and: { $gte: parseInt(minScore), @@ -291,32 +315,44 @@ app.get('/contest/:id/submissions', async (req, res) => { } }; } + } + + if (req.query.language) { + if (req.query.language === 'submit-answer') where.language = ''; + else where.language = req.query.language; + } - if (req.query.language) where.language = req.query.language; + if (!displayConfig.hideResult) { if (req.query.status) where.status = { $like: req.query.status + '%' }; } + if (req.query.problem_id) where.problem_id = problems_id[parseInt(req.query.problem_id) - 1]; + where.type = 1; + where.type_info = contest_id; + let paginate = syzoj.utils.paginate(await JudgeState.count(where), req.query.page, syzoj.config.page.judge_state); let judge_state = await JudgeState.query(paginate, where, [['submit_time', 'desc']]); - await judge_state.forEachAsync(async obj => obj.allowedSeeCode = await obj.isAllowedSeeCodeBy(res.locals.user)); await judge_state.forEachAsync(async obj => { await obj.loadRelationships(); obj.problem_id = problems_id.indexOf(obj.problem_id) + 1; obj.problem.title = syzoj.utils.removeTitleTag(obj.problem.title); - - if (contest.type === 'noi' && !contest.ended && !await obj.problem.isAllowedEditBy(res.locals.user)) { - if (!['Compile Error', 'Waiting', 'Compiling'].includes(obj.status)) { - obj.status = 'Submitted'; - } - } }); - res.render('contest_submissions', { + res.render('submissions', { contest: contest, - judge_state: judge_state, + items: judge_state.map(x => ({ + info: getSubmissionInfo(x, displayConfig), + token: (getRoughResult(x, displayConfig) == null) ? jwt.sign({ + taskId: x.id, + displayConfig: displayConfig + }, syzoj.config.judge_token) : null, + result: getRoughResult(x, displayConfig), + running: false, + })), paginate: paginate, - form: req.query + form: req.query, + displayConfig: displayConfig, }); } catch (e) { syzoj.log(e); @@ -326,7 +362,7 @@ app.get('/contest/:id/submissions', async (req, res) => { } }); -app.get('/contest/:id/:pid', async (req, res) => { +app.get('/contest/:id/problem/:pid', async (req, res) => { try { let contest_id = parseInt(req.params.id); let contest = await Contest.fromID(contest_id); @@ -341,8 +377,8 @@ app.get('/contest/:id/:pid', async (req, res) => { let problem = await Problem.fromID(problem_id); await problem.loadRelationships(); - contest.ended = await contest.isEnded(); - if (!(await contest.isRunning() || contest.ended)) { + contest.ended = contest.isEnded(); + if (!(contest.isRunning() || contest.isEnded())) { if (await problem.isAllowedUseBy(res.locals.user)) { return res.redirect(syzoj.utils.makeUrl(['problem', problem_id])); } @@ -351,7 +387,7 @@ app.get('/contest/:id/:pid', async (req, res) => { problem.specialJudge = await problem.hasSpecialJudge(); - await syzoj.utils.markdown(problem, [ 'description', 'input_format', 'output_format', 'example', 'limit_and_hint' ]); + await syzoj.utils.markdown(problem, ['description', 'input_format', 'output_format', 'example', 'limit_and_hint']); let state = await problem.getJudgeState(res.locals.user, false); let testcases = await syzoj.utils.parseTestdata(problem.getTestdataPath(), problem.type === 'submit-answer'); @@ -388,8 +424,8 @@ app.get('/contest/:id/:pid/download/additional_file', async (req, res) => { let problem_id = problems_id[pid - 1]; let problem = await Problem.fromID(problem_id); - contest.ended = await contest.isEnded(); - if (!(await contest.isRunning() || contest.ended)) { + contest.ended = contest.isEnded(); + if (!(contest.isRunning() || contest.idEnded())) { if (await problem.isAllowedUseBy(res.locals.user)) { return res.redirect(syzoj.utils.makeUrl(['problem', problem_id, 'download', 'additional_file'])); } diff --git a/modules/problem.js b/modules/problem.js index b385626..fd9b11e 100644 --- a/modules/problem.js +++ b/modules/problem.js @@ -618,11 +618,11 @@ app.post('/problem/:id/submit', app.multer.fields([{ name: 'answer', maxCount: 1 }); } - let contest_id = parseInt(req.query.contest_id), redirectToContest = false; + let contest_id = parseInt(req.query.contest_id); if (contest_id) { let contest = await Contest.fromID(contest_id); if (!contest) throw new ErrorMessage('无此比赛。'); - if (!await contest.isRunning()) throw new ErrorMessage('比赛未开始或已结束。'); + if (!contest.isRunning()) throw new ErrorMessage('比赛未开始或已结束。'); let problems_id = await contest.getProblems(); if (!problems_id.includes(id)) throw new ErrorMessage('无此题目。'); @@ -638,14 +638,19 @@ app.post('/problem/:id/submit', app.multer.fields([{ name: 'answer', maxCount: 1 await judge_state.updateRelatedInfo(true); try { - await Judger.judge(judge_state, contest_id ? 3 : 2); + await Judger.judge(judge_state, problem, contest_id ? 3 : 2); } catch (err) { judge_state.status = "System Error"; + judge_state.pending = false; await judge_state.save(); throw new ErrorMessage(`无法开始评测:${err.toString()}`); } - res.redirect(syzoj.utils.makeUrl(['submission', judge_state.id])); + if (contest_id) { + res.redirect(syzoj.utils.makeUrl(['contest', contest_id, 'submissions'])); + } else { + res.redirect(syzoj.utils.makeUrl(['submission', judge_state.id])); + } } catch (e) { syzoj.log(e); res.render('error', { @@ -781,7 +786,7 @@ app.get('/problem/:id/download/additional_file', async (req, res) => { if (contest_id) { let contest = await Contest.fromID(contest_id); if (!contest) throw new ErrorMessage('无此比赛。'); - if (!await contest.isRunning()) throw new ErrorMessage('比赛未开始或已结束。'); + if (!contest.isRunning()) throw new ErrorMessage('比赛未开始或已结束。'); let problems_id = await contest.getProblems(); if (!problems_id.includes(id)) throw new ErrorMessage('无此题目。'); } else { diff --git a/modules/submission.js b/modules/submission.js index 8a8ff40..1f693b6 100644 --- a/modules/submission.js +++ b/modules/submission.js @@ -24,33 +24,33 @@ let User = syzoj.model('user'); let Contest = syzoj.model('contest'); const jwt = require('jsonwebtoken'); +const { getSubmissionInfo, getRoughResult } = require('../libs/submissions_process'); // s is JudgeState -const getSubmissionInfo = (s) => ({ - taskId: s.id, - user: s.user.username, - userId: s.user_id, - problemName: s.problem.title, - problemId: s.problem.id, - language: (s.language != null && s.language !== '') ? syzoj.config.languages[s.language].show : null, - codeSize: s.allowedSeeCode ? syzoj.utils.formatSize(s.code.length) : null, - submitTime: syzoj.utils.formatDate(s.submit_time), -}); - -const getRoughResult = (x) => (x.pending ? null : { - result: x.status, - time: x.total_time, - memory: x.max_memory, - score: x.score -}); - app.get('/submissions', async (req, res) => { try { + const curUser = res.locals.user; let user = await User.fromName(req.query.submitter || ''); let where = {}; if (user) where.user_id = user.id; else if (req.query.submitter) where.user_id = -1; + if (req.query.contest == null) { + where.type = { $ne: 1 }; + } else { + const contestId = Number(req.query.contest); + const contest = await Contest.fromID(contestId); + contest.ended = contest.isEnded(); + if (contest.ended || // If the contest is ended + (curUser && contest.isSupervisior(curUser)) // Or if the user have the permission to check + ) { + where.type = { $eq: 1 }; + where.type_info = { $eq: contestId }; + } else { + throw new Error("您暂时无权查看此比赛的详细评测信息。"); + } + } + let minScore = parseInt(req.query.min_score); let maxScore = parseInt(req.query.max_score); @@ -71,9 +71,7 @@ app.get('/submissions', async (req, res) => { } if (req.query.status) where.status = { $like: req.query.status + '%' }; - where.type = { $ne: 1 }; - - if (!res.locals.user || !await res.locals.user.hasPrivilege('manage_problem')) { + if (!curUser || !await curUser.hasPrivilege('manage_problem')) { if (req.query.problem_id) { where.problem_id = { $and: [ @@ -94,22 +92,31 @@ app.get('/submissions', async (req, res) => { let judge_state = await JudgeState.query(paginate, where, [['submit_time', 'desc']]); await judge_state.forEachAsync(async obj => obj.loadRelationships()); - await judge_state.forEachAsync(async obj => obj.allowedSeeCode = await obj.isAllowedSeeCodeBy(res.locals.user)); - await judge_state.forEachAsync(async obj => obj.allowedSeeData = await obj.isAllowedSeeDataBy(res.locals.user)); + const displayConfig = { + pushType: 'rough', + hideScore: false, + hideUsage: false, + hideCode: false, + hideResult: false, + hideOthers: false, + inContest: false, + showDetailResult: true + }; res.render('submissions', { // judge_state: judge_state, items: judge_state.map(x => ({ - info: getSubmissionInfo(x), - token: x.pending ? jwt.sign({ + info: getSubmissionInfo(x, displayConfig), + token: (getRoughResult(x, displayConfig) == null) ? jwt.sign({ taskId: x.id, - type: 'rough' + displayConfig: displayConfig }, syzoj.config.judge_token) : null, - result: getRoughResult(x), - running: false + result: getRoughResult(x, displayConfig), + running: false, })), paginate: paginate, - form: req.query + form: req.query, + displayConfig: displayConfig, }); } catch (e) { syzoj.log(e); @@ -128,7 +135,14 @@ app.get('/submission/:id', async (req, res) => { let contest; if (judge.type === 1) { contest = await Contest.fromID(judge.type_info); - contest.ended = await contest.isEnded(); + contest.ended = contest.isEnded(); + let problems_id = await contest.getProblems(); + judge.problem_id = problems_id.indexOf(judge.problem_id) + 1; + judge.problem.title = syzoj.utils.removeTitleTag(judge.problem.title); + + if (!contest.ended && !await judge.problem.isAllowedEditBy(res.locals.user)) { + throw new Error("对不起,在比赛结束之前,您不能查看评测结果。"); + } } await judge.loadRelationships(); @@ -137,31 +151,14 @@ app.get('/submission/:id', async (req, res) => { judge.codeLength = judge.code.length; judge.code = await syzoj.utils.highlight(judge.code, syzoj.config.languages[judge.language].highlight); } - judge.allowedSeeCode = await judge.isAllowedSeeCodeBy(res.locals.user); - judge.allowedSeeCase = await judge.isAllowedSeeCaseBy(res.locals.user); - judge.allowedSeeData = await judge.isAllowedSeeDataBy(res.locals.user); + judge.allowedRejudge = await judge.problem.isAllowedEditBy(res.locals.user); judge.allowedManage = await judge.problem.isAllowedManageBy(res.locals.user); - let hideScore = false; - if (contest) { - let problems_id = await contest.getProblems(); - judge.problem_id = problems_id.indexOf(judge.problem_id) + 1; - judge.problem.title = syzoj.utils.removeTitleTag(judge.problem.title); - - if (contest.type === 'noi' && !contest.ended && !await judge.problem.isAllowedEditBy(res.locals.user)) { - if (!['Compile Error', 'Waiting', 'Compiling'].includes(judge.status)) { - judge.status = 'Submitted'; - } - - hideScore = true; - } - } - res.render('submission', { info: getSubmissionInfo(judge), roughResult: getRoughResult(judge), - code: judge.code.toString("utf8"), + code: (judge.problem.type !== 'submit-answer') ? judge.code.toString("utf8") : '', detailResult: judge.result, socketToken: judge.pending ? jwt.sign({ taskId: judge.id, @@ -176,58 +173,6 @@ app.get('/submission/:id', async (req, res) => { } }); -app.get('/submission/:id/ajax', async (req, res) => { - try { - let id = parseInt(req.params.id); - let judge = await JudgeState.fromID(id); - if (!judge || !await judge.isAllowedVisitBy(res.locals.user)) throw new ErrorMessage('您没有权限进行此操作。'); - - let contest; - if (judge.type === 1) { - contest = await Contest.fromID(judge.type_info); - contest.ended = await contest.isEnded(); - } - - await judge.loadRelationships(); - - if (judge.problem.type !== 'submit-answer') { - judge.codeLength = judge.code.length; - judge.code = await syzoj.utils.highlight(judge.code, syzoj.config.languages[judge.language].highlight); - } - judge.allowedSeeCode = await judge.isAllowedSeeCodeBy(res.locals.user); - judge.allowedSeeCase = await judge.isAllowedSeeCaseBy(res.locals.user); - judge.allowedSeeData = await judge.isAllowedSeeDataBy(res.locals.user); - judge.allowedRejudge = await judge.problem.isAllowedEditBy(res.locals.user); - judge.allowedManage = await judge.problem.isAllowedManageBy(res.locals.user); - - let hideScore = false; - if (contest) { - let problems_id = await contest.getProblems(); - judge.problem_id = problems_id.indexOf(judge.problem_id) + 1; - judge.problem.title = syzoj.utils.removeTitleTag(judge.problem.title); - - if (contest.type === 'noi' && !contest.ended && !await judge.problem.isAllowedEditBy(res.locals.user)) { - if (!['Compile Error', 'Waiting', 'Compiling'].includes(judge.status)) { - judge.status = 'Submitted'; - } - - hideScore = true; - } - } - - res.render('submission_content', { - hideScore, hideScore, - contest: contest, - judge: judge - }); - } catch (e) { - syzoj.log(e); - res.render('error', { - err: e - }); - } -}); - app.post('/submission/:id/rejudge', async (req, res) => { try { let id = parseInt(req.params.id); diff --git a/views/contest.ejs b/views/contest.ejs index ea21a3d..feaedc6 100644 --- a/views/contest.ejs +++ b/views/contest.ejs @@ -10,7 +10,8 @@
编号 | -题目 | -状态 | - <% if ((typeof contest === 'undefined' || !contest) || !((!user || !user.is_admin) && !contest.ended && (contest.type === 'acm' || contest.type === 'noi'))) { %> -分数 | - <% } %> -总时间 | -内存 | -代码 | -提交者 | -提交时间 | -
---|---|---|---|---|---|---|---|---|