/* * This file is part of SYZOJ. * * Copyright (c) 2016 Menci * * SYZOJ is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * 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 * License along with SYZOJ. If not, see . */ 'use strict'; let Contest = syzoj.model('contest'); let ContestRanklist = syzoj.model('contest_ranklist'); let ContestPlayer = syzoj.model('contest_player'); let Problem = syzoj.model('problem'); let JudgeState = syzoj.model('judge_state'); let User = syzoj.model('user'); app.get('/contests', async (req, res) => { try { let where; if (res.locals.user && await res.locals.user.is_admin) where = {} else where = { is_public: true }; let paginate = syzoj.utils.paginate(await Contest.count(where), req.query.page, syzoj.config.page.contest); let contests = await Contest.query(paginate, where, [['start_time', 'desc']]); await contests.forEachAsync(async x => x.subtitle = await syzoj.utils.markdown(x.subtitle)); res.render('contests', { contests: contests, paginate: paginate }) } catch (e) { syzoj.log(e); res.render('error', { err: e }); } }); app.get('/contest/:id/edit', async (req, res) => { try { if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); let contest_id = parseInt(req.params.id); let contest = await Contest.fromID(contest_id); if (!contest) { contest = await Contest.create(); contest.id = 0; } let problems = []; if (contest.problems) problems = await contest.problems.split('|').mapAsync(async id => await Problem.fromID(id)); res.render('contest_edit', { contest: contest, problems: problems }); } catch (e) { syzoj.log(e); res.render('error', { err: e }); } }); app.post('/contest/:id/edit', async (req, res) => { try { if (!res.locals.user || !res.locals.user.is_admin) throw new ErrorMessage('您没有权限进行此操作。'); let contest_id = parseInt(req.params.id); let contest = await Contest.fromID(contest_id); if (!contest) { contest = await Contest.create(); contest.holder_id = res.locals.user.id; let ranklist = await ContestRanklist.create(); await ranklist.save(); contest.ranklist_id = ranklist.id; // Only new contest can be set type if (!['noi', 'ioi', 'acm'].includes(req.body.type)) throw new ErrorMessage('无效的赛制。'); contest.type = req.body.type; } if (!req.body.title.trim()) throw new ErrorMessage('比赛名不能为空。'); contest.title = req.body.title; contest.subtitle = req.body.subtitle; if (!Array.isArray(req.body.problems)) req.body.problems = [req.body.problems]; contest.problems = req.body.problems.join('|'); contest.information = req.body.information; contest.start_time = syzoj.utils.parseDate(req.body.start_time); contest.end_time = syzoj.utils.parseDate(req.body.end_time); contest.is_public = req.body.is_public === 'on'; await contest.save(); res.redirect(syzoj.utils.makeUrl(['contest', contest.id])); } catch (e) { syzoj.log(e); res.render('error', { err: e }); } }); app.get('/contest/:id', async (req, res) => { try { let contest_id = parseInt(req.params.id); let contest = await Contest.fromID(contest_id); if (!contest) throw new ErrorMessage('无此比赛。'); contest.allowedEdit = await contest.isAllowedEditBy(res.locals.user); contest.running = await contest.isRunning(); contest.ended = await contest.isEnded(); contest.subtitle = await syzoj.utils.markdown(contest.subtitle); contest.information = await syzoj.utils.markdown(contest.information); let problems_id = await contest.getProblems(); let problems = await problems_id.mapAsync(async id => await Problem.fromID(id)); let player = null; if (res.locals.user) { player = await ContestPlayer.findInContest({ contest_id: contest.id, user_id: res.locals.user.id }); } problems = problems.map(x => ({ problem: x, status: null, judge_id: null, statistics: null })); if (player) { for (let problem of problems) { if (contest.type === 'noi') { if (player.score_details[problem.problem.id]) { let judge_state = await JudgeState.fromID(player.score_details[problem.problem.id].judge_id); problem.status = judge_state.status; if (!contest.ended && !await problem.problem.isAllowedEditBy(res.locals.user) && !['Compile Error', 'Waiting', 'Compiling'].includes(problem.status)) { problem.status = 'Submitted'; } problem.judge_id = player.score_details[problem.problem.id].judge_id; } } else if (contest.type === 'ioi') { if (player.score_details[problem.problem.id]) { let judge_state = await JudgeState.fromID(player.score_details[problem.problem.id].judge_id); problem.status = judge_state.status; problem.judge_id = player.score_details[problem.problem.id].judge_id; } } else if (contest.type === 'acm') { if (player.score_details[problem.problem.id]) { problem.status = { accepted: player.score_details[problem.problem.id].accepted, unacceptedCount: player.score_details[problem.problem.id].unacceptedCount }; problem.judge_id = player.score_details[problem.problem.id].judge_id; } else { problem.status = null; } } } } let hasStatistics = false; if (contest.type === 'ioi' || contest.type === 'acm' || (contest.type === 'noi' && (contest.ended || (res.locals.user && res.locals.user.is_admin)))) { hasStatistics = true; await contest.loadRelationships(); let players = await contest.ranklist.getPlayers(); for (let problem of problems) { problem.statistics = { attempt: 0, accepted: 0 }; if (contest.type === 'ioi' || contest.type === 'noi') { problem.statistics.partially = 0; } for (let player of players) { if (player.score_details[problem.problem.id]) { problem.statistics.attempt++; if ((contest.type === 'acm' && player.score_details[problem.problem.id].accepted) || ((contest.type === 'noi' || contest.type === 'ioi') && player.score_details[problem.problem.id].score === 100)) { problem.statistics.accepted++; } if ((contest.type === 'noi' || contest.type === 'ioi') && player.score_details[problem.problem.id].score > 0) { problem.statistics.partially++; } } } } } res.render('contest', { contest: contest, problems: problems, hasStatistics: hasStatistics }); } catch (e) { syzoj.log(e); res.render('error', { err: e }); } }); app.get('/contest/:id/ranklist', 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('您没有权限进行此操作。'); await contest.loadRelationships(); let players_id = []; for (let i = 1; i <= contest.ranklist.ranklist.player_num; i++) players_id.push(contest.ranklist.ranklist[i]); let ranklist = await players_id.mapAsync(async player_id => { let player = await ContestPlayer.fromID(player_id); for (let i in player.score_details) { player.score_details[i].judge_state = await JudgeState.fromID(player.score_details[i].judge_id); } let user = await User.fromID(player.user_id); return { user: user, player: player }; }); let problems_id = await contest.getProblems(); let problems = await problems_id.mapAsync(async id => await Problem.fromID(id)); res.render('contest_ranklist', { contest: contest, ranklist: ranklist, problems: problems }); } catch (e) { syzoj.log(e); res.render('error', { err: e }); } }); 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(); let problems_id = await contest.getProblems(); let user = 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 (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)) minScore = 0; let maxScore = parseInt(req.query.max_score); if (isNaN(maxScore)) maxScore = 100; where.score = { $and: { $gte: parseInt(minScore), $lte: parseInt(maxScore) } }; } if (req.query.language) where.language = req.query.language; if (req.query.status) where.status = { $like: req.query.status + '%' }; } 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', { contest: contest, judge_state: judge_state, paginate: paginate, form: req.query }); } catch (e) { syzoj.log(e); res.render('error', { err: e }); } }); app.get('/contest/:id/:pid', async (req, res) => { try { let contest_id = parseInt(req.params.id); let contest = await Contest.fromID(contest_id); if (!contest) throw new ErrorMessage('无此比赛。'); let problems_id = await contest.getProblems(); let pid = parseInt(req.params.pid); if (!pid || pid < 1 || pid > problems_id.length) throw new ErrorMessage('无此题目。'); 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)) { if (await problem.isAllowedUseBy(res.locals.user)) { return res.redirect(syzoj.utils.makeUrl(['problem', problem_id])); } throw new ErrorMessage('比赛尚未开始。'); } problem.specialJudge = await problem.hasSpecialJudge(); 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'); await problem.loadRelationships(); res.render('problem', { pid: pid, contest: contest, problem: problem, state: state, lastLanguage: res.locals.user ? await res.locals.user.getLastSubmitLanguage() : null, testcases: testcases }); } catch (e) { syzoj.log(e); res.render('error', { err: e }); } }); app.get('/contest/:id/:pid/download/additional_file', async (req, res) => { try { let id = parseInt(req.params.id); let contest = await Contest.fromID(id); if (!contest) throw new ErrorMessage('无此比赛。'); let problems_id = await contest.getProblems(); let pid = parseInt(req.params.pid); if (!pid || pid < 1 || pid > problems_id.length) throw new ErrorMessage('无此题目。'); 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)) { if (await problem.isAllowedUseBy(res.locals.user)) { return res.redirect(syzoj.utils.makeUrl(['problem', problem_id, 'download', 'additional_file'])); } throw new ErrorMessage('比赛尚未开始。'); } await problem.loadRelationships(); if (!problem.additional_file) throw new ErrorMessage('无附加文件。'); console.log(`additional_file_${id}_${pid}.zip`); res.download(problem.additional_file.getPath(), `additional_file_${id}_${pid}.zip`); } catch (e) { syzoj.log(e); res.status(404); res.render('error', { err: e }); } });