mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 17:36:08 +08:00
比赛递交
This commit is contained in:
1 parent
d3e36fcb6c
commit
b66ed01fa2
13 files changed
+86
-84
No files matched your search
+10
-54
@@ -1,8 +1,7 @@
|
||||
const
|
||||
{ ContestNotLiveError, ValidationError, ProblemNotFoundError,
|
||||
ContestNotAttendedError, ContestScoreboardHiddenError } = require('../error'),
|
||||
{ PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD, PERM_CREATE_CONTEST,
|
||||
PERM_EDIT_CONTEST } = require('../permission'),
|
||||
ContestNotAttendedError } = require('../error'),
|
||||
{ PERM_CREATE_CONTEST, PERM_EDIT_CONTEST } = require('../permission'),
|
||||
{ constants } = require('../options'),
|
||||
paginate = require('../lib/paginate'),
|
||||
contest = require('../model/contest'),
|
||||
@@ -11,52 +10,7 @@ const
|
||||
user = require('../model/user'),
|
||||
{ Route, Handler } = require('../service/server');
|
||||
|
||||
|
||||
class ContestHandler extends Handler {
|
||||
canViewHiddenScoreboard() {
|
||||
return this.user.hasPerm(PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD);
|
||||
}
|
||||
canShowRecord(tdoc, allowPermOverride = true) {
|
||||
if (contest.RULES[tdoc.rule].showRecord(tdoc, new Date())) return true;
|
||||
if (allowPermOverride && this.canViewHiddenScoreboard(tdoc)) return true;
|
||||
return false;
|
||||
}
|
||||
canShowScoreboard(tdoc, allowPermOverride = true) {
|
||||
if (contest.RULES[tdoc.rule].showScoreboard(tdoc, new Date())) return true;
|
||||
if (allowPermOverride && this.canViewHiddenScoreboard(tdoc)) return true;
|
||||
return false;
|
||||
}
|
||||
async getScoreboard(tid, isExport = false) {
|
||||
let tdoc = await contest.get(tid);
|
||||
if (!this.canShowScoreboard(tdoc)) throw new ContestScoreboardHiddenError(tid);
|
||||
let tsdocs = await contest.getMultiStatus(tid).sort(contest.RULES[tdoc.rule].statusSort).toArray();
|
||||
let uids = [];
|
||||
for (let tsdoc of tsdocs) uids.push(tsdoc.uid);
|
||||
let [udict, pdict] = await Promise.all([user.getList(uids), problem.getList(tdoc['pids'])]);
|
||||
let ranked_tsdocs = contest.RULES[tdoc.rule].rank(tsdocs);
|
||||
let rows = contest.RULES[tdoc.rule].scoreboard(isExport, str => str ? str.toString().translate(this.user.language) : '', tdoc, ranked_tsdocs, udict, pdict);
|
||||
return [tdoc, rows, udict];
|
||||
}
|
||||
async verifyProblems(pids) {
|
||||
console.log(pids);
|
||||
let pdocs = await problem.getMulti({
|
||||
$or: [
|
||||
{ _id: { $in: pids } }, { pid: { $in: pids } }
|
||||
]
|
||||
}).sort({ _id: 1 }).toArray();
|
||||
if (pids.length != pdocs.length)
|
||||
for (let pid of pids) {
|
||||
let p = false;
|
||||
for (let pdoc of pdocs)
|
||||
if (pid == pdoc._id || pid == pdoc.pid) {
|
||||
p = true;
|
||||
break;
|
||||
}
|
||||
if (!p) throw new ProblemNotFoundError(pid);
|
||||
}
|
||||
return pids;
|
||||
}
|
||||
}
|
||||
const ContestHandler = contest.ContestHandlerMixin(Handler);
|
||||
class ContestListHandler extends ContestHandler {
|
||||
async get({ rule = 0, page = 1 }) {
|
||||
this.response.template = 'contest_main.html';
|
||||
@@ -91,7 +45,7 @@ class ContestDetailHandler extends ContestHandler {
|
||||
let psdict = {}, rdict = {}, attended;
|
||||
if (tsdoc) {
|
||||
attended = tsdoc.attend == 1;
|
||||
for (let pdetail in tsdoc.detail || [])
|
||||
for (let pdetail of tsdoc.journal || [])
|
||||
psdict[pdetail.pid] = pdetail;
|
||||
if (this.canShowRecord(this.tdoc)) {
|
||||
let q = [];
|
||||
@@ -161,7 +115,7 @@ class ContestEditHandler extends ContestDetailHandler {
|
||||
}
|
||||
endAt = new Date(beginAt + duration * 3600 * 1000);
|
||||
if (beginAt >= endAt) throw new ValidationError('duration');
|
||||
await this.verifyProblems(pids);
|
||||
pids = await this.verifyProblems(pids);
|
||||
await contest.edit(this.tdoc._id, title, content, rule, beginAt, endAt, pids);
|
||||
if (this.tdoc.beginAt != beginAt || this.tdoc.endAt != endAt
|
||||
|| Array.isDiff(this.tdoc.pids, pids) || this.tdoc.rule != rule)
|
||||
@@ -172,7 +126,7 @@ class ContestEditHandler extends ContestDetailHandler {
|
||||
}
|
||||
class ContestProblemHandler extends ContestDetailHandler {
|
||||
async prepare({ tid, pid }) {
|
||||
[this.tdoc, this.pdoc] = await Promise.all([contest.get(tid), problem.get({ pid, uid: this.user._id })]);
|
||||
[this.tdoc, this.pdoc] = await Promise.all([contest.get(tid), problem.get(pid, this.user._id)]);
|
||||
[this.tsdoc, this.udoc] = await Promise.all([
|
||||
contest.getStatus(this.tdoc._id, this.user._id),
|
||||
user.getById(this.tdoc.owner)
|
||||
@@ -183,7 +137,9 @@ class ContestProblemHandler extends ContestDetailHandler {
|
||||
if (!this.attended) throw new ContestNotAttendedError(this.tdoc._id);
|
||||
if (!contest.is_ongoing(this.tdoc)) throw new ContestNotLiveError(this.tdoc._id);
|
||||
}
|
||||
if (!this.tdoc.pids.includes(pid)) throw new ProblemNotFoundError(pid, this.tdoc._id);
|
||||
console.log(this.tdoc.pids, this.pdoc._id);
|
||||
if (!this.tdoc.pids.map(s => s.toString()).includes(this.pdoc._id.toString()))
|
||||
throw new ProblemNotFoundError(pid, this.tdoc._id);
|
||||
}
|
||||
async get({ tid }) {
|
||||
let path = [
|
||||
@@ -257,7 +213,7 @@ class ContestCreateHandler extends ContestHandler {
|
||||
}
|
||||
endAt = new Date(beginAt.getTime() + duration * 3600 * 1000);
|
||||
if (beginAt >= endAt) throw new ValidationError('duration');
|
||||
await this.verifyProblems(pids);
|
||||
pids = await this.verifyProblems(pids);
|
||||
let tid = await contest.add(title, content, this.user._id, rule, beginAt, endAt, pids);
|
||||
this.response.body = { tid };
|
||||
this.response.redirect = `/c/${tid}`;
|
||||
|
||||
@@ -56,7 +56,7 @@ class ProblemDetailHandler extends ProblemHandler {
|
||||
this.response.template = 'problem_detail.html';
|
||||
this.uid = this.user._id;
|
||||
this.pid = pid;
|
||||
if (pid) this.pdoc = await problem.get(this.pid, this.uid);
|
||||
if (pid) this.pdoc = await problem.get(pid, this.uid);
|
||||
if (this.pdoc.hidden && this.pdoc.owner != this.uid) this.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
|
||||
if (this.pdoc) this.udoc = await user.getById(this.pdoc.owner);
|
||||
this.response.body = {
|
||||
|
||||
@@ -4,6 +4,7 @@ const
|
||||
PERM_REJUDGE, PERM_VIEW_PROBLEM_HIDDEN } = require('../permission'),
|
||||
problem = require('../model/problem'),
|
||||
record = require('../model/record'),
|
||||
contest = require('../model/contest'),
|
||||
user = require('../model/user'),
|
||||
bus = require('../service/bus'),
|
||||
queue = require('../service/queue'),
|
||||
@@ -75,14 +76,16 @@ class RecordConnectionHandler extends ConnectionHandler {
|
||||
this.send({ html: await this.renderHTML('record_main_tr.html', { rdoc, udoc, pdoc }) });
|
||||
}
|
||||
}
|
||||
class RecordDetailConnectionHandler extends ConnectionHandler {
|
||||
class RecordDetailConnectionHandler extends contest.ContestHandlerMixin(ConnectionHandler) {
|
||||
async prepare({ rid }) {
|
||||
let rdoc = await record.get(rid);
|
||||
if (rdoc.tid)
|
||||
if (!await this.rdocContestVisible(rdoc)) {
|
||||
if (rdoc.tid) {
|
||||
let tdoc = await contest.get(rdoc.tid);
|
||||
if (!this.canShowRecord(tdoc)) {
|
||||
this.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.rid = rid;
|
||||
bus.subscribe(['record_change'], this.onRecordChange);
|
||||
this.onRecordChange({ value: rdoc });
|
||||
|
||||
+54
-11
@@ -1,7 +1,11 @@
|
||||
const
|
||||
user = require('./user'),
|
||||
problem = require('./problem'),
|
||||
{ ValidationError, ContestNotFoundError, ContestAlreadyAttendedError,
|
||||
ContestNotAttendedError, ProblemNotFoundError, ContestScoreboardHiddenError } = require('../error'),
|
||||
{ PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD } = require('../permission'),
|
||||
validator = require('../lib/validator'),
|
||||
{ ValidationError, ContestNotFoundError, ContestAlreadyAttendedError } = require('../error'),
|
||||
db = require('../service/db.js'),
|
||||
db = require('../service/db'),
|
||||
coll = db.collection('contest'),
|
||||
coll_status = db.collection('contest.status');
|
||||
|
||||
@@ -65,13 +69,13 @@ async function get(tid) {
|
||||
if (!tdoc) throw new ContestNotFoundError(tid);
|
||||
return tdoc;
|
||||
}
|
||||
async function get_random_id(query) {
|
||||
let pdocs = coll.find(query);
|
||||
let pcount = await pdocs.count();
|
||||
if (pcount) {
|
||||
let pdoc = await pdocs.skip(Math.floor(Math.random() * pcount)).limit(1).toArray()[0];
|
||||
return pdoc.pid;
|
||||
} else return null;
|
||||
async function updateStatus(tid, uid, rid, pid, accept, score) {
|
||||
let tdoc = await get(tid);
|
||||
let tsdoc = await coll_status.findOneAndUpdate({ tid: tdoc._id, uid }, {
|
||||
$push: { journal: { rid, pid, accept, score } },
|
||||
$inc: { rev: 1 }
|
||||
}, { upsert: true });
|
||||
if (!tsdoc.value.attend) throw new ContestNotAttendedError(tid, uid);
|
||||
}
|
||||
async function getListStatus(uid, tids) {
|
||||
let r = {};
|
||||
@@ -86,6 +90,9 @@ async function attend(tid, uid) {
|
||||
}
|
||||
await coll.findOneAndUpdate({ _id: tid }, { $inc: { attend: 1 } });
|
||||
}
|
||||
function getMultiStatus(query) {
|
||||
return coll_status.find(query);
|
||||
}
|
||||
function is_new(tdoc, days = 1) {
|
||||
let now = new Date().getTime();
|
||||
let readyAt = tdoc.beginAt.getTime();
|
||||
@@ -106,11 +113,47 @@ function is_ongoing(tdoc) {
|
||||
function is_done(tdoc) {
|
||||
return tdoc.endAt <= new Date();
|
||||
}
|
||||
|
||||
const ContestHandlerMixin = c => class extends c {
|
||||
canViewHiddenScoreboard() {
|
||||
return this.user.hasPerm(PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD);
|
||||
}
|
||||
canShowRecord(tdoc, allowPermOverride = true) {
|
||||
if (RULES[tdoc.rule].showRecord(tdoc, new Date())) return true;
|
||||
if (allowPermOverride && this.canViewHiddenScoreboard(tdoc)) return true;
|
||||
return false;
|
||||
}
|
||||
canShowScoreboard(tdoc, allowPermOverride = true) {
|
||||
if (RULES[tdoc.rule].showScoreboard(tdoc, new Date())) return true;
|
||||
if (allowPermOverride && this.canViewHiddenScoreboard(tdoc)) return true;
|
||||
return false;
|
||||
}
|
||||
async getScoreboard(tid, isExport = false) {
|
||||
let tdoc = await get(tid);
|
||||
if (!this.canShowScoreboard(tdoc)) throw new ContestScoreboardHiddenError(tid);
|
||||
let tsdocs = await getMultiStatus(tid).sort(RULES[tdoc.rule].statusSort).toArray();
|
||||
let uids = [];
|
||||
for (let tsdoc of tsdocs) uids.push(tsdoc.uid);
|
||||
let [udict, pdict] = await Promise.all([user.getList(uids), problem.getList(tdoc['pids'])]);
|
||||
let ranked_tsdocs = RULES[tdoc.rule].rank(tsdocs);
|
||||
let rows = RULES[tdoc.rule].scoreboard(isExport, str => str ? str.toString().translate(this.user.language) : '', tdoc, ranked_tsdocs, udict, pdict);
|
||||
return [tdoc, rows, udict];
|
||||
}
|
||||
async verifyProblems(pids) {
|
||||
let r = [];
|
||||
for (let pid of pids) {
|
||||
let res = await problem.get(pid);
|
||||
if (res) r.push(res._id);
|
||||
else throw new ProblemNotFoundError(pid);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
RULES, add, getListStatus, attend, edit, get, get_random_id,
|
||||
RULES, ContestHandlerMixin, add, getListStatus, attend, edit, get, updateStatus,
|
||||
count: query => coll.find(query).count(),
|
||||
getMulti: query => coll.find(query),
|
||||
getMultiStatus: query => coll_status.find(query),
|
||||
getStatus: (tid, uid) => coll_status.findOne({ tid, uid }),
|
||||
setStatus: (tid, uid, $set) => coll_status.findOneAndUpdate({ tid, uid }, { $set }),
|
||||
is_new, is_upcoming, is_not_started, is_ongoing, is_done,
|
||||
|
||||
@@ -120,7 +120,7 @@ async function random(query) {
|
||||
}
|
||||
async function getList(pids) {
|
||||
let r = {};
|
||||
for (let pid of pids) r[pid] = await get({ pid });
|
||||
for (let pid of pids) r[pid] = await get(pid);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<a
|
||||
{%- if tdoc is none %}
|
||||
href="/p/{{ pdoc.pid }}"
|
||||
{%- elif tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST %}
|
||||
href="/t/{{ tdoc._id }}/{{ pdoc._id }}"
|
||||
{%- else %}
|
||||
href="/c/{{ tdoc._id }}/p/{{ pdoc._id }}"
|
||||
{%- endif %}
|
||||
>
|
||||
{%- endif %}
|
||||
|
||||
@@ -46,12 +46,12 @@
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if psdict[pid] and psdict[pid].rid %}
|
||||
{% set rdoc = rdict[psdict[pid].rid] %}
|
||||
{% if handler.can_show_record(tdoc) %}
|
||||
{% if handler.canShowRecord(tdoc) %}
|
||||
{{ record.render_status_td(rdoc) }}
|
||||
{% else %}
|
||||
<td class="col--status record-status--border"><a href="/r/{{ rdoc._id }}">{{ _('Submitted') }}</a></td>
|
||||
{% endif %}
|
||||
<td class="col--submit-at">{{ datetime_span(rdoc._id) }}</td>
|
||||
<td class="col--submit-at">{{ datetime_span(rdoc._id)|safe }}</td>
|
||||
{% else %}
|
||||
<td class="col--status record-status--border">{{ _('No Submissions') }}</td>
|
||||
<td class="col--submit-at">-</td>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
{% if handler.is_not_started(tdoc) %}
|
||||
{{ nothing.render('This homework is not open and you cannot view problems.') }}
|
||||
{% else %}
|
||||
{% if attended or handler.is_done(tdoc) %}
|
||||
{% if attended or model.contest.is_done(tdoc) %}
|
||||
<div class="section__body no-padding">
|
||||
<table class="data-table">
|
||||
<colgroup>
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
{{ _('Open Since') }}: {{ datetime_span(tdoc['begin_at']) }}
|
||||
</li>
|
||||
<li>
|
||||
{% if handler.is_homework_extended(tdoc) or handler.is_done(tdoc) %}
|
||||
{% if handler.is_homework_extended(tdoc) or model.homework.is_done(tdoc) %}
|
||||
{{ _('Hard Deadline') }}: {{ datetime_span(tdoc['end_at']) }}
|
||||
{% else %}
|
||||
{{ _('Deadline') }}: {{ datetime_span(tdoc['penalty_since']) }}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<span class="icon icon-homework"></span> {{ _('View Homework') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if not attended and not handler.is_done(tdoc) %}
|
||||
{% if not attended and not model.contest.is_done(tdoc) %}
|
||||
<li class="menu__item">
|
||||
{% if handler.hasPerm(perm.PERM_ATTEND_HOMEWORK) and handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
<form action="{{ reverse_url('homework_detail', tid=tdoc['doc_id']) }}" method="POST">
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
{% endif %}
|
||||
<div>
|
||||
<ol class="menu">
|
||||
{% if handler.is_ongoing(tdoc) %}
|
||||
{% if model.contest.is_ongoing(tdoc) %}
|
||||
{% if page_name == 'contest_detail_problem' %}
|
||||
<li class="menu__item scratchpad--hide"><a class="menu__link highlight" name="problem-sidebar__open-scratchpad" href="javascript:;" data-global-hotkey="alt+e">
|
||||
<span class="icon icon-enlarge"></span> {{ _('Open Scratchpad') }} (Alt+E)
|
||||
@@ -16,19 +16,19 @@
|
||||
<span class="icon icon-enlarge"></span> {{ _('Quit Scratchpad') }} (Alt+Q)
|
||||
</a></li>
|
||||
{% else %}
|
||||
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('contest_detail_problem', tid=tdoc['doc_id'], pid=pdoc['_id']) }}">
|
||||
<li class="menu__item"><a class="menu__link" href="/c/{{ tdoc._id }}/p/{{ pdoc._id }}">
|
||||
<span class="icon icon-flag"></span> {{ _('View Problem') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
<li class="menu__item scratchpad--hide"><a class="menu__link{% if page_name == 'contest_detail_problem_submit' %} active{% endif %}" href="/t/{{ tdoc._id }}/p/{{ pdoc._id }}">
|
||||
<li class="menu__item scratchpad--hide"><a class="menu__link{% if page_name == 'contest_detail_problem_submit' %} active{% endif %}" href="/c/{{ tdoc._id }}/p/{{ pdoc._id }}/submit">
|
||||
<span class="icon icon-send"></span> {{ _('Submit') }}
|
||||
</a></li>
|
||||
{% elif handler.is_done(tdoc) %}
|
||||
{% elif model.contest.is_done(tdoc) %}
|
||||
<li class="menu__item scratchpad--hide"><a class="menu__link" href="/p/{{ pdoc.pid }}">
|
||||
<span class="icon icon-send"></span> {{ _('Open in Problem Set') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if handler.own(pdoc, perm.PERM_EDIT_PROBLEM_SELF) or handler.hasPerm(perm.PERM_EDIT_PROBLEM) %}
|
||||
{% if pdoc.owner == handler.user._id or handler.hasPerm(perm.PERM_EDIT_PROBLEM) %}
|
||||
<li class="menu__seperator"></li>
|
||||
<li class="menu__item"><a class="menu__link{% if page_name == 'problem_edit' %} active{% endif %}" href="/p/{{ pdoc.pid }}/edit">
|
||||
<span class="icon icon-edit"></span> {{ _('Edit') }}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<li class="menu__item scratchpad--hide"><a class="menu__link{% if page_name == 'homework_detail_problem_submit' %} active{% endif %}" href="{{ reverse_url('homework_detail_problem_submit', tid=tdoc['doc_id'], pid=pdoc['_id']) }}">
|
||||
<span class="icon icon-send"></span> {{ _('Submit') }}
|
||||
</a></li>
|
||||
{% elif handler.is_done(tdoc) %}
|
||||
{% elif model.contest.is_done(tdoc) %}
|
||||
<li class="menu__item scratchpad--hide"><a class="menu__link" href="/p/{{ pdoc.pid }}">
|
||||
<span class="icon icon-send"></span> {{ _('Open in Problem Set') }}
|
||||
</a></li>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
{% endif %}
|
||||
{% if tdoc %}
|
||||
{% if tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST %}
|
||||
{% if handler.is_done(tdoc) %}
|
||||
{% if model.contest.is_done(tdoc) %}
|
||||
<blockquote class="note">
|
||||
<p>{{ _('You cannot submit for this problem because the contest is ended. You can click "Open in Problem Set" to view this problem in normal mode.') }}</p>
|
||||
</blockquote>
|
||||
|
||||
Reference in new issue
Block a user