题目编辑,测试数据上传,评测记录

This commit is contained in:
masnn committed 2020-04-09 21:43:16 +08:00
1 parent 6e28dd868e
commit f055d79fef
9 files changed
+76 -32

No files matched your search

-1
View File
@@ -1,7 +1,6 @@
class UserFacingError extends Error { class UserFacingError extends Error {
constructor(type) { constructor(type) {
super(type); super(type);
this.template = 'error';
this.code = 500; this.code = 500;
//this.stack = ''; //this.stack = '';
this.params = []; this.params = [];
+5 -2
View File
@@ -2,6 +2,7 @@ const
{ requirePerm } = require('./tools'), { requirePerm } = require('./tools'),
{ PERM_JUDGE } = require('../permission'), { PERM_JUDGE } = require('../permission'),
record = require('../model/record'), record = require('../model/record'),
problem = require('../model/problem'),
bus = require('../service/bus'), bus = require('../service/bus'),
queue = require('../service/queue'), queue = require('../service/queue'),
{ GET, POST } = require('../service/server'); { GET, POST } = require('../service/server');
@@ -15,11 +16,12 @@ GET('/judge/fetch', requirePerm(PERM_JUDGE), async ctx => {
let rid = await queue.get('judge', false); let rid = await queue.get('judge', false);
if (rid) { if (rid) {
let rdoc = await record.get(rid); let rdoc = await record.get(rid);
let pdoc = await problem.getById(rdoc.pid);
let task = { let task = {
event: 'judge', event: 'judge',
rid, type: 0, rid, type: 0,
pid: rdoc.pid, pid: rdoc.pid,
data: rdoc.data, data: pdoc.data,
lang: rdoc.lang, lang: rdoc.lang,
code: rdoc.code code: rdoc.code
}; };
@@ -28,6 +30,7 @@ GET('/judge/fetch', requirePerm(PERM_JUDGE), async ctx => {
else ctx.body = {}; else ctx.body = {};
}); });
POST('/judge/next', requirePerm(PERM_JUDGE), async ctx => { POST('/judge/next', requirePerm(PERM_JUDGE), async ctx => {
console.log(ctx.request.body);
let body = ctx.request.body; let body = ctx.request.body;
let rdoc = await record.get(body.rid); let rdoc = await record.get(body.rid);
let $set = {}; let $set = {};
@@ -40,7 +43,7 @@ POST('/judge/next', requirePerm(PERM_JUDGE), async ctx => {
$set.judgeTexts = rdoc.judgeTexts; $set.judgeTexts = rdoc.judgeTexts;
} }
if (body.compiler_text) { if (body.compiler_text) {
rdoc.compilerTexts.push(body.compilerTexts); rdoc.compilerTexts.push(body.compiler_text);
$set.compilerTexts = rdoc.compilerTexts; $set.compilerTexts = rdoc.compilerTexts;
} }
if (body.status) $set.status = body.status; if (body.status) $set.status = body.status;
+33 -5
View File
@@ -80,11 +80,12 @@ GET('/p/:pid/submit', requirePerm(PERM_SUBMIT_PROBLEM), async ctx => {
}; };
}); });
POST('/p/:pid/submit', requirePerm(PERM_SUBMIT_PROBLEM), async ctx => { POST('/p/:pid/submit', requirePerm(PERM_SUBMIT_PROBLEM), async ctx => {
let pdoc = await problem.get({ pid: ctx.params.pid });
let rid = await record.add({ let rid = await record.add({
uid: ctx.state.user._id, uid: ctx.state.user._id,
lang: ctx.request.body.lang, lang: ctx.request.body.lang,
code: ctx.request.body.code, code: ctx.request.body.code,
pid: ctx.params.pid pid: pdoc._id
}); });
await queue.push('judge', rid); await queue.push('judge', rid);
ctx.state.user.nSubmit++; ctx.state.user.nSubmit++;
@@ -94,7 +95,7 @@ POST('/p/:pid/submit', requirePerm(PERM_SUBMIT_PROBLEM), async ctx => {
GET('/p/:pid/settings', async ctx => { GET('/p/:pid/settings', async ctx => {
ctx.templateName = 'problem_settings.html'; ctx.templateName = 'problem_settings.html';
let pdoc = await problem.get({ pid: ctx.params.pid, uid: ctx.state.user._id }); let pdoc = await problem.get({ pid: ctx.params.pid, uid: ctx.state.user._id });
if (pdoc.hidden) ctx.checkPerm(PERM_VIEW_PROBLEM_HIDDEN); if (pdoc.owner != ctx.state.user._id) ctx.checkPerm(PERM_EDIT_PROBLEM);
ctx.body = { ctx.body = {
path: [ path: [
['Hydro', '/'], ['Hydro', '/'],
@@ -109,12 +110,36 @@ POST('/p/:pid/settings', async ctx => {
// TODO(masnn) // TODO(masnn)
ctx.back(); ctx.back();
}); });
GET('/p/:pid/edit', async ctx => {
ctx.templateName = 'problem_edit.html';
let pdoc = await problem.get({ pid: ctx.params.pid, uid: ctx.state.user._id });
if (pdoc.owner != ctx.state.user._id) ctx.checkPerm(PERM_EDIT_PROBLEM);
ctx.body = {
path: [
['Hydro', '/'],
['problem_main', '/p'],
[pdoc.title, `/p/${ctx.params.pid}`, true],
['problem_edit', null]
],
pdoc, page_name: 'problem_edit'
};
});
POST('/p/:pid/edit', async ctx => {
let title = validator.checkTitle(ctx.request.body.title);
let content = validator.checkContent(ctx.request.body.content);
let pid = validator.checkPid(ctx.request.body.pid);
let pdoc = await problem.get({ pid: ctx.params.pid });
await problem.edit(pdoc._id, {
title, content, pid
});
ctx.setRedirect = `/p/${pid}`;
});
GET('/p/:pid/upload', async ctx => { GET('/p/:pid/upload', async ctx => {
ctx.templateName = 'problem_upload.html'; ctx.templateName = 'problem_upload.html';
let pdoc = await problem.get({ pid: ctx.params.pid, uid: ctx.state.user._id }); let pdoc = await problem.get({ pid: ctx.params.pid, uid: ctx.state.user._id });
if (pdoc.owner != ctx.state.user._id) ctx.checkPerm(PERM_EDIT_PROBLEM); if (pdoc.owner != ctx.state.user._id) ctx.checkPerm(PERM_EDIT_PROBLEM);
let md5; let md5;
if (typeof pdoc.data == 'object') { if (pdoc.data && typeof pdoc.data == 'object') {
let files = await gridfs.find({ _id: pdoc.data }).toArray(); let files = await gridfs.find({ _id: pdoc.data }).toArray();
md5 = files[0].md5; md5 = files[0].md5;
} }
@@ -133,11 +158,14 @@ POST('/p/:pid/upload', async ctx => {
f.once('error', reject); f.once('error', reject);
}); });
let md5; let md5;
if (typeof pdoc.data == 'object') { if (pdoc.data && typeof pdoc.data == 'object') {
gridfs.delete(pdoc.data);
}
pdoc = await problem.edit(pdoc._id, { data: f.id });
if (pdoc.data && typeof pdoc.data == 'object') {
let files = await gridfs.find({ _id: pdoc.data }).toArray(); let files = await gridfs.find({ _id: pdoc.data }).toArray();
md5 = files[0].md5; md5 = files[0].md5;
} }
pdoc = await problem.edit(ctx.params.pid, { data: f.id });
ctx.body = { pdoc, md5 }; ctx.body = { pdoc, md5 };
}); });
GET('/p/:pid/data', async ctx => { GET('/p/:pid/data', async ctx => {
+4 -1
View File
@@ -128,7 +128,10 @@ MIDDLEWARE(async (ctx, next) => {
} else { } else {
throw new NotFoundError(); throw new NotFoundError();
} }
} catch (error) { } catch (e) {
let error;
if (ctx.body && ctx.body.error) error = ctx.body.error;
else error = e;
if (error instanceof NotFoundError) ctx.status = 404; if (error instanceof NotFoundError) ctx.status = 404;
if (error.toString().startsWith('NotFoundError')) console.log(error); if (error.toString().startsWith('NotFoundError')) console.log(error);
if (error.toString().startsWith('Template render error')) throw error; if (error.toString().startsWith('Template render error')) throw error;
+8 -8
View File
@@ -7,21 +7,21 @@ const
const const
isTitle = s => s && s.length < 64, isTitle = s => s && s.length < 64,
checkTitle = s => { if (!isTitle(s)) throw new ValidationError('title'); }, checkTitle = s => { if (!isTitle(s)) throw new ValidationError('title'); else return s; },
isUid = s => RE_UID.test(s), isUid = s => RE_UID.test(s),
checkUid = s => { if (!isUid(s)) throw new ValidationError('uid'); }, checkUid = s => { if (!isUid(s)) throw new ValidationError('uid'); else return s; },
isUname = s => RE_UNAME.test(s), isUname = s => RE_UNAME.test(s),
checkUname = s => { if (!isUname(s)) throw new ValidationError('uname'); }, checkUname = s => { if (!isUname(s)) throw new ValidationError('uname'); else return s; },
isPassword = s => s.length >= 5, isPassword = s => s.length >= 5,
checkPassword = s => { if (!isPassword(s)) throw new ValidationError('password'); }, checkPassword = s => { if (!isPassword(s)) throw new ValidationError('password'); else return s; },
isEmail = s => RE_MAIL.test(s), isEmail = s => RE_MAIL.test(s),
checkEmail = s => { if (!isEmail(s)) throw new ValidationError('mail'); }, checkEmail = s => { if (!isEmail(s)) throw new ValidationError('mail'); else return s; },
isContent = s => s && s.length < 65536, isContent = s => s && s.length < 65536,
checkContent = s => { if (!isContent(s)) throw new ValidationError('content'); }, checkContent = s => { if (!isContent(s)) throw new ValidationError('content'); else return s; },
isName = s => s && s.length < 256, isName = s => s && s.length < 256,
checkName = s => { if (!isName(s)) throw new ValidationError('name'); }, checkName = s => { if (!isName(s)) throw new ValidationError('name'); else return s; },
isPid = s => RE_PID.test(s.toString()), isPid = s => RE_PID.test(s.toString()),
checkPid = s => { if (!RE_PID.test(s)) throw new ValidationError('pid'); }; checkPid = s => { if (!RE_PID.test(s)) throw new ValidationError('pid'); else return s; };
module.exports = { module.exports = {
isTitle, checkTitle, isTitle, checkTitle,
+22 -11
View File
@@ -1,6 +1,7 @@
const const
validator = require('../lib/validator'), { ObjectID } = require('bson'),
{ ProblemNotFoundError } = require('../error'), { ProblemNotFoundError } = require('../error'),
validator = require('../lib/validator'),
db = require('../service/db.js'), db = require('../service/db.js'),
coll = db.collection('problem'), coll = db.collection('problem'),
coll_status = db.collection('problem.status'); coll_status = db.collection('problem.status');
@@ -42,23 +43,32 @@ async function add({
return pid; return pid;
} }
async function get({ pid, uid }) { async function get({ pid, uid }) {
pid = parseInt(pid) || pid; let query = {};
let pdoc = await coll.findOne({ pid }); if (pid.generationTime || pid.length == 24) query = { _id: new ObjectID(pid) };
else query = { pid: parseInt(pid) || pid };
let pdoc = await coll.findOne(query);
if (!pdoc) throw new ProblemNotFoundError(pid); if (!pdoc) throw new ProblemNotFoundError(pid);
pdoc.psdoc = uid ? if (uid) {
await coll_status.findOne({ pid, uid }) : query.uid = uid;
null; pdoc.psdoc = await coll_status.findOne(query);
}
return pdoc;
}
async function getById(_id) {
_id = new ObjectID(_id);
let pdoc = await coll.findOne({ _id });
if (!pdoc) throw new ProblemNotFoundError(_id);
return pdoc; return pdoc;
} }
async function getMany(query, sort, page, limit) { async function getMany(query, sort, page, limit) {
return await coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit).toArray(); return await coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit).toArray();
} }
async function edit(pid, $set) { async function edit(_id, $set) {
if ($set.title) validator.checkTitle($set.title); if ($set.title) validator.checkTitle($set.title);
if ($set.content) validator.checkContent($set.content); if ($set.content) validator.checkContent($set.content);
await coll.findOneAndUpdate({ pid }, { $set }); await coll.findOneAndUpdate({ _id }, { $set });
let pdoc = await coll.findOne({ pid }); let pdoc = await getById(_id);
if (!pdoc) throw new ProblemNotFoundError(pid); if (!pdoc) throw new ProblemNotFoundError(_id);
return pdoc; return pdoc;
} }
async function count(query) { async function count(query) {
@@ -80,5 +90,6 @@ module.exports = {
getMany, getMany,
edit, edit,
count, count,
random random,
getById
}; };
+1 -1
View File
@@ -8,7 +8,7 @@
/ {% if c[1] %}<a href="{{ c[1] }}">{{ c[0] if c[2] else _(c[0]) }}</a>{% endif %} / {% if c[1] %}<a href="{{ c[1] }}">{{ c[0] if c[2] else _(c[0]) }}</a>{% endif %}
{%- endfor %} {%- endfor %}
</div> </div>
<h1 class="location-current" data-emoji-enabled>{{ path[path.length - 1][0] if path[path.length - 1][0] else _(path[path.length - 1][0]) }}</h1> <h1 class="location-current" data-emoji-enabled>{{ path[path.length - 1][0] if path[path.length - 1][2] else _(path[path.length - 1][0]) }}</h1>
</div> </div>
<div class="media__right"> <div class="media__right">
{% include "partials/hamburger.html" %} {% include "partials/hamburger.html" %}
+1 -1
View File
@@ -3,7 +3,7 @@
{% import "components/problem.html" as problem with context %} {% import "components/problem.html" as problem with context %}
{% import "components/nothing.html" as nothing with context %} {% import "components/nothing.html" as nothing with context %}
<div data-fragment-id="problem_list"> <div data-fragment-id="problem_list">
{% if not pdocs %} {% if not pdocs.length %}
{{ nothing.render('Sorry, there is no problem in the problem set') }} {{ nothing.render('Sorry, there is no problem in the problem set') }}
{% else %} {% else %}
<table class="data-table"> <table class="data-table">
+2 -2
View File
@@ -4,8 +4,8 @@
{% if (typeof(pdoc.data) == 'string') %} {% if (typeof(pdoc.data) == 'string') %}
<p><b>{{ _('Current dataset comes from') }} <a href="{{ pdoc.data.split('from:')[1] }}" target="_blank">{{ pdoc.data.split('from:')[1] }}</a></b></p> <p><b>{{ _('Current dataset comes from') }} <a href="{{ pdoc.data.split('from:')[1] }}" target="_blank">{{ pdoc.data.split('from:')[1] }}</a></b></p>
{% else %} {% else %}
<p>{{ _('Current dataset: {0}').format(pdoc['data'].toString()|default(_('(None)'))) }}</p> <p>{{ _('Current dataset: {0}').format((pdoc['data'] or _('(None)')).toString()) }}</p>
<p>{{ _('MD5: {0}').format(md5|default(_('(None)'))) }}</p> <p>{{ _('MD5: {0}').format(md5 or _('(None)')) }}</p>
{% endif %} {% endif %}
<p>{{ _('New dataset') }}: <p>{{ _('New dataset') }}:
<input type="hidden" name="csrf_token" value="{{ handler.csrf_token }}"> <input type="hidden" name="csrf_token" value="{{ handler.csrf_token }}">