From f40e4e2c7421587baace53e30bcb88a4d4b29183 Mon Sep 17 00:00:00 2001 From: masnn Date: Sun, 12 Apr 2020 12:29:49 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8F=82=E5=8A=A0=E6=AF=94?= =?UTF-8?q?=E8=B5=9B=E7=9A=84=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hydro/error.js | 14 +++++++------- hydro/handler/contest.js | 21 +++++++++++---------- hydro/lib/misc.js | 3 +++ hydro/lib/rank.js | 11 +++++++++++ hydro/model/contest.js | 8 ++++---- hydro/module/contest/acm.js | 2 ++ hydro/module/contest/oi.js | 14 ++++++++++---- hydro/service/server.js | 2 +- templates/contest_scoreboard.html | 21 +++++++++++---------- templates/partials/homework_sidebar.html | 4 ++-- 10 files changed, 62 insertions(+), 38 deletions(-) create mode 100644 hydro/lib/rank.js diff --git a/hydro/error.js b/hydro/error.js index d6dbc07a..123743b7 100644 --- a/hydro/error.js +++ b/hydro/error.js @@ -92,6 +92,12 @@ class ContestNotAttendedError extends ForbiddenError { this.params = [tid]; } } +class ContestAlreadyAttendedError extends ForbiddenError { + constructor(tid, uid) { + super('You\'ve already attended this contest.'); + this.params = [tid, uid]; + } +} class ContestNotLiveError extends ForbiddenError { constructor(tid) { super('This contest is not live.'); @@ -148,7 +154,7 @@ module.exports = { ValidationError, ProblemNotFoundError, TrainingNotFoundError, ContestNotFoundError, RecordNotFoundError, SolutionNotFoundError, AlreadyVotedError, ContestNotAttendedError, ContestNotLiveError, - ContestScoreboardHiddenError + ContestScoreboardHiddenError, ContestAlreadyAttendedError }; /* @@ -278,12 +284,6 @@ class InvalidJoinInvitationCodeError(ForbiddenError): return 'The invitation code you provided is invalid.' -class ContestAlreadyAttendedError(ForbiddenError): - @property - def message(self): - return "You've already attended this contest." - - class HomeworkScoreboardHiddenError(ForbiddenError): @property def message(self): diff --git a/hydro/handler/contest.js b/hydro/handler/contest.js index 5e65c536..d26d4bf6 100644 --- a/hydro/handler/contest.js +++ b/hydro/handler/contest.js @@ -14,10 +14,10 @@ const class ContestHandler extends Handler { canViewHiddenScoreboard() { - return this.hasPerm(PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD); + return this.user.hasPerm(PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD); } canShowRecord(tdoc, allowPermOverride = true) { - if (contest.RULES[tdoc.rule].showRecordFunc(tdoc, new Date())) return true; + if (contest.RULES[tdoc.rule].showRecord(tdoc, new Date())) return true; if (allowPermOverride && this.canViewHiddenScoreboard(tdoc)) return true; return false; } @@ -27,14 +27,15 @@ class ContestHandler extends Handler { return false; } async getScoreboard(tid, isExport = false) { - let [tdoc, tsdocs] = await contest.getAndListStatus(tid); + 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, this.translate, tdoc, ranked_tsdocs, udict, pdict); - return tdoc, rows, udict; + 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); @@ -61,17 +62,17 @@ class ContestListHandler extends ContestHandler { this.response.template = 'contest_main.html'; let tdocs, qs, tpcount; if (!rule) { - tdocs = contest.getMulti(); + tdocs = contest.getMulti().sort({ beginAt: -1 }); qs = ''; } else { if (!contest.CONTEST_RULES.includes(rule)) throw new ValidationError('rule'); - tdocs = contest.getMulti({ rule }); + tdocs = contest.getMulti({ rule }).sort({ beginAt: -1 }); qs = 'rule={0}'.format(rule); } [tdocs, tpcount] = await paginate(tdocs, page, constants.CONTEST_PER_PAGE); let tids = []; for (let tdoc of tdocs) tids.push(tdoc._id); - let tsdict = await contest.getListStatus(this.uid, tids); + let tsdict = await contest.getListStatus(this.user._id, tids); this.response.body = { page, tpcount, qs, rule, tdocs, tsdict }; @@ -84,7 +85,7 @@ class ContestDetailHandler extends ContestHandler { async get({ page = 1 }) { this.response.template = 'contest_detail.html'; let [tsdoc, pdict] = await Promise.all([ - contest.getStatus(this.tdoc._id, this.uid), + contest.getStatus(this.tdoc._id, this.user._id), problem.getList(this.tdoc.pids) ]); let psdict = {}, rdict = {}, attended; @@ -111,7 +112,7 @@ class ContestDetailHandler extends ContestHandler { } async post_attend() { if (contest.is_done(this.tdoc)) throw new ContestNotLiveError(this.tdoc._id); - await contest.attend(this.tdoc._id, this.uid); + await contest.attend(this.tdoc._id, this.user._id); this.back(); } } diff --git a/hydro/lib/misc.js b/hydro/lib/misc.js index d07a4bdd..f27e671c 100644 --- a/hydro/lib/misc.js +++ b/hydro/lib/misc.js @@ -11,6 +11,9 @@ exports.datetime_span = function (dt, relative = true, format = '%Y-%m-%d %H:%M: dt.format(format) ); }; +exports.nl2br = function (self) { + return self.replace(/\n/gm, '
'); +}; exports.paginate = function* (page, num_pages) { let radius = 2, first, last; if (page > 1) { diff --git a/hydro/lib/rank.js b/hydro/lib/rank.js new file mode 100644 index 00000000..b415599d --- /dev/null +++ b/hydro/lib/rank.js @@ -0,0 +1,11 @@ +module.exports = function* ranked(diter, equ = (a, b) => a == b) { + let last_doc = null; + let r = 0, count = 0; + for (let doc of diter) { + count++; + if (count == 1 || !equ(last_doc, doc)) + r = count; + last_doc = doc; + yield [r, doc]; + } +}; diff --git a/hydro/model/contest.js b/hydro/model/contest.js index 4a2a7e5d..4ea3b927 100644 --- a/hydro/model/contest.js +++ b/hydro/model/contest.js @@ -1,6 +1,6 @@ const validator = require('../lib/validator'), - { ValidationError, ContestNotFoundError } = require('../error'), + { ValidationError, ContestNotFoundError, ContestAlreadyAttendedError } = require('../error'), db = require('../service/db.js'), coll = db.collection('contest'), coll_status = db.collection('contest.status'); @@ -56,7 +56,7 @@ async function getListStatus(uid, tids) { } async function attend(tid, uid) { try { - await coll_status.insertOne({ tid, uid }); + await coll_status.insertOne({ tid, uid, attend: 1 }); } catch (e) { throw new ContestAlreadyAttendedError(tid, uid); } @@ -70,10 +70,10 @@ function is_new(tdoc, days = 1) { function is_upcoming(tdoc, days = 1) { let now = new Date().getTime(); let readyAt = tdoc.beginAt.getTime(); - return (now > readyAt + days * 24 * 3600 * 1000 && now < tdoc.beginAt); + return (now > readyAt - days * 24 * 3600 * 1000 && now < tdoc.beginAt); } function is_not_started(tdoc) { - return new Date() < tdoc.beginAt; + return (new Date()) < tdoc.beginAt; } function is_ongoing(tdoc) { let now = new Date(); diff --git a/hydro/module/contest/acm.js b/hydro/module/contest/acm.js index 612a0101..1085221a 100644 --- a/hydro/module/contest/acm.js +++ b/hydro/module/contest/acm.js @@ -1,6 +1,8 @@ module.exports = { TEXT: 'ACM/ICPC', check: () => { }, + showScoreboard: () => true, + showRecord: (tdoc, now) => now > tdoc.endAt, stat: (tdoc, journal) => { let naccept = {}, effective = {}, detail = [], accept = 0, time = 0; for (let j in journal) { diff --git a/hydro/module/contest/oi.js b/hydro/module/contest/oi.js index 6ee27cd7..e2ad6d26 100644 --- a/hydro/module/contest/oi.js +++ b/hydro/module/contest/oi.js @@ -1,3 +1,5 @@ +const ranked = require('../../lib/rank'); + module.exports = { TEXT: 'OI', check: () => { }, @@ -10,10 +12,13 @@ module.exports = { } return { score, detail }; }, - showScoreboard(tdoc,now){ - return now>tdoc.endAt; + showScoreboard(tdoc, now) { + return now > tdoc.endAt; }, - scoreboard(is_export, _, tdoc, ranked_tsdocs, udict, dudict, pdict) { + showRecord(tdoc, now) { + return now > tdoc.endAt; + }, + scoreboard(is_export, _, tdoc, ranked_tsdocs, udict, pdict) { let columns = [ { type: 'rank', value: _('Rank') }, { type: 'user', value: _('User') }, @@ -49,5 +54,6 @@ module.exports = { rows.push(row); } return rows; - } + }, + rank: tdocs => ranked(tdocs, (a, b) => a.score == b.score) }; \ No newline at end of file diff --git a/hydro/service/server.js b/hydro/service/server.js index 5e866f83..f622c578 100644 --- a/hydro/service/server.js +++ b/hydro/service/server.js @@ -252,7 +252,7 @@ class ConnectionHandler { return new Promise((resolve, reject) => { template.render(name, Object.assign(context, { handler: this, - _: this.translate, + _: str => str ? str.toString().translate(this.user.language) : '', user: this.user }), (error, res) => { console.timeEnd(name); diff --git a/templates/contest_scoreboard.html b/templates/contest_scoreboard.html index 202d460c..6460e88f 100644 --- a/templates/contest_scoreboard.html +++ b/templates/contest_scoreboard.html @@ -1,3 +1,4 @@ +{% set page_name = "contest_scoreboard" %} {% extends "layout/basic.html" %} {% block content %}
@@ -21,25 +22,25 @@ {%- for column in rows[0] -%} - {% if column['type'] == 'problem_detail' %} - {{ column['value'] }} + {% if column.type == 'problem_detail' %} + {{ column['value'] }} {% else %} - {{ column['value'] }} + {{ column.value }} {% endif %} {%- endfor -%} - {%- for row in rows[1:] -%} + {%- for i, row in rows -%}{% if i>0 %} {%- for column in row -%} - {% if column['type'] == 'user' %} - {{ user.render_inline(column['raw'], badge=false) }} - {% elif column['type'] == 'record' %} - {% if column['raw'] %} - {{ column['value']|nl2br }} + {% if column.type == 'user' %} + {{ user.render_inline(column.raw, badge=false) }} + {% elif column.type == 'record' %} + {% if column.raw %} + {{ column.value|nl2br }} {% else %} {{ column['value']|nl2br }} {% endif %} @@ -49,7 +50,7 @@ {%- endfor -%} - {%- endfor -%} + {% endif %}{%- endfor -%}
diff --git a/templates/partials/homework_sidebar.html b/templates/partials/homework_sidebar.html index 1848316e..dbb290cc 100644 --- a/templates/partials/homework_sidebar.html +++ b/templates/partials/homework_sidebar.html @@ -43,11 +43,11 @@ {% endif %} {% endif %} - {% if handler.can_show_scoreboard(tdoc, False) %} + {% if handler.canShowScoreboard(tdoc, False) %} - {% elif handler.can_view_hidden_scoreboard(tdoc) %} + {% elif handler.canViewHiddenScoreboard(tdoc) %}