From b1153b641a65f11c852e6fc4969963113a2e74de Mon Sep 17 00:00:00 2001 From: masnn Date: Fri, 17 Apr 2020 13:53:06 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=BB=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hydro/handler/home.js | 53 ++++++++++++++++++++++- hydro/handler/judge.js | 47 ++++++++++++++++---- hydro/handler/problem.js | 7 ++- hydro/handler/record.js | 2 + hydro/handler/training.js | 2 +- hydro/model/problem.js | 16 +++++++ hydro/model/record.js | 7 ++- hydro/model/training.js | 17 +++++++- hydro/model/user.js | 6 +++ hydro/options.js | 3 ++ templates/components/problem.html | 6 +-- templates/discussion_nodes_widget.html | 2 +- templates/{domain_main.html => main.html} | 37 +++++++--------- 13 files changed, 163 insertions(+), 42 deletions(-) rename templates/{domain_main.html => main.html} (86%) diff --git a/hydro/handler/home.js b/hydro/handler/home.js index ed384fcbf..6ce6dff86 100644 --- a/hydro/handler/home.js +++ b/hydro/handler/home.js @@ -1,8 +1,59 @@ const { Route, Handler } = require('../service/server'); +const contest = require('../model/contest'); +const user = require('../model/user'); +const training = require('../model/training'); +const { PERM_VIEW_TRAINING, PERM_VIEW_CONTEST, PERM_VIEW_DISCUSSION } = require('../permission'); +const { CONTESTS_ON_MAIN, TRAININGS_ON_MAIN, DISCUSSIONS_ON_MAIN } = require('../options').constants; class HomeHandler extends Handler { + async contest() { + if (this.user.hasPerm(PERM_VIEW_CONTEST)) { + const tdocs = await contest.getMulti() + .limit(CONTESTS_ON_MAIN) + .toArray(); + const tsdict = await contest.getListStatus( + this.user._id, tdocs.map((tdoc) => tdoc._id), + ); + return [tdocs, tsdict]; + } + return [[], {}]; + } + + async training() { + if (this.user.hasPerm(PERM_VIEW_TRAINING)) { + const tdocs = await training.getMulti() + .sort('_id', 1) + .limit(TRAININGS_ON_MAIN) + .toArray(); + const tsdict = await training.getListStatus( + this.user._id, tdocs.map((tdoc) => tdoc._id), + ); + return [tdocs, tsdict]; + } + return [[], {}]; + } + + async discussion() { + // TODO(masnn) + // if (this.user.hasPerm(PERM_VIEW_DISCUSSION)) { + // const ddocs = await discussion.getMulti() + // .limit(DISCUSSIONS_ON_MAIN) + // .toArray(); + // const vndict = await discussion.getListVnodes(map(discussion.node_id, ddocs)); + // return [ddocs, vndict]; + // } + return [[], {}]; + } + async get() { - this.response.body = {}; + const [[tdocs, tsdict], [trdocs, trsdict], [ddocs, vndict]] = await Promise.all([ + this.contest(), this.training(), this.discussion(), + ]); + const udict = await user.getList(ddocs.map((ddoc) => ddoc.owner)); + this.response.template = 'main.html'; + this.response.body = { + tdocs, tsdict, trdocs, trsdict, ddocs, vndict, udict, + }; } } diff --git a/hydro/handler/judge.js b/hydro/handler/judge.js index 879fa1f6a..6d72bd3dd 100644 --- a/hydro/handler/judge.js +++ b/hydro/handler/judge.js @@ -1,6 +1,9 @@ const { PERM_JUDGE } = require('../permission'); const record = require('../model/record'); const problem = require('../model/problem'); +const builtin = require('../model/builtin'); +const contest = require('../model/contest'); +const user = require('../model/user'); const bus = require('../service/bus'); const queue = require('../service/queue'); const { @@ -9,42 +12,66 @@ const { queue.assert('judge'); +async function _postJudge(rdoc) { + const accept = rdoc.status === builtin.STATUS_ACCEPTED; + bus.publish('record_change', rdoc); + const tasks = []; + if (rdoc.tid) { + tasks.push( + contest.updateStatus(rdoc.tid, rdoc.uid, rdoc._id, rdoc.pid, accept, rdoc.score), + ); + } + if (!rdoc.rejudged) { + if (await problem.updateStatus(rdoc.pid, rdoc.uid, rdoc._id, rdoc.status)) { + if (accept) { + tasks.push( + problem.inc(rdoc.pid, 'nAccept', 1), + user.inc(rdoc.uid, 'nAccept', 1), + ); + } + } + } + await Promise.all(tasks); +} + async function next(body) { let rdoc = await record.get(body.rid); const $set = {}; + const $push = {}; if (body.case) { rdoc.testCases.push(body.case); - $set.testCases = rdoc.testCases; + $push.testCases = body.case; } if (body.judge_text) { rdoc.judgeTexts.push(body.judge_text); - $set.judgeTexts = rdoc.judgeTexts; + $push.judgeTexts = body.judge_text; } if (body.compiler_text) { rdoc.compilerTexts.push(body.compiler_text); - $set.compilerTexts = rdoc.compilerTexts; + $push.compilerTexts = body.compiler_text; } if (body.status) $set.status = body.status; if (body.score) $set.score = body.score; if (body.time_ms) $set.time = body.time_ms; if (body.memory_kb) $set.memory = body.memory_kb; - rdoc = await record.update(body.rid, $set); + rdoc = await record.update(body.rid, $set, $push); bus.publish('record_change', rdoc); } async function end(body) { let rdoc = await record.get(body.rid); const $set = {}; + const $push = {}; if (body.case) { rdoc.testCases.push(body.case); - $set.testCases = rdoc.testCases; + $push.testCases = body.case; } if (body.judge_text) { rdoc.judgeTexts.push(body.judge_text); - $set.judgeTexts = rdoc.judgeTexts; + $push.judgeTexts = body.judge_text; } if (body.compiler_text) { rdoc.compilerTexts.push(body.compiler_text); - $set.compilerTexts = rdoc.compilerTexts; + $push.compilerTexts = body.compiler_text; } if (body.status) $set.status = body.status; if (body.score) $set.score = body.score; @@ -52,8 +79,9 @@ async function end(body) { if (body.memory_kb) $set.memory = body.memory_kb; $set.judgeAt = new Date(); $set.judger = body.judger; - rdoc = await record.update(body.rid, $set); - bus.publish('record_change', rdoc); + rdoc = await record.update(body.rid, $set, $push); + await _postJudge(rdoc); + rdoc = await record.update(body.rid, $set, $push); } class JudgeHandler extends Handler { @@ -87,6 +115,7 @@ class JudgeHandler extends Handler { } async postEnd() { + this.request.body.judger = this.user._id; await end(this.request.body); this.response.body = {}; } diff --git a/hydro/handler/problem.js b/hydro/handler/problem.js index 9e2e22e69..72ede39f6 100644 --- a/hydro/handler/problem.js +++ b/hydro/handler/problem.js @@ -19,7 +19,7 @@ const { PERM_CREATE_PROBLEM, PERM_READ_PROBLEM_DATA, PERM_EDIT_PROBLEM, PERM_JUDGE, PERM_VIEW_PROBLEM_SOLUTION, PERM_CREATE_PROBLEM_SOLUTION, PERM_EDIT_PROBLEM_SOLUTION, PERM_DELETE_PROBLEM_SOLUTION, PERM_EDIT_PROBLEM_SOLUTION_REPLY, - PERM_REPLY_PROBLEM_SOLUTION, + PERM_REPLY_PROBLEM_SOLUTION, PERM_LOGGEDIN, } = require('../permission'); queue.assert('judge'); @@ -32,9 +32,13 @@ class ProblemHandler extends Handler { async get({ page = 1, category = null }) { this.response.template = 'problem_main.html'; const q = {}; + let psdict = {}; if (category) q.category = category; if (!this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false; const pdocs = await problem.getMany(q, { pid: 1 }, page, constants.PROBLEM_PER_PAGE); + if (this.user.hasPerm(PERM_LOGGEDIN)) { + psdict = await problem.getListStatus(this.user._id, pdocs.map((pdoc) => pdoc._id)); + } this.response.body = { path: [ ['Hydro', '/'], @@ -42,6 +46,7 @@ class ProblemHandler extends Handler { ], page, pdocs, + psdict, category: '', }; } diff --git a/hydro/handler/record.js b/hydro/handler/record.js index e10e91929..3beb64d37 100644 --- a/hydro/handler/record.js +++ b/hydro/handler/record.js @@ -54,12 +54,14 @@ class RecordDetailHandler extends Handler { if (rdoc.uid !== this.user.uid && !this.user.hasPerm(PERM_READ_RECORD_CODE)) { rdoc.code = null; } + const pdoc = await problem.getById(rdoc.pid); this.response.body = { path: [ ['Hydro', '/'], ['record_detail', null], ], rdoc, + pdoc, show_status: true, }; } diff --git a/hydro/handler/training.js b/hydro/handler/training.js index f21e8101e..8a5650e1b 100644 --- a/hydro/handler/training.js +++ b/hydro/handler/training.js @@ -103,7 +103,7 @@ class TrainingDetailHandler extends TrainingHandler { for (const pid in psdict) { const psdoc = psdict[pid]; if (psdoc.status) { - if (psdoc.status === builtin.status.STATUS_ACCEPTED) donePids.add(pid); + if (psdoc.status === builtin.STATUS_ACCEPTED) donePids.add(pid); else progPids.add(pid); } } diff --git a/hydro/model/problem.js b/hydro/model/problem.js index ce3458777..9fd3f07b0 100644 --- a/hydro/model/problem.js +++ b/hydro/model/problem.js @@ -112,6 +112,12 @@ async function edit(_id, $set) { if (!pdoc) throw new ProblemNotFoundError(_id); return pdoc; } +async function inc(_id, field, n) { + await coll.findOneAndUpdate({ _id }, { $inc: { [field]: n } }); + const pdoc = await getById(_id); + if (!pdoc) throw new ProblemNotFoundError(_id); + return pdoc; +} function count(query) { return coll.find(query).count(); } @@ -134,6 +140,14 @@ async function getListStatus(uid, pids) { for (const psdoc of psdocs) r[psdoc.pid] = psdoc; return r; } +async function updateStatus(pid, uid, rid, status) { + const res = await collStatus.findOneAndUpdate( + { pid, uid }, + { $set: { rid, status } }, + { upsert: true }, + ); + return res.value; +} async function setTestdata(_id, readStream) { const pdoc = await getById(_id); const f = gridfs.openUploadStream('data.zip'); @@ -148,6 +162,7 @@ async function setTestdata(_id, readStream) { module.exports = { add, + inc, get, getMany, edit, @@ -159,4 +174,5 @@ module.exports = { getListStatus, getMultiStatus, setTestdata, + updateStatus, }; diff --git a/hydro/model/record.js b/hydro/model/record.js index a3773f3c7..3bb5ab59d 100644 --- a/hydro/model/record.js +++ b/hydro/model/record.js @@ -39,9 +39,12 @@ function getMany(query, sort, page, limit) { return coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit) .toArray(); } -async function update(rid, $set) { +async function update(rid, $set, $push) { const _id = new ObjectID(rid); - await coll.findOneAndUpdate({ _id }, { $set }); + const upd = {}; + if ($set && Object.keys($set).length) upd.$set = $set; + if ($push && Object.keys($push).length) upd.$push = $push; + await coll.findOneAndUpdate({ _id }, upd); const rdoc = await coll.findOne({ _id }); if (!rdoc) throw new RecordNotFoundError(rid); return rdoc; diff --git a/hydro/model/training.js b/hydro/model/training.js index 12fcca79f..0cbf7b8f8 100644 --- a/hydro/model/training.js +++ b/hydro/model/training.js @@ -6,6 +6,18 @@ const db = require('../service/db.js'); const coll = db.collection('traning'); const collStatus = db.collection('training.status'); +function getStatus(tid, uid) { + return collStatus.findOne({ tid, uid }); +} +function getMultiStatus(query) { + return collStatus.find(query); +} +async function getListStatus(uid, tids) { + const psdocs = await getMultiStatus({ uid, pid: { $in: Array.from(new Set(tids)) } }).toArray(); + const r = {}; + for (const psdoc of psdocs) r[psdoc.pid] = psdoc; + return r; +} async function enroll(tid, uid) { try { await collStatus.insertOne({ tid, uid, enroll: 1 }); @@ -93,8 +105,9 @@ module.exports = { return tdoc; }, getMulti: (query) => coll.find(query), - getMultiStatus: (query) => collStatus.find(query), - getStatus: (tid, uid) => collStatus.findOne({ tid, uid }), + getMultiStatus, + getStatus, enroll, setStatus, + getListStatus, }; diff --git a/hydro/model/user.js b/hydro/model/user.js index 87067115f..ad1a8bac1 100644 --- a/hydro/model/user.js +++ b/hydro/model/user.js @@ -89,6 +89,11 @@ async function changePassword(uid, currentPassword, newPassword) { $set: { salt, hash: pwhash.hash(newPassword, salt) }, }); } +async function inc(_id, field, n) { + await coll.findOneAndUpdate({ _id }, { $inc: { [field]: n } }); + const udoc = await getById(_id); + return udoc; +} async function create({ uid, mail, uname, password, regip = '127.0.0.1', role = 'default', }) { @@ -129,6 +134,7 @@ module.exports = { getById, getByUname, getMany, + inc, setById, setEmail, setPassword, diff --git a/hydro/options.js b/hydro/options.js index f05169e69..2149856d3 100644 --- a/hydro/options.js +++ b/hydro/options.js @@ -43,6 +43,9 @@ let options = { SOLUTION_PER_PAGE: 20, CONTEST_PER_PAGE: 20, TRAINING_PER_PAGE: 10, + CONTESTS_ON_MAIN: 5, + TRAININGS_ON_MAIN: 5, + DISCUSSIONS_ON_MAIN: 20, }, }; diff --git a/templates/components/problem.html b/templates/components/problem.html index 2954d33e5..5c7feba2f 100644 --- a/templates/components/problem.html +++ b/templates/components/problem.html @@ -1,10 +1,10 @@ {% macro render_problem_title(pdoc, tdoc=none, show_tags=true, show_invisible_flag=true, rp=none, invalid=false) %} {%- if not invalid %} {%- endif %} diff --git a/templates/discussion_nodes_widget.html b/templates/discussion_nodes_widget.html index d224338e9..516b771e5 100644 --- a/templates/discussion_nodes_widget.html +++ b/templates/discussion_nodes_widget.html @@ -3,7 +3,7 @@

{{ _('Discussion Nodes') }}

{% endif %} - {% if not trdocs %} - - {% else %} + {% if trdocs.length %}

{{ _('Training') }}

- {% with tdocs=trdocs, tsdict=trsdict %} + {% set tdocs = trdocs %} + {% set tsdict = trsdict %}
    {% for tdoc in tdocs %}
  1. @@ -76,19 +74,19 @@
-

{{ tdoc.title }}

+

{{ tdoc.title }}

{{ tdoc['content'] }}

{% endif %} - {% if not ddocs %} - - {% else %} + {% if ddocs.length %}

{{ _('Discussion') }}

@@ -119,9 +114,8 @@
{% if handler.hasPerm(perm.PERM_VIEW_DISCUSSION) %} - {% include 'discussion_nodes_widget.html' %} + {#{% include 'discussion_nodes_widget.html' %} // TODO(masnn) #} {% endif %} - {% if domain_id == vj4.model.builtin.DOMAIN_ID_SYSTEM %}

{{ _('Recommended') }}

@@ -153,7 +147,6 @@
- {% endif %}
{% endblock %}