diff --git a/hydro/handler/contest.js b/hydro/handler/contest.js index c13d2b8c8..29116e155 100644 --- a/hydro/handler/contest.js +++ b/hydro/handler/contest.js @@ -53,8 +53,9 @@ class ContestDetailHandler extends ContestHandler { contest.getStatus(domainId, this.tdoc.docId, this.user._id), problem.getList(domainId, this.tdoc.pids), ]); - const psdict = {}; let rdict = {}; let - attended; + const psdict = {}; + let rdict = {}; + let attended; if (tsdoc) { attended = tsdoc.attend === 1; for (const pdetail of tsdoc.journal || []) psdict[pdetail.pid] = pdetail; @@ -242,7 +243,10 @@ class ContestProblemSubmitHandler extends ContestProblemHandler { code, hidden: true, }); - await contest.updateStatus(domainId, this.tdoc.docId, this.user._id, rid, this.pdoc.docId, false, 0); + await Promise.all([ + record.judge(domainId, rid), + contest.updateStatus(domainId, this.tdoc.docId, this.user._id, rid, this.pdoc.docId), + ]); if (!this.canShowRecord(this.tdoc)) { this.response.body = { tid }; this.response.redirect = `/c/${tid}`; diff --git a/hydro/handler/judge.js b/hydro/handler/judge.js index 11233b78c..4ae3ab00e 100644 --- a/hydro/handler/judge.js +++ b/hydro/handler/judge.js @@ -35,6 +35,8 @@ async function next(body) { const $set = {}; const $push = {}; if (body.case) { + body.case.memory = body.case.memory_kb; + body.case.time = body.case.time_ms; rdoc.testCases.push(body.case); $push.testCases = body.case; } @@ -58,10 +60,6 @@ async function end(body) { let rdoc = await record.get(body.domainId, body.rid); const $set = {}; const $push = {}; - if (body.case) { - rdoc.testCases.push(body.case); - $push.testCases = body.case; - } if (body.judge_text) { rdoc.judgeTexts.push(body.judge_text); $push.judgeTexts = body.judge_text; diff --git a/hydro/handler/manage.js b/hydro/handler/manage.js index 881aa6956..ac24bbc46 100644 --- a/hydro/handler/manage.js +++ b/hydro/handler/manage.js @@ -1,5 +1,5 @@ const user = require('../model/user'); -const system = require('../model/system'); +const domain = require('../model/domain'); const setting = require('../model/setting'); const { Route, Handler } = require('../service/server'); const { PERM_MANAGE } = require('../permission'); @@ -10,7 +10,7 @@ const { RoleAlreadyExistError, ValidationError } = require('../error'); class ManageHandler extends Handler { async prepare({ domainId }) { this.checkPerm(PERM_MANAGE); - this.system = await user.getById(domainId, 0); + this.domain = await domain.get(domainId); } } @@ -28,7 +28,7 @@ class ManageDashboardHandler extends ManageHandler { ['manage_dashboard', null], ]; this.response.template = 'manage_dashboard.html'; - this.response.body = { system: this.system, path }; + this.response.body = { domain: this.domain, path }; } } @@ -40,14 +40,13 @@ class ManageEditHandler extends ManageHandler { ['manage_edit', null], ]; this.response.template = 'manage_edit.html'; - this.response.body = { system: this.system, path }; + this.response.body = { domain: this.domain, path }; } - async post({ uname, gravatar, bio }) { - const unameLower = uname.trim().toLowerCase(); - await user.setById(0, { - uname, unameLower, gravatar, bio, - }); + async post({ + domainId, name, gravatar, bulletin, + }) { + await domain.edit(domainId, { name, gravatar, bulletin }); this.response.redirect = '/manage/dashboard'; } } @@ -56,10 +55,9 @@ class ManageUserHandler extends ManageHandler { async get({ domainId }) { const uids = []; const rudocs = {}; - const [udocs, roles, sys] = await Promise.all([ + const [udocs, roles] = await Promise.all([ user.getMulti(domainId, { role: { $nin: ['default', 'guest'] } }).toArray(), user.getRoles(domainId), - user.getById(domainId, 0), ]); for (const role of roles) rudocs[role._id] = []; for (const udoc of udocs) { diff --git a/hydro/handler/training.js b/hydro/handler/training.js index 4d708608a..39b0482e7 100644 --- a/hydro/handler/training.js +++ b/hydro/handler/training.js @@ -12,7 +12,7 @@ const user = require('../model/user'); const system = require('../model/system'); const { Route, Handler } = require('../service/server'); -async function _parseDagJson(dag) { +async function _parseDagJson(domainId, dag) { const parsed = []; try { dag = JSON.parse(dag); @@ -30,7 +30,7 @@ async function _parseDagJson(dag) { } for (const i in node.pids) { // eslint-disable-next-line no-await-in-loop - const pdoc = await problem.get(node.pids[i]); // FIXME no-await-in-loop + const pdoc = await problem.get(domainId, node.pids[i]); // FIXME no-await-in-loop assert(pdoc, `Problem not found: ${node.pids[i]}`); node.pids[i] = pdoc.docId; } @@ -43,7 +43,7 @@ async function _parseDagJson(dag) { parsed.push(newNode); } } catch (e) { - throw new ValidationError('dag'); + throw new ValidationError('dag', [e.message]); } return parsed; } @@ -55,10 +55,10 @@ class TrainingHandler extends Handler { } class TrainingMainHandler extends TrainingHandler { - async get({ sort, page }) { + async get({ domainId, sort, page }) { const qs = sort ? 'sort={0}'.format(sort) : ''; const [tdocs, tpcount] = await paginate( - training.getMulti().sort('_id', 1), + training.getMulti(domainId).sort('_id', 1), page, await system.get('TRAINING_PER_PAGE'), ); @@ -68,16 +68,18 @@ class TrainingMainHandler extends TrainingHandler { let tdict = {}; if (this.user.hasPerm(PERM_LOGGEDIN)) { const enrolledTids = new Set(); - const tsdocs = await training.getMultiStatus({ + const tsdocs = await training.getMultiStatus(domainId, { uid: this.user._id, - $or: [{ _id: { $in: Array.from(tids) } }, { enroll: 1 }], + $or: [{ docId: { $in: Array.from(tids) } }, { enroll: 1 }], }).toArray(); for (const tsdoc of tsdocs) { - tsdict[tsdoc.tid] = tsdoc; - enrolledTids.add(tsdoc.tid); + tsdict[tsdoc.docId] = tsdoc; + enrolledTids.add(tsdoc.docId); } for (const tid of tids) enrolledTids.delete(tid); - if (enrolledTids.size) tdict = await training.getList(Array.from(enrolledTids)); + if (enrolledTids.size) { + tdict = await training.getList(domainId, Array.from(enrolledTids)); + } } for (const tdoc in tdocs) tdict[tdoc.docId] = tdoc; const path = [ @@ -101,14 +103,14 @@ class TrainingDetailHandler extends TrainingHandler { user.getById(domainId, tdoc.owner), problem.getList(domainId, pids, f), ]); - const psdict = await problem.getListStatus(domainId, this.user._id, Object.keys(pdict)); + const psdict = await problem.getListStatus(domainId, this.user._id, pids); const donePids = new Set(); const progPids = new Set(); for (const pid in psdict) { const psdoc = psdict[pid]; if (psdoc.status) { - if (psdoc.status === builtin.STATUS_ACCEPTED) donePids.add(pid); - else progPids.add(pid); + if (psdoc.status === builtin.STATUS.STATUS_ACCEPTED) donePids.add(parseInt(pid)); + else progPids.add(parseInt(pid)); } } const nsdict = {}; @@ -117,7 +119,7 @@ class TrainingDetailHandler extends TrainingHandler { for (const node of tdoc.dag) { ndict[node._id] = node; const totalCount = node.pids.length; - const doneCount = Set.union(new Set(node.pids), new Set(donePids)).size; + const doneCount = Set.union(new Set(node.pids), donePids).size; const nsdoc = { progress: totalCount ? parseInt(100 * (doneCount / totalCount)) : 100, isDone: training.isDone(node, doneNids, donePids), @@ -168,18 +170,18 @@ class TrainingCreateHandler extends TrainingHandler { } async post({ - title, content, dag, description, + domainId, title, content, dag, description, }) { - dag = await _parseDagJson(dag); + dag = await _parseDagJson(domainId, dag); const pids = training.getPids({ dag }); assert(pids.length, new ValidationError('dag')); - const pdocs = await problem.getMulti({ - $or: [{ _id: { $in: pids } }, { pid: { $in: pids } }], + const pdocs = await problem.getMulti(domainId, { + $or: [{ docId: { $in: pids } }, { pid: { $in: pids } }], }).sort('_id', 1).toArray(); const existPids = pdocs.map((pdoc) => pdoc.docId); const existPnames = pdocs.map((pdoc) => pdoc.pid); if (pids.length !== existPids.length) { - for (const pid in pids) { + for (const pid of pids) { assert( existPids.includes(pid) || existPnames.includes(pid), new ProblemNotFoundError(pid), @@ -189,15 +191,15 @@ class TrainingCreateHandler extends TrainingHandler { for (const pdoc in pdocs) { if (pdoc.hidden) this.checkPerm(PERM_VIEW_PROBLEM_HIDDEN); } - const tid = await training.add(title, content, this.user._id, dag, description); + const tid = await training.add(domainId, title, content, this.user._id, dag, description); this.response.body = { tid }; this.response.redirect = `/t/${tid}`; } } class TrainingEditHandler extends TrainingHandler { - async get({ tid }) { - const tdoc = await training.get(tid); + async get({ domainId, tid }) { + const tdoc = await training.get(domainId, tid); if (tdoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_TRAINING); const dag = JSON.stringify(tdoc.dag, null, 2); const path = [ @@ -212,16 +214,16 @@ class TrainingEditHandler extends TrainingHandler { } async post({ - tid, title, content, dag, description, + domainId, tid, title, content, dag, description, }) { - const tdoc = await training.get(tid); + const tdoc = await training.get(domainId, tid); if (!this.user._id === tdoc.owner) this.checkPerm(PERM_EDIT_TRAINING); - dag = await _parseDagJson(dag); + dag = await _parseDagJson(domainId, dag); const pids = training.getPids({ dag }); assert(pids.length, new ValidationError('dag')); - const pdocs = await problem.getMulti({ + const pdocs = await problem.getMulti(domainId, { $or: [ - { _id: { $in: pids } }, + { docId: { $in: pids } }, { pid: { $in: pids } }, ], }).sort('_id', 1).toArray(); @@ -235,8 +237,10 @@ class TrainingEditHandler extends TrainingHandler { ); } } - for (const pdoc in pdocs) { if (pdoc.hidden) this.checkPerm(PERM_VIEW_PROBLEM_HIDDEN); } - await training.edit(tid, { + for (const pdoc in pdocs) { + if (pdoc.hidden) this.checkPerm(PERM_VIEW_PROBLEM_HIDDEN); + } + await training.edit(domainId, tid, { title, content, dag, description, }); this.response.body = { tid }; diff --git a/hydro/lib/rank.js b/hydro/lib/rank.js index 7b5d287be..92ae1697e 100644 --- a/hydro/lib/rank.js +++ b/hydro/lib/rank.js @@ -10,4 +10,4 @@ function* ranked(diter, equ = (a, b) => a === b) { } } -global.Hydro['lib.rank'] = module.exports = ranked; +global.Hydro.lib.rank = module.exports = ranked; diff --git a/hydro/model/contest.js b/hydro/model/contest.js index 01547f284..5b4cb4876 100644 --- a/hydro/model/contest.js +++ b/hydro/model/contest.js @@ -119,6 +119,7 @@ const acm = { } return rows; }, + rank: (tdocs) => ranked(tdocs, (a, b) => a.score === b.score), }; const oi = { @@ -251,16 +252,17 @@ async function get(domainId, tid) { if (!tdoc) throw new ContestNotFoundError(tid); return tdoc; } -async function updateStatus(domainId, tid, uid, rid, pid, accept, score) { + +async function updateStatus(domainId, tid, uid, rid, pid, accept = false, score = 0) { await get(domainId, tid); const tsdoc = await document.revPushStatus(domainId, document.TYPE_CONTEST, tid, uid, 'journal', { rid, pid, accept, score, }); - if (!tsdoc.value.attend) throw new ContestNotAttendedError(tid, uid); + if (!tsdoc.attend) throw new ContestNotAttendedError(tid, uid); } function getStatus(domainId, tid, uid) { - return document.getStatus(domainId, tid, uid); + return document.getStatus(domainId, document.TYPE_CONTEST, tid, uid); } async function getListStatus(domainId, uid, tids) { @@ -276,7 +278,7 @@ async function attend(domainId, tid, uid) { } catch (e) { throw new ContestAlreadyAttendedError(tid, uid); } - await document.inc(domainId, document, document.TYPE_CONTEST, tid, 'attend', 1); + await document.inc(domainId, document.TYPE_CONTEST, tid, 'attend', 1); return {}; } @@ -330,7 +332,7 @@ const ContestHandlerMixin = (c) => class extends c { const uids = []; for (const tsdoc of tsdocs) uids.push(tsdoc.uid); const [udict, pdict] = await Promise.all([ - user.getList(uids), + user.getList(domainId, uids), problem.getList(domainId, tdoc.pids), ]); const rankedTsdocs = RULES[tdoc.rule].rank(tsdocs); @@ -363,8 +365,8 @@ global.Hydro.model.contest = module.exports = { get, updateStatus, getStatus, - count: (query) => document.count({ ...query, docType: document.TYPE_CONTEST }), - getMulti: (query = {}) => document.getMulti({ ...query, docType: document.TYPE_CONTEST }), + count: (domainId, query) => document.count(domainId, document.TYPE_CONTEST, query), + getMulti: (domainId, query = {}) => document.getMulti(domainId, document.TYPE_CONTEST, query), setStatus, isNew, isUpcoming, diff --git a/hydro/model/document.js b/hydro/model/document.js index 556bb8c8a..1d5a38d1b 100644 --- a/hydro/model/document.js +++ b/hydro/model/document.js @@ -18,7 +18,7 @@ const TYPE_FILE = 50; const TYPE_HOMEWORK = 60; async function add( - domainId, content, owner, docType, docId, + domainId, content, owner, docType, docId = null, parentType = null, parentId = null, args = {}, ) { const _id = new ObjectID(); @@ -210,7 +210,7 @@ async function cappedIncStatus( { domainId, docType, docId, uid, [key]: { $not }, }, - { $inc: { key: value } }, + { $inc: { [key]: value } }, { upsert: true, returnOriginal: false }, ); return res.value; diff --git a/hydro/model/training.js b/hydro/model/training.js index f1184dd02..6e603b992 100644 --- a/hydro/model/training.js +++ b/hydro/model/training.js @@ -1,7 +1,5 @@ -const assert = require('assert'); const document = require('./document'); -const validator = require('../lib/validator'); -const { ValidationError, TrainingNotFoundError, TrainingAlreadyEnrollError } = require('../error'); +const { TrainingNotFoundError, TrainingAlreadyEnrollError } = require('../error'); function getStatus(domainId, tid, uid) { return document.getStatus(domainId, document.TYPE_TRAINING, tid, uid); @@ -34,14 +32,6 @@ function setStatus(domainId, tid, uid, $set) { } function add(domainId, title, content, owner, dag = [], description = '') { - validator.checkTitle(title); - validator.checkIntro(content); - validator.checkDescription(description); - for (const node of dag) { - for (const nid of node.requireNids) { - if (nid >= node._id) throw new ValidationError('dag'); - } - } return document.add(domainId, content, owner, document.TYPE_TRAINING, null, null, null, { dag, title, @@ -51,16 +41,6 @@ function add(domainId, title, content, owner, dag = [], description = '') { } function edit(domainId, tid, $set) { - if ($set.title) validator.checkTitle($set.title); - if ($set.content) validator.checkIntro($set.content); - if ($set.desc) validator.checkDescription($set.description); - if ($set.dag) { - for (const node of $set.dag) { - for (const nid of node.requireNids) { - assert(nid >= node._id, new ValidationError('dag')); - } - } - } return document.set(domainId, document.TYPE_TRAINING, tid, $set); } @@ -101,6 +81,13 @@ global.Hydro.model.training = module.exports = { async get(domainId, tid) { const tdoc = await document.get(domainId, document.TYPE_TRAINING, tid); if (!tdoc) throw new TrainingNotFoundError(tid); + for (const i in tdoc.dag) { + for (const j in tdoc.dag[i].pids) { + if (Number.isSafeInteger(parseInt(tdoc.dag[i].pids[j]))) { + tdoc.dag[i].pids[j] = parseInt(tdoc.dag[i].pids[j]); + } + } + } return tdoc; }, async getList(domainId, tids) { diff --git a/hydro/model/user.js b/hydro/model/user.js index 6d35e70e3..fc4ecb360 100644 --- a/hydro/model/user.js +++ b/hydro/model/user.js @@ -47,7 +47,7 @@ async function getPerm(domainId, udoc) { return p.content; } const role = await document.getStatus(domainId, document.TYPE_DOMAIN_ROLE, 0, udoc._id); - const p = await document.get(domainId, document.TYPE_DOMAIN_ROLE, role || 'default'); + const p = await document.get(domainId, document.TYPE_DOMAIN_ROLE, role.role || 'default'); return p.content; } @@ -159,8 +159,13 @@ function setRole(domainId, uid, role) { return document.setStatus(domainId, document.TYPE_DOMAIN_ROLE, 0, uid, { role }); } -function getRoles(domainId) { - return document.getMulti(domainId, document.TYPE_DOMAIN_ROLE).sort('_id', 1).toArray(); +async function getRoles(domainId) { + const docs = await document.getMulti(domainId, document.TYPE_DOMAIN_ROLE).sort('_id', 1).toArray(); + const roles = []; + for (const doc of docs) { + roles.push({ _id: doc.docId, perm: doc.content }); + } + return roles; } function getRole(domainId, name) { diff --git a/hydro/service/setup.js b/hydro/service/setup.js index 4b1f131d4..448048ab7 100644 --- a/hydro/service/setup.js +++ b/hydro/service/setup.js @@ -68,6 +68,7 @@ async function setup() { const db = Database.db(name); // Check Write Access await db.collection('test').insertOne({ test: 'test' }); + await db.collection('test').deleteOne({ test: 'test' }); fs.writeFileSync(path.resolve(process.cwd(), 'config.json'), JSON.stringify({ host, port, name, username, password, })); diff --git a/module/migrate-vijos/script.js b/module/migrate-vijos/script.js index 8bfc0c0d9..6791e47b8 100644 --- a/module/migrate-vijos/script.js +++ b/module/migrate-vijos/script.js @@ -173,9 +173,9 @@ const tasks = { testCases.push({ status: c.status, score: c.score, - time: c.time, - memory: c.memory, - judge_text: c.judge_text, + time: c.time_ms || c.time, + memory: c.memory_kb || c.memory, + judge_text: (c.judge_text || '') + (c.message || ''), }); } return { diff --git a/templates/record_detail_status.html b/templates/record_detail_status.html index ff2aa78c2..57a3d9535 100644 --- a/templates/record_detail_status.html +++ b/templates/record_detail_status.html @@ -49,7 +49,7 @@ {% if rcdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rcdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rcdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rcdoc.time }}ms - {% if rcdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rcdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rcdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ size(rcdoc.memory_kb, 1024) }} + {% if rcdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rcdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rcdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ size(rcdoc.memory, 1024) }} {% endfor %} diff --git a/templates/training_main.html b/templates/training_main.html index 6980800de..2c8cabbd7 100644 --- a/templates/training_main.html +++ b/templates/training_main.html @@ -33,7 +33,7 @@ {% if tsdict[tdoc.docId]['enroll'] %} {% if not tsdict[tdoc.docId]['done'] %} - {{ _('Completed') }} {{ (100 * tsdict[tdoc.docId]['done_pids']|length / model.training.getPids(tdoc)|length)|round|int }}% + {{ _('Completed') }} {{ (100 * tsdict[tdoc.docId]['donePids']|length / model.training.getPids(tdoc)|length)|round|int }}% {% else %} {{ _('Completed') }} 100% @@ -67,8 +67,8 @@
-

{{ tdict[tsdoc['tid']]['title'] }}

-
{{ _('Complete') }} {{ (100 * tsdoc['done_pids']|length / handler.get_pids(tdict[tsdoc['doc_id']])|length)|round|int }}%
+

{{ tdict[tsdoc['docId']]['title'] }}

+
{{ _('Complete') }} {{ (100 * tsdoc['donePids']|length / handler.getPids(tdict[tsdoc.docId])|length)|round|int }}%
{% endif %} diff --git a/ui/components/scratchpad/ScratchpadRecordsRowContainer.js b/ui/components/scratchpad/ScratchpadRecordsRowContainer.js index 8c8dceed8..b3d39de72 100644 --- a/ui/components/scratchpad/ScratchpadRecordsRowContainer.js +++ b/ui/components/scratchpad/ScratchpadRecordsRowContainer.js @@ -75,7 +75,7 @@ export default class ScratchpadRecordsRowContainer extends React.PureComponent { {getRecordDetail(data)} - {shouldShowDetail(data) ? `${Math.ceil(data.memory_kb / 1000)} MB` : '-'} + {shouldShowDetail(data) ? `${Math.ceil(data.memory / 1000)} MB` : '-'} {shouldShowDetail(data) ? `${(data.time_ms / 1000).toFixed(1)}s` : '-'}