mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 09:36:08 +08:00
judge, problem_settings, problem_upload
This commit is contained in:
1 parent
5b4f5b6520
commit
b3024bebb0
45 files changed
+598
-250
No files matched your search
@@ -31,8 +31,8 @@ async function run() {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
require('./service/gridfs');
|
||||
let server = require('./service/server');
|
||||
|
||||
require('./handler/ui');
|
||||
require('./handler/base');
|
||||
require('./handler/home');
|
||||
|
||||
+8
-13
@@ -104,10 +104,17 @@ class ContestNotFoundError extends NotFoundError {
|
||||
this.params = [cid];
|
||||
}
|
||||
}
|
||||
|
||||
class ProblemDataNotFoundError extends NotFoundError {
|
||||
constructor(pid) {
|
||||
super('Data of problem {0} not found.');
|
||||
this.params = [pid];
|
||||
}
|
||||
}
|
||||
module.exports = {
|
||||
BadRequestError, ForbiddenError, NotFoundError,
|
||||
LoginError, UserAlreadyExistError, InvalidTokenError,
|
||||
UserNotFoundError, VerifyPasswordError,
|
||||
UserNotFoundError, VerifyPasswordError, ProblemDataNotFoundError,
|
||||
OpcountExceededError, PermissionError, NoProblemError,
|
||||
ValidationError, ProblemNotFoundError, TrainingNotFoundError,
|
||||
ContestNotFoundError, RecordNotFoundError
|
||||
@@ -179,18 +186,6 @@ class DocumentNotFoundError(NotFoundError):
|
||||
return 'Document {2} not found.'
|
||||
|
||||
|
||||
class ProblemDataNotFoundError(NotFoundError):
|
||||
@property
|
||||
def message(self):
|
||||
return 'Data of problem {1} not found.'
|
||||
|
||||
|
||||
class RecordDataNotFoundError(NotFoundError):
|
||||
@property
|
||||
def message(self):
|
||||
return 'Data of record {0} not found.'
|
||||
|
||||
|
||||
class CsrfTokenError(ForbiddenError):
|
||||
pass
|
||||
|
||||
|
||||
@@ -59,7 +59,6 @@ MIDDLEWARE(async (ctx, next) => {
|
||||
update_ip: ctx.request.ip,
|
||||
update_ua: ctx.request.headers['user-agent'] || ''
|
||||
}, ctx.session));
|
||||
console.log(ctx.session.sid, ctx.session.uid);
|
||||
let cookie = { secure: options.session.secure, httponly: true };
|
||||
if (save) {
|
||||
cookie.expires = ctx.session.expireAt, cookie.maxAge = expireSeconds;
|
||||
|
||||
+47
-2
@@ -14,7 +14,7 @@ GET('/judge/fetch', requirePerm(PERM_JUDGE), async ctx => {
|
||||
let rid = await queue.get('judge', false);
|
||||
if (rid) {
|
||||
let rdoc = await record.get(rid);
|
||||
let data = {
|
||||
let task = {
|
||||
event: 'judge',
|
||||
rid, type: 0,
|
||||
pid: rdoc.pid,
|
||||
@@ -22,12 +22,57 @@ GET('/judge/fetch', requirePerm(PERM_JUDGE), async ctx => {
|
||||
lang: rdoc.lang,
|
||||
code: rdoc.code
|
||||
};
|
||||
ctx.body = data;
|
||||
ctx.body = { task };
|
||||
}
|
||||
else ctx.body = {};
|
||||
});
|
||||
POST('/judge/next', requirePerm(PERM_JUDGE), async ctx => {
|
||||
console.log(ctx.request.body);
|
||||
let body = ctx.request.body;
|
||||
let rdoc = await record.get(body.rid);
|
||||
let $set = {};
|
||||
if (body.case) {
|
||||
rdoc.testCases.push(body.case);
|
||||
$set.testCases = rdoc.testCases;
|
||||
}
|
||||
if (body.judge_text) {
|
||||
rdoc.judgeTexts.push(body.judge_text);
|
||||
$set.judgeTexts = rdoc.judgeTexts;
|
||||
}
|
||||
if (body.compiler_text) {
|
||||
rdoc.compilerTexts.push(body.compilerTexts);
|
||||
$set.compilerTexts = rdoc.compilerTexts;
|
||||
}
|
||||
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;
|
||||
await record.update(body.rid, $set);
|
||||
ctx.body = {};
|
||||
});
|
||||
POST('/judge/end', requirePerm(PERM_JUDGE), async ctx => {
|
||||
console.log(ctx.request.body);
|
||||
let body = ctx.request.body;
|
||||
let rdoc = await record.get(body.rid);
|
||||
let $set = {};
|
||||
if (body.case) {
|
||||
rdoc.testCases.push(body.case);
|
||||
$set.testCases = rdoc.testCases;
|
||||
}
|
||||
if (body.judge_text) {
|
||||
rdoc.judgeTexts.push(body.judge_text);
|
||||
$set.judgeTexts = rdoc.judgeTexts;
|
||||
}
|
||||
if (body.compiler_text) {
|
||||
rdoc.compilerTexts.push(body.compilerTexts);
|
||||
$set.compilerTexts = rdoc.compilerTexts;
|
||||
}
|
||||
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;
|
||||
$set.judgeAt = new Date();
|
||||
$set.judger = ctx.state.user._id;
|
||||
await record.update(body.rid, $set);
|
||||
ctx.body = {};
|
||||
});
|
||||
@@ -1,29 +1,37 @@
|
||||
const
|
||||
fs = require('fs'),
|
||||
bson = require('bson'),
|
||||
validator = require('../lib/validator'),
|
||||
problem = require('../model/problem'),
|
||||
record = require('../model/record'),
|
||||
user = require('../model/user'),
|
||||
system = require('../model/system'),
|
||||
{ GET, POST } = require('../service/server'),
|
||||
gridfs = require('../service/gridfs'),
|
||||
queue = require('../service/queue'),
|
||||
{ requirePerm } = require('./tools'),
|
||||
{ NoProblemError } = require('../error'),
|
||||
{ NoProblemError, ProblemDataNotFoundError, BadRequestError } = require('../error'),
|
||||
{ constants } = require('../options'),
|
||||
{
|
||||
PERM_VIEW_PROBLEM,
|
||||
PERM_VIEW_PROBLEM_HIDDEN,
|
||||
PERM_SUBMIT_PROBLEM,
|
||||
PERM_CREATE_PROBLEM
|
||||
PERM_CREATE_PROBLEM,
|
||||
PERM_READ_PROBLEM_DATA,
|
||||
PERM_EDIT_PROBLEM,
|
||||
PERM_JUDGE
|
||||
} = require('../permission');
|
||||
|
||||
queue.assert('judge');
|
||||
|
||||
GET('/p', requirePerm(PERM_VIEW_PROBLEM), async ctx => {
|
||||
ctx.templateName = 'problem_main.html';
|
||||
let q = {},
|
||||
page = ctx.query.page || 1;
|
||||
if (ctx.query.category) q.category = ctx.query.category;
|
||||
if (!ctx.state.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false;
|
||||
let pdocs = await problem.getMany(q, { pid: 1 }, page, constants.PROBLEM_PER_PAGE);
|
||||
ctx.templateName = 'problem_main.html';
|
||||
ctx.body = { page_name: 'problem_list', page, pdocs, category: '' };
|
||||
ctx.body = { page, pdocs, category: '' };
|
||||
});
|
||||
GET('/problem/random', requirePerm(PERM_VIEW_PROBLEM), async ctx => {
|
||||
let q = {};
|
||||
@@ -33,33 +41,89 @@ GET('/problem/random', requirePerm(PERM_VIEW_PROBLEM), async ctx => {
|
||||
ctx.body = { pid };
|
||||
});
|
||||
GET('/p/:pid', requirePerm(PERM_VIEW_PROBLEM), async ctx => {
|
||||
ctx.templateName = 'problem_detail.html';
|
||||
let uid = ctx.state.user._id,
|
||||
pid = ctx.params.pid;
|
||||
let pdoc = await problem.get({ pid, uid });
|
||||
if (pdoc.hidden) ctx.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
|
||||
let udoc = await user.getById(pdoc.owner);
|
||||
ctx.templateName = 'problem_detail.html';
|
||||
ctx.body = { pdoc, udoc, title: pdoc.title };
|
||||
});
|
||||
GET('/p/:pid/submit', requirePerm(PERM_SUBMIT_PROBLEM), async ctx => {
|
||||
ctx.templateName = 'problem_submit.html';
|
||||
let uid = ctx.state.user._id,
|
||||
pid = ctx.params.pid;
|
||||
let pdoc = await problem.get({ pid, uid });
|
||||
if (pdoc.hidden) ctx.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
|
||||
let udoc = await user.getById(pdoc.owner);
|
||||
ctx.templateName = 'problem_submit.html';
|
||||
ctx.body = { pdoc, udoc, title: pdoc.title };
|
||||
});
|
||||
POST('/p/:pid/submit', requirePerm(PERM_SUBMIT_PROBLEM), async ctx => {
|
||||
let rid = await record.add({
|
||||
uid: ctx.state.user._id,
|
||||
lang: ctx.request.body.language,
|
||||
lang: ctx.request.body.lang,
|
||||
code: ctx.request.body.code,
|
||||
pid: ctx.params.pid
|
||||
});
|
||||
await queue.push('judge', rid);
|
||||
ctx.state.user.nSubmit++;
|
||||
ctx.body = { rid };
|
||||
ctx.setRedirect = `/r/${rid}`;
|
||||
});
|
||||
GET('/p/:pid/settings', async ctx => {
|
||||
ctx.templateName = 'problem_settings.html';
|
||||
let pdoc = await problem.get({ pid: ctx.params.pid, uid: ctx.state.user._id });
|
||||
if (pdoc.hidden) ctx.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
|
||||
ctx.body = { pdoc };
|
||||
});
|
||||
POST('/p/:pid/settings', async ctx => {
|
||||
ctx.templateName = 'problem_settings.html';
|
||||
// TODO(masnn)
|
||||
let pdoc = await problem.get({ pid: ctx.params.pid, uid: ctx.state.user._id });
|
||||
ctx.body = { pdoc };
|
||||
});
|
||||
GET('/p/:pid/upload', async ctx => {
|
||||
ctx.templateName = 'problem_upload.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);
|
||||
let md5;
|
||||
if (typeof pdoc.data == 'object') {
|
||||
let files = await gridfs.find({ _id: pdoc.data }).toArray();
|
||||
md5 = files[0].md5;
|
||||
}
|
||||
ctx.body = { pdoc, md5 };
|
||||
});
|
||||
POST('/p/:pid/upload', async ctx => {
|
||||
ctx.templateName = 'problem_upload.html';
|
||||
if (!ctx.request.files.file) throw new BadRequestError();
|
||||
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);
|
||||
const r = fs.createReadStream(ctx.request.files.file.path);
|
||||
let f = gridfs.openUploadStream('data.zip');
|
||||
await new Promise((resolve, reject) => {
|
||||
r.pipe(f);
|
||||
f.once('finish', resolve);
|
||||
f.once('error', reject);
|
||||
});
|
||||
let md5;
|
||||
if (typeof pdoc.data == 'object') {
|
||||
let files = await gridfs.find({ _id: pdoc.data }).toArray();
|
||||
md5 = files[0].md5;
|
||||
}
|
||||
pdoc = await problem.edit(ctx.params.pid, { data: f.id });
|
||||
ctx.body = { pdoc, md5 };
|
||||
});
|
||||
GET('/p/:pid/data', async ctx => {
|
||||
let pdoc = await problem.get({ pid: ctx.params.pid, uid: ctx.state.user._id });
|
||||
if (!ctx.state.user._id == pdoc.owner) ctx.checkPerm([PERM_READ_PROBLEM_DATA, PERM_JUDGE]);
|
||||
if (!pdoc.data) throw new ProblemDataNotFoundError(ctx.params.pid);
|
||||
else if (typeof pdoc.data == 'string') ctx.setRedirect = pdoc.data.split('from:')[1];
|
||||
ctx.attachment(`${pdoc.title}.zip`);
|
||||
ctx.body = gridfs.openDownloadStream(pdoc.data);
|
||||
});
|
||||
GET('/problem/create', requirePerm(PERM_CREATE_PROBLEM), async ctx => {
|
||||
ctx.templateName = 'problem_edit.html';
|
||||
ctx.body = { page_name: 'problem_create' };
|
||||
});
|
||||
POST('/problem/create', requirePerm(PERM_CREATE_PROBLEM), async ctx => {
|
||||
let { title, pid, content, hidden } = ctx.request.body;
|
||||
@@ -67,8 +131,10 @@ POST('/problem/create', requirePerm(PERM_CREATE_PROBLEM), async ctx => {
|
||||
pid = pid || await system.incPidCounter();
|
||||
await problem.add({ title, content, owner: ctx.state.user._id, pid, hidden });
|
||||
ctx.body = { pid };
|
||||
ctx.setRedirect = `/p/${pid}/settings`;
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@app.route('/p/category/{category:[^/]*}/random', 'problem_category_random')
|
||||
|
||||
+15
-4
@@ -1,13 +1,16 @@
|
||||
const
|
||||
bson = require('bson'),
|
||||
{ constants } = require('../options'),
|
||||
{ GET } = require('../service/server'),
|
||||
{ GET, POST } = require('../service/server'),
|
||||
{ requirePerm } = require('../handler/tools'),
|
||||
queue = require('../service/queue'),
|
||||
record = require('../model/record'),
|
||||
user = require('../model/user'),
|
||||
problem = require('../model/problem'),
|
||||
{ PERM_READ_RECORD_CODE, PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD } = require('../permission');
|
||||
{ PERM_READ_RECORD_CODE, PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD, PERM_REJUDGE } = require('../permission');
|
||||
|
||||
GET('/r', async ctx => {
|
||||
ctx.templateName = 'record_main.html';
|
||||
let q = {},
|
||||
page = ctx.query.page || 1;
|
||||
let rdocs = await record.getMany(q, { rid: 1 }, page, constants.RECORD_PER_PAGE);
|
||||
@@ -16,16 +19,24 @@ GET('/r', async ctx => {
|
||||
udict[rdoc.uid] = await user.getById(rdoc.uid);
|
||||
pdict[rdoc.pid] = await problem.get({ pid: rdoc.pid, uid: ctx.state.user._id });
|
||||
}
|
||||
ctx.templateName = 'record_main.html';
|
||||
ctx.body = { page, rdocs, pdict, udict };
|
||||
});
|
||||
GET('/r/:rid', async ctx => {
|
||||
ctx.templateName = 'record_detail.html';
|
||||
let uid = ctx.state.user._id, rid = new bson.ObjectID(ctx.params.rid);
|
||||
let rdoc = await record.get(rid);
|
||||
if (rdoc.hidden) ctx.checkPerm(PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD);
|
||||
if (rdoc.uid != uid && !ctx.state.user.hasPerm(PERM_READ_RECORD_CODE)) rdoc.code = null;
|
||||
ctx.body = { rdoc, show_status: true };
|
||||
});
|
||||
POST('/r/:rid/rejudge', requirePerm(PERM_REJUDGE), async ctx => {
|
||||
ctx.templateName = 'record_detail.html';
|
||||
ctx.body = { rdoc };
|
||||
let uid = ctx.state.user._id, rid = new bson.ObjectID(ctx.params.rid);
|
||||
let rdoc = await record.get(rid);
|
||||
if (rdoc.hidden) ctx.checkPerm(PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD);
|
||||
if (rdoc.uid != uid && !ctx.state.user.hasPerm(PERM_READ_RECORD_CODE)) rdoc.code = null;
|
||||
if (rdoc) await queue.push('judge', rid);
|
||||
ctx.body = { rdoc, show_status: true };
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
+34
-13
@@ -1,6 +1,5 @@
|
||||
const
|
||||
path = require('path'),
|
||||
md5 = require('blueimp-md5'),
|
||||
static = require('koa-static'),
|
||||
nunjucks = require('nunjucks'),
|
||||
hljs = require('hljs'),
|
||||
@@ -9,14 +8,9 @@ const
|
||||
options = require('../options'),
|
||||
perm = require('../permission'),
|
||||
builtin = require('../model/builtin'),
|
||||
md5 = require('../lib/md5'),
|
||||
{ MIDDLEWARE } = require('../service/server'),
|
||||
{ NotFoundError } = require('../error'),
|
||||
MD5_REGEX = /^[0-9a-f]{32}$/;
|
||||
|
||||
function getHash(email) {
|
||||
email = (typeof email === 'string') ? email.trim().toLowerCase() : 'unspecified';
|
||||
return email.match(MD5_REGEX) ? email : md5(email);
|
||||
}
|
||||
{ NotFoundError } = require('../error');
|
||||
|
||||
class Markdown extends MarkdownIt {
|
||||
constructor() {
|
||||
@@ -35,6 +29,14 @@ class Markdown extends MarkdownIt {
|
||||
}
|
||||
}
|
||||
const md = new Markdown();
|
||||
function datetime_span(dt, { relative = true } = {}) {
|
||||
dt = new Date(dt);
|
||||
return '<span class="time{0}" data-timestamp="{1}">{2}</span>'.format(
|
||||
relative ? ' relative' : '',
|
||||
dt.getTime(),
|
||||
dt.toLocaleString()
|
||||
);
|
||||
}
|
||||
class Nunjucks extends nunjucks.Environment {
|
||||
constructor() {
|
||||
super(
|
||||
@@ -51,12 +53,26 @@ class Nunjucks extends nunjucks.Environment {
|
||||
return md.render(self);
|
||||
});
|
||||
this.addFilter('gravatar_url', function (email, size) {
|
||||
return `//gravatar.loli.net/avatar/${getHash(email)}?d=mm&s=${size}`;
|
||||
return `//gravatar.loli.net/avatar/${md5(email.toString().trim().toLowerCase())}?d=mm&s=${size}`;
|
||||
});
|
||||
this.addFilter('format_size', function (size, base = 1) {
|
||||
size *= base;
|
||||
let unit = 1024;
|
||||
let unit_names = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
|
||||
for (let unit_name of unit_names) {
|
||||
if (size < unit) return '{0} {1}'.format(Math.round(size), unit_name);
|
||||
size /= unit;
|
||||
}
|
||||
return '{0} {1}'.format(Math.round(size * unit), unit_names[unit_names.length - 1]);
|
||||
});
|
||||
this.addGlobal('typeof', o => typeof o);
|
||||
this.addGlobal('console', console);
|
||||
this.addGlobal('static_url', str => `/${str}`);
|
||||
this.addGlobal('reverse_url', str => str);
|
||||
this.addGlobal('datetime_span', datetime_span);
|
||||
this.addGlobal('perm', perm);
|
||||
this.addGlobal('builtin', builtin);
|
||||
this.addGlobal('status', builtin.STATUS);
|
||||
}
|
||||
}
|
||||
const env = new Nunjucks();
|
||||
@@ -67,6 +83,7 @@ MIDDLEWARE(async (ctx, next) => {
|
||||
cdn_prefix: '/',
|
||||
url_prefix: '/'
|
||||
};
|
||||
ctx.preferJson = ctx.request.headers['accept'].includes('application/json');
|
||||
ctx.render = async (name, context) => {
|
||||
ctx.user = ctx.state.user;
|
||||
ctx.translate = str => {
|
||||
@@ -77,7 +94,8 @@ MIDDLEWARE(async (ctx, next) => {
|
||||
ctx.body = await new Promise((resolve, reject) => {
|
||||
env.render(name, Object.assign(ctx.state, context, {
|
||||
handler: ctx,
|
||||
_: ctx.translate
|
||||
_: ctx.translate,
|
||||
user: ctx.state.user
|
||||
}), (error, res) => {
|
||||
if (error) reject(error);
|
||||
else resolve(res);
|
||||
@@ -89,6 +107,7 @@ MIDDLEWARE(async (ctx, next) => {
|
||||
try {
|
||||
await next();
|
||||
if (ctx.body || ctx.templateName) {
|
||||
if (ctx.preferJson) return;
|
||||
if (ctx.query.template || ctx.templateName) {
|
||||
ctx.body = ctx.body || {};
|
||||
Object.assign(ctx.body, JSON.parse(ctx.query.data || '{}'));
|
||||
@@ -99,11 +118,13 @@ MIDDLEWARE(async (ctx, next) => {
|
||||
}
|
||||
} else throw new NotFoundError();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
if (error.toString().startsWith('NotFoundError')) console.log(error);
|
||||
if (error.toString().startsWith('Template render error')) throw error;
|
||||
await ctx.render('error.html', { error });
|
||||
if (ctx.preferJson) ctx.body = { error };
|
||||
else await ctx.render('error.html', { error });
|
||||
}
|
||||
} catch (error) {
|
||||
await ctx.render('bsod.html', { error });
|
||||
if (ctx.preferJson) ctx.body = { error };
|
||||
else await ctx.render('bsod.html', { error });
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,133 @@
|
||||
|
||||
'use strict';
|
||||
function safeAdd(x, y) {
|
||||
var lsw = (x & 0xffff) + (y & 0xffff);
|
||||
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
|
||||
return (msw << 16) | (lsw & 0xffff);
|
||||
}
|
||||
const bitRotateLeft = (num, cnt) => (num << cnt) | (num >>> (32 - cnt));
|
||||
const md5cmn = (q, a, b, x, s, t) => safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b);
|
||||
const md5ff = (a, b, c, d, x, s, t) => md5cmn((b & c) | (~b & d), a, b, x, s, t);
|
||||
const md5gg = (a, b, c, d, x, s, t) => md5cmn((b & d) | (c & ~d), a, b, x, s, t);
|
||||
const md5hh = (a, b, c, d, x, s, t) => md5cmn(b ^ c ^ d, a, b, x, s, t);
|
||||
const md5ii = (a, b, c, d, x, s, t) => md5cmn(c ^ (b | ~d), a, b, x, s, t);
|
||||
function binlMD5(x, len) {
|
||||
x[len >> 5] |= 0x80 << len % 32;
|
||||
x[(((len + 64) >>> 9) << 4) + 14] = len;
|
||||
var i;
|
||||
var olda;
|
||||
var oldb;
|
||||
var oldc;
|
||||
var oldd;
|
||||
var a = 1732584193;
|
||||
var b = -271733879;
|
||||
var c = -1732584194;
|
||||
var d = 271733878;
|
||||
for (i = 0; i < x.length; i += 16) {
|
||||
olda = a;
|
||||
oldb = b;
|
||||
oldc = c;
|
||||
oldd = d;
|
||||
a = md5ff(a, b, c, d, x[i], 7, -680876936);
|
||||
d = md5ff(d, a, b, c, x[i + 1], 12, -389564586);
|
||||
c = md5ff(c, d, a, b, x[i + 2], 17, 606105819);
|
||||
b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330);
|
||||
a = md5ff(a, b, c, d, x[i + 4], 7, -176418897);
|
||||
d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426);
|
||||
c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341);
|
||||
b = md5ff(b, c, d, a, x[i + 7], 22, -45705983);
|
||||
a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416);
|
||||
d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417);
|
||||
c = md5ff(c, d, a, b, x[i + 10], 17, -42063);
|
||||
b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162);
|
||||
a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682);
|
||||
d = md5ff(d, a, b, c, x[i + 13], 12, -40341101);
|
||||
c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290);
|
||||
b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329);
|
||||
a = md5gg(a, b, c, d, x[i + 1], 5, -165796510);
|
||||
d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632);
|
||||
c = md5gg(c, d, a, b, x[i + 11], 14, 643717713);
|
||||
b = md5gg(b, c, d, a, x[i], 20, -373897302);
|
||||
a = md5gg(a, b, c, d, x[i + 5], 5, -701558691);
|
||||
d = md5gg(d, a, b, c, x[i + 10], 9, 38016083);
|
||||
c = md5gg(c, d, a, b, x[i + 15], 14, -660478335);
|
||||
b = md5gg(b, c, d, a, x[i + 4], 20, -405537848);
|
||||
a = md5gg(a, b, c, d, x[i + 9], 5, 568446438);
|
||||
d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690);
|
||||
c = md5gg(c, d, a, b, x[i + 3], 14, -187363961);
|
||||
b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501);
|
||||
a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467);
|
||||
d = md5gg(d, a, b, c, x[i + 2], 9, -51403784);
|
||||
c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473);
|
||||
b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734);
|
||||
a = md5hh(a, b, c, d, x[i + 5], 4, -378558);
|
||||
d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463);
|
||||
c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562);
|
||||
b = md5hh(b, c, d, a, x[i + 14], 23, -35309556);
|
||||
a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060);
|
||||
d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353);
|
||||
c = md5hh(c, d, a, b, x[i + 7], 16, -155497632);
|
||||
b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640);
|
||||
a = md5hh(a, b, c, d, x[i + 13], 4, 681279174);
|
||||
d = md5hh(d, a, b, c, x[i], 11, -358537222);
|
||||
c = md5hh(c, d, a, b, x[i + 3], 16, -722521979);
|
||||
b = md5hh(b, c, d, a, x[i + 6], 23, 76029189);
|
||||
a = md5hh(a, b, c, d, x[i + 9], 4, -640364487);
|
||||
d = md5hh(d, a, b, c, x[i + 12], 11, -421815835);
|
||||
c = md5hh(c, d, a, b, x[i + 15], 16, 530742520);
|
||||
b = md5hh(b, c, d, a, x[i + 2], 23, -995338651);
|
||||
a = md5ii(a, b, c, d, x[i], 6, -198630844);
|
||||
d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415);
|
||||
c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905);
|
||||
b = md5ii(b, c, d, a, x[i + 5], 21, -57434055);
|
||||
a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571);
|
||||
d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606);
|
||||
c = md5ii(c, d, a, b, x[i + 10], 15, -1051523);
|
||||
b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799);
|
||||
a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359);
|
||||
d = md5ii(d, a, b, c, x[i + 15], 10, -30611744);
|
||||
c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380);
|
||||
b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649);
|
||||
a = md5ii(a, b, c, d, x[i + 4], 6, -145523070);
|
||||
d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379);
|
||||
c = md5ii(c, d, a, b, x[i + 2], 15, 718787259);
|
||||
b = md5ii(b, c, d, a, x[i + 9], 21, -343485551);
|
||||
a = safeAdd(a, olda);
|
||||
b = safeAdd(b, oldb);
|
||||
c = safeAdd(c, oldc);
|
||||
d = safeAdd(d, oldd);
|
||||
}
|
||||
return [a, b, c, d];
|
||||
}
|
||||
function binl2rstr(input) {
|
||||
var i;
|
||||
var output = '';
|
||||
var length32 = input.length * 32;
|
||||
for (i = 0; i < length32; i += 8)
|
||||
output += String.fromCharCode((input[i >> 5] >>> i % 32) & 0xff);
|
||||
return output;
|
||||
}
|
||||
function rstr2binl(input) {
|
||||
var i;
|
||||
var output = [];
|
||||
output[(input.length >> 2) - 1] = undefined;
|
||||
for (i = 0; i < output.length; i += 1)
|
||||
output[i] = 0;
|
||||
var length8 = input.length * 8;
|
||||
for (i = 0; i < length8; i += 8)
|
||||
output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << i % 32;
|
||||
return output;
|
||||
}
|
||||
const rstrMD5 = s => binl2rstr(binlMD5(rstr2binl(s), s.length * 8));
|
||||
function rstr2hex(input) {
|
||||
var hexTab = '0123456789abcdef';
|
||||
var output = '';
|
||||
var x;
|
||||
var i;
|
||||
for (i = 0; i < input.length; i += 1) {
|
||||
x = input.charCodeAt(i);
|
||||
output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
module.exports = s => rstr2hex(rstrMD5(unescape(encodeURIComponent(s))));
|
||||
+55
-4
@@ -177,9 +177,9 @@ const FOOTER_EXTRA_HTMLS = [];
|
||||
const VIEW_LANGS = [
|
||||
{ code: 'zh-CN', name: 'zh-CN' }
|
||||
];
|
||||
const LANGS = [
|
||||
{ id: 'cc', name: 'cpp' }
|
||||
];
|
||||
const LANGS = {
|
||||
cc: 'cpp'
|
||||
};
|
||||
const LANG_TEXTS = {
|
||||
c: 'C',
|
||||
cc: 'C++',
|
||||
@@ -210,7 +210,58 @@ const LANG_HIGHLIGHT_ID = {
|
||||
go: 'go',
|
||||
rb: 'ruby',
|
||||
};
|
||||
const STATUS = {
|
||||
STATUS_WAITING: 0,
|
||||
STATUS_ACCEPTED: 1,
|
||||
STATUS_WRONG_ANSWER: 2,
|
||||
STATUS_TIME_LIMIT_EXCEEDED: 3,
|
||||
STATUS_MEMORY_LIMIT_EXCEEDED: 4,
|
||||
STATUS_OUTPUT_LIMIT_EXCEEDED: 5,
|
||||
STATUS_RUNTIME_ERROR: 6,
|
||||
STATUS_COMPILE_ERROR: 7,
|
||||
STATUS_SYSTEM_ERROR: 8,
|
||||
STATUS_CANCELED: 9,
|
||||
STATUS_ETC: 10,
|
||||
STATUS_JUDGING: 20,
|
||||
STATUS_COMPILING: 21,
|
||||
STATUS_FETCHED: 22,
|
||||
STATUS_IGNORED: 30
|
||||
};
|
||||
const STATUS_TEXTS = {
|
||||
[STATUS.STATUS_WAITING]: 'Waiting',
|
||||
[STATUS.STATUS_ACCEPTED]: 'Accepted',
|
||||
[STATUS.STATUS_WRONG_ANSWER]: 'Wrong Answer',
|
||||
[STATUS.STATUS_TIME_LIMIT_EXCEEDED]: 'Time Exceeded',
|
||||
[STATUS.STATUS_MEMORY_LIMIT_EXCEEDED]: 'Memory Exceeded',
|
||||
[STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED]: 'Output Exceeded',
|
||||
[STATUS.STATUS_RUNTIME_ERROR]: 'Runtime Error',
|
||||
[STATUS.STATUS_COMPILE_ERROR]: 'Compile Error',
|
||||
[STATUS.STATUS_SYSTEM_ERROR]: 'System Error',
|
||||
[STATUS.STATUS_CANCELED]: 'Cancelled',
|
||||
[STATUS.STATUS_ETC]: 'Unknown Error',
|
||||
[STATUS.STATUS_JUDGING]: 'Running',
|
||||
[STATUS.STATUS_COMPILING]: 'Compiling',
|
||||
[STATUS.STATUS_FETCHED]: 'Fetched',
|
||||
[STATUS.STATUS_IGNORED]: 'Ignored',
|
||||
};
|
||||
const STATUS_CODES = {
|
||||
0: 'pending',
|
||||
1: 'pass',
|
||||
2: 'fail',
|
||||
3: 'fail',
|
||||
4: 'fail',
|
||||
5: 'fail',
|
||||
6: 'fail',
|
||||
7: 'fail',
|
||||
8: 'fail',
|
||||
9: 'ignored',
|
||||
10: 'fail',
|
||||
20: 'progress',
|
||||
21: 'progress',
|
||||
22: 'progress',
|
||||
30: 'ignored'
|
||||
};
|
||||
module.exports = {
|
||||
BUILTIN_USERS, BUILTIN_ROLES, CATEGORIES, VIEW_LANGS, FOOTER_EXTRA_HTMLS, LANGS,
|
||||
LANG_TEXTS, LANG_HIGHLIGHT_ID
|
||||
LANG_TEXTS, LANG_HIGHLIGHT_ID, STATUS, STATUS_TEXTS, STATUS_CODES
|
||||
};
|
||||
@@ -1,7 +1,8 @@
|
||||
const
|
||||
_ = require('lodash'),
|
||||
{ ObjectID } = require('bson'),
|
||||
{ RecordNotFoundError } = require('../error'),
|
||||
{ STATUS_WAITING } = require('../status'),
|
||||
{ STATUS_WAITING } = require('../model/builtin').STATUS,
|
||||
db = require('../service/db.js'),
|
||||
coll = db.collection('record');
|
||||
|
||||
@@ -29,7 +30,8 @@ async function add(data) {
|
||||
* @returns {import('../interface').Record}
|
||||
*/
|
||||
async function get(rid) {
|
||||
let rdoc = await coll.findOne({ _id: rid });
|
||||
let _id = new ObjectID(rid);
|
||||
let rdoc = await coll.findOne({ _id });
|
||||
if (!rdoc) throw new RecordNotFoundError(rid);
|
||||
return rdoc;
|
||||
}
|
||||
@@ -37,8 +39,9 @@ async function getMany(query, sort, page, limit) {
|
||||
return await coll.find(query).sort(sort).skip((page - 1) * limit).limit(limit).toArray();
|
||||
}
|
||||
async function update(rid, $set) {
|
||||
await coll.findOneAndUpdate({ _id: rid }, { $set });
|
||||
let rdoc = await coll.findOne({ _id: rid });
|
||||
let _id = new ObjectID(rid);
|
||||
await coll.findOneAndUpdate({ _id }, { $set });
|
||||
let rdoc = await coll.findOne({ _id });
|
||||
if (!rdoc) throw new RecordNotFoundError(rid);
|
||||
return rdoc;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ class USER {
|
||||
this.perm = user.perm;
|
||||
}
|
||||
hasPerm(perm) {
|
||||
console.log('PERM', perm, this.perm);
|
||||
return this.perm == '-' || (this.perm || '').includes(perm);
|
||||
}
|
||||
checkPassword(password) {
|
||||
|
||||
@@ -9,7 +9,7 @@ module.exports = {
|
||||
naccept[j.pid]++;
|
||||
}
|
||||
function _time(jdoc) {
|
||||
let real = jdoc.rid.generation_time - Math.floor(tdoc.begin_at / 1000);
|
||||
let real = jdoc.rid.generationTime - Math.floor(tdoc.begin_at / 1000);
|
||||
let penalty = 20 * 60 * naccept[jdoc.pid];
|
||||
return real + penalty;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ module.exports = {
|
||||
effective[j.pid] = j;
|
||||
const _time = jdoc => jdoc.rid.generationTime - Math.floor(tdoc.begin_at / 1000);
|
||||
function _penalty_score(jdoc) {
|
||||
let exceed_seconds = jdoc.rid.generation_time - Math.floor(tdoc.penalty_since / 1000);
|
||||
let exceed_seconds = jdoc.rid.generationTime - Math.floor(tdoc.penalty_since / 1000);
|
||||
if (exceed_seconds < 0) return jdoc.score;
|
||||
else return 0.5 * jdoc.score;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ let permissions = {
|
||||
PERM_VIEW_TRAINING: 'a',
|
||||
PERM_CREATE_TRAINING: 'b',
|
||||
PERM_EDIT_TRAINING: 'c',
|
||||
PERM_JUDGE: 'd'
|
||||
};
|
||||
permissions.PERM_BASIC =
|
||||
permissions.PERM_VIEW +
|
||||
|
||||
@@ -10,6 +10,7 @@ let db = null;
|
||||
Mongo.MongoClient.connect(mongourl, { useNewUrlParser: true, useUnifiedTopology: true })
|
||||
.then(Database => {
|
||||
db = Database.db(options.db.name);
|
||||
db.Db = Database;
|
||||
global.bus.emit('connected');
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
const
|
||||
{ GridFSBucket } = require('mongodb'),
|
||||
db = require('./db');
|
||||
if (!db.s) db.s = {};
|
||||
module.exports = new GridFSBucket(db);
|
||||
@@ -12,7 +12,12 @@ let app = new Koa();
|
||||
let server = http.createServer(app.callback());
|
||||
app.keys = options.session.keys;
|
||||
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'));
|
||||
app.use(Body());
|
||||
app.use(Body({
|
||||
multipart: true,
|
||||
formidable: {
|
||||
maxFileSize: 256 * 1024 * 1024
|
||||
}
|
||||
}));
|
||||
let router = new Router();
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
exports.STATUS_WAITING = 0;
|
||||
exports.STATUS_ACCEPTED = 1;
|
||||
exports.STATUS_WRONG_ANSWER = 2;
|
||||
exports.STATUS_TIME_LIMIT_EXCEEDED = 3;
|
||||
exports.STATUS_MEMORY_LIMIT_EXCEEDED = 4;
|
||||
exports.STATUS_OUTPUT_LIMIT_EXCEEDED = 5;
|
||||
exports.STATUS_RUNTIME_ERROR = 6;
|
||||
exports.STATUS_COMPILE_ERROR = 7;
|
||||
exports.STATUS_SYSTEM_ERROR = 8;
|
||||
exports.STATUS_CANCELED = 9;
|
||||
exports.STATUS_ETC = 10;
|
||||
exports.STATUS_JUDGING = 20;
|
||||
exports.STATUS_COMPILING = 21;
|
||||
exports.STATUS_FETCHED = 22;
|
||||
exports.STATUS_IGNORED = 30;
|
||||
exports.STATUS_TEXTS = {
|
||||
[exports.STATUS_WAITING]: 'Waiting',
|
||||
[exports.STATUS_ACCEPTED]: 'Accepted',
|
||||
[exports.STATUS_WRONG_ANSWER]: 'Wrong Answer',
|
||||
[exports.STATUS_TIME_LIMIT_EXCEEDED]: 'Time Exceeded',
|
||||
[exports.STATUS_MEMORY_LIMIT_EXCEEDED]: 'Memory Exceeded',
|
||||
[exports.STATUS_OUTPUT_LIMIT_EXCEEDED]: 'Output Exceeded',
|
||||
[exports.STATUS_RUNTIME_ERROR]: 'Runtime Error',
|
||||
[exports.STATUS_COMPILE_ERROR]: 'Compile Error',
|
||||
[exports.STATUS_SYSTEM_ERROR]: 'System Error',
|
||||
[exports.STATUS_CANCELED]: 'Cancelled',
|
||||
[exports.STATUS_ETC]: 'Unknown Error',
|
||||
[exports.STATUS_JUDGING]: 'Running',
|
||||
[exports.STATUS_COMPILING]: 'Compiling',
|
||||
[exports.STATUS_FETCHED]: 'Fetched',
|
||||
[exports.STATUS_IGNORED]: 'Ignored',
|
||||
};
|
||||
+1
-2
@@ -7,7 +7,6 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^0.19.0",
|
||||
"blueimp-md5": "^2.13.0",
|
||||
"bson": "^4.0.2",
|
||||
"hljs": "^6.2.3",
|
||||
"js-yaml": "^3.13.1",
|
||||
@@ -19,7 +18,7 @@
|
||||
"lodash": "^4.17.15",
|
||||
"markdown-it": "^10.0.0",
|
||||
"markdown-it-katex": "^2.0.3",
|
||||
"mongodb": "^3.3.4",
|
||||
"mongodb": "^3.5.5",
|
||||
"nodemailer": "^6.3.1",
|
||||
"nunjucks": "^3.2.1",
|
||||
"sockjs": "^0.3.20"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<div class="clearfix">
|
||||
<div class="supplementary dczcomments__supplementary">
|
||||
{{ user.render_inline(udoc, dudict[udoc['_id']], avatar=false) }}
|
||||
@ {{ datetime_span(rdoc['_id'].generation_time) }}
|
||||
@ {{ datetime_span(rdoc['_id'].generationTime) }}
|
||||
</div>
|
||||
{{ comment_base.reply_operations(doc, rdoc) }}
|
||||
</div>
|
||||
@@ -37,7 +37,7 @@
|
||||
<div class="clearfix">
|
||||
<div class="supplementary dczcomments__supplementary">
|
||||
{{ user.render_inline(udoc, dudict[udoc['_id']], avatar=false) }}
|
||||
@ {{ datetime_span(doc['_id'].generation_time) }}
|
||||
@ {{ datetime_span(doc['_id'].generationTime) }}
|
||||
</div>
|
||||
{{ comment_base.comment_operations(doc) }}
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<div class="clearfix">
|
||||
<div class="supplementary dczcomments__supplementary">
|
||||
{{ user.render_inline(udoc, badge=false) }}
|
||||
@ {{ datetime_span(rdoc['_id'].generation_time) }}
|
||||
@ {{ datetime_span(rdoc['_id'].generationTime) }}
|
||||
</div>
|
||||
{{ comment_base.reply_operations(doc, rdoc) }}
|
||||
</div>
|
||||
@@ -65,7 +65,7 @@
|
||||
<div class="clearfix">
|
||||
<div class="supplementary dczcomments__supplementary">
|
||||
{{ user.render_inline(udoc, dudict[udoc['_id']]) }}
|
||||
@ {{ datetime_span(doc['_id'].generation_time) }}
|
||||
@ {{ datetime_span(doc['_id'].generationTime) }}
|
||||
</div>
|
||||
{{ comment_base.comment_operations(doc) }}
|
||||
</div>
|
||||
|
||||
@@ -71,19 +71,19 @@
|
||||
name="{{ args.name }}"
|
||||
{{ form_attr('select', args) }}
|
||||
>
|
||||
{% for item in args.options %}
|
||||
<option value="{{ item.id }}"{% if value == item.id %} selected{% endif %}>
|
||||
{{ _(item.name) }}
|
||||
{% for k, v in args.options %}
|
||||
<option value="{{ k }}"{% if value == k %} selected{% endif %}>
|
||||
{{ _(v) }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro form_select(columns=5, args) %}
|
||||
{{ form_begin({columns:columns}|assign(args)) }}
|
||||
{{ select({columns:columns}|assign(args)) }}
|
||||
{{ form_end({columns:columns}|assign(args)) }}
|
||||
{% macro form_select(args) %}
|
||||
{{ form_begin({columns:5}|assign(args)) }}
|
||||
{{ select(args) }}
|
||||
{{ form_end(args) }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro radio(args) %}
|
||||
@@ -91,24 +91,24 @@
|
||||
name="form_item_{{ args.name }}"
|
||||
{{ container_attr('radiobox-container', args) }}
|
||||
>
|
||||
{% for item in options %}
|
||||
{% for k, v in options %}
|
||||
<label class="radiobox">
|
||||
<input
|
||||
value="{{ item.id }}"
|
||||
value="{{ k }}"
|
||||
type="radio"
|
||||
name="{{ args.name }}"
|
||||
name="{{ v }}"
|
||||
{{ form_attr('radiobox__input', args) }}
|
||||
{% if value == args.id %} checked{% endif %}
|
||||
/> {{ _(item.name) }}
|
||||
/> {{ _(v) }}
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro form_radio(columns=5, args) %}
|
||||
{{ form_begin({columns:columns, label_wrap:false}|assign(args)) }}
|
||||
{{ radio({columns:columns}|assign(args)) }}
|
||||
{{ form_end({columns:columns, label_warp:false}|assign(args)) }}
|
||||
{% macro form_radio(args) %}
|
||||
{{ form_begin({columns:5, label_wrap:false}|assign(args)) }}
|
||||
{{ radio(args) }}
|
||||
{{ form_end({label_warp:false}|assign(args)) }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro image_radio(args) %}
|
||||
@@ -116,27 +116,27 @@
|
||||
name="form_item_{{ args.name }}"
|
||||
{{ container_attr('radiobox-container with-image', args) }}
|
||||
>
|
||||
{% for item in options %}
|
||||
{% for k, v in options %}
|
||||
<label class="radiobox with-image">
|
||||
<input
|
||||
value="{{ item.id }}"
|
||||
value="{{ k }}"
|
||||
type="radio"
|
||||
name="{{ name }}"
|
||||
name="{{ args.name }}"
|
||||
{{ form_attr('radiobox__input', args) }}
|
||||
{% if value == item.id %} checked{% endif %}
|
||||
{% if value == k %} checked{% endif %}
|
||||
/>
|
||||
<div class="radiobox__image-container" data-tooltip="{{ _(item.name) }}">
|
||||
<div class="radiobox__image {{ image_class.format(item.id) }}"></div>
|
||||
<div class="radiobox__image-container" data-tooltip="{{ _(v) }}">
|
||||
<div class="radiobox__image {{ image_class.format(k) }}"></div>
|
||||
</div>
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro form_image_radio(columns=8, args) %}
|
||||
{{ form_begin({columns:columns, label_warp:false}|assign(args)) }}
|
||||
{{ image_radio({columns:columns}|assign(args)) }}
|
||||
{{ form_end({columns:columns, label_warp:false}|assign(args)) }}
|
||||
{% macro form_image_radio(args) %}
|
||||
{{ form_begin({columns:8, label_warp:false}|assign(args)) }}
|
||||
{{ image_radio(args) }}
|
||||
{{ form_end({label_warp:false}|assign(args)) }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro text(args) %}
|
||||
@@ -154,10 +154,10 @@
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro form_text(columns=5, args) %}
|
||||
{{ form_begin({columns:columns}|assign(args)) }}
|
||||
{{ text({columns:columns}|assign(args)) }}
|
||||
{{ form_end({columns:columns}|assign(args)) }}
|
||||
{% macro form_text(args) %}
|
||||
{{ form_begin({columns:5}|assign(args)) }}
|
||||
{{ text(args) }}
|
||||
{{ form_end(args) }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro textarea(args) %}
|
||||
@@ -175,10 +175,10 @@
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro form_textarea(columns=8) %}
|
||||
{{ form_begin({columns:columns}|assign(args)) }}
|
||||
{{ textarea({columns:columns}|assign(args)) }}
|
||||
{{ form_end({columns:columns}|assign(args)) }}
|
||||
{% macro form_textarea(args) %}
|
||||
{{ form_begin({columns:8}|assign(args)) }}
|
||||
{{ textarea(args) }}
|
||||
{{ form_end(args) }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro checkbox(args) %}
|
||||
@@ -194,8 +194,8 @@
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro form_checkbox(columns=5, args) %}
|
||||
{{ form_begin({columns:columns, no_label:true}|assign(args)) }}
|
||||
{{ checkbox({columns:columns}|assign(args)) }}
|
||||
{{ form_end({columns:columns, no_label:true}|assign(args)) }}
|
||||
{% macro form_checkbox(args) %}
|
||||
{{ form_begin({columns:5, no_label:true}|assign(args)) }}
|
||||
{{ checkbox(args) }}
|
||||
{{ form_end({no_label:true}|assign(args)) }}
|
||||
{% endmacro %}
|
||||
@@ -1,8 +1,8 @@
|
||||
{% macro render_status_td(rdoc, rid_key='_id') %}
|
||||
<td class="col--status record-status--border {{ status.STATUS_CODES[rdoc['status']] }}">
|
||||
<td class="col--status record-status--border {{ builtin.STATUS_CODES[rdoc['status']] }}">
|
||||
<div class="col--status__text">
|
||||
<span class="icon record-status--icon {{ status.STATUS_CODES[rdoc['status']] }}"></span>
|
||||
<a href="{{ reverse_url('record_detail', rid=rdoc[rid_key], domain_id=rdoc['domain_id']) }}" class="record-status--text {{ status.STATUS_CODES[rdoc['status']] }}">
|
||||
<span class="icon record-status--icon {{ builtin.STATUS_CODES[rdoc['status']] }}"></span>
|
||||
<a href="/r/{{ rdoc._id }}" class="record-status--text {{ builtin.STATUS_CODES[rdoc['status']] }}">
|
||||
{{ status.STATUS_TEXTS[rdoc['status']] }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -48,9 +48,9 @@
|
||||
{% if handler.can_show_record(tdoc) %}
|
||||
{{ record.render_status_td(rdoc) }}
|
||||
{% else %}
|
||||
<td class="col--status record-status--border"><a href="{{ reverse_url('record_detail', rid=rdoc['_id'], domain_id=handler.domain_id) }}">{{ _('Submitted') }}</a></td>
|
||||
<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'].generation_time) }}</td>
|
||||
<td class="col--submit-at">{{ datetime_span(rdoc['_id'].generationTime) }}</td>
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<td class="col--status record-status--border">{{ _('No Submissions') }}</td>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<li>
|
||||
{{ user.render_inline(udict[ddoc['owner_uid']], dudict[ddoc['owner_uid']]) }}
|
||||
@
|
||||
{{ datetime_span(ddoc['_id'].generation_time) }}
|
||||
{{ datetime_span(ddoc['_id'].generationTime) }}
|
||||
</li>
|
||||
</ul>
|
||||
<div class="typo topic__content" data-emoji-enabled data-raw-url="{{ reverse_url('discussion_detail_raw', did=ddoc['_id']) }}">
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
{% if handler.has_priv(vj4.model.builtin.PRIV_CREATE_DOMAIN) %}
|
||||
<a class="primary rounded button" href="{{ reverse_url('home_domain_create') }}"><span class="icon icon-add"></span> {{ _('Create Domain') }}</a>
|
||||
{% endif %}
|
||||
<a class="tool-button" href="{{ reverse_url('wiki_help') }}#domain"><span class="icon icon-help"></span> {{ _('What\'s domain?') }}</a>
|
||||
<a class="tool-button" href="/wiki/help#domain"><span class="icon icon-help"></span> {{ _('What\'s domain?') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% if pending_ddocs|length > 0 %}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
{% if handler.has_priv(vj4.model.builtin.PRIV_CREATE_FILE) %}
|
||||
<a class="primary rounded button" href="{{ reverse_url('fs_upload') }}" target="_blank"><span class="icon icon-add"></span> {{ _('Upload File') }}</a>
|
||||
{% endif %}
|
||||
<a class="tool-button" href="{{ reverse_url('wiki_help') }}#file"><span class="icon icon-help"></span> {{ _('What\'s file?') }}</a>
|
||||
<a class="tool-button" href="/wiki/help#file"><span class="icon icon-help"></span> {{ _('What\'s file?') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section__body no-padding">
|
||||
|
||||
@@ -47,9 +47,9 @@
|
||||
{% if handler.can_show_record(tdoc) %}
|
||||
{{ record.render_status_td(rdoc) }}
|
||||
{% else %}
|
||||
<td class="col--status record-status--border"><a href="{{ reverse_url('record_detail', rid=rdoc['_id'], domain_id=handler.domain_id) }}">{{ _('Submitted') }}</a></td>
|
||||
<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'].generation_time) }}</td>
|
||||
<td class="col--submit-at">{{ datetime_span(rdoc['_id'].generationTime) }}</td>
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
<td class="col--status record-status--border">{{ _('No Submissions') }}</td>
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
<li class="menu__item"><a class="menu__link" href="/r?tid={{ tdoc['doc_id'] }}">
|
||||
<span class="icon icon-flag"></span> {{ _('All Submissions') }}
|
||||
</a></li>
|
||||
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('wiki_help') }}#contest">
|
||||
<li class="menu__item"><a class="menu__link" href="/wiki/help#contest">
|
||||
<span class="icon icon-help"></span> {{ _('Help') }}
|
||||
</a></li>
|
||||
<li class="menu__seperator"></li>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<li>
|
||||
{{ user.render_inline(udict[ddoc['owner_uid']], dudoc=dudoc if dudoc else dudict[ddoc['owner_uid']], badge=false) }}
|
||||
@
|
||||
{{ datetime_span(ddoc['update_at'] or ddoc['_id'].generation_time) }}
|
||||
{{ datetime_span(ddoc['update_at'] or ddoc['_id'].generationTime) }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
<span class="icon icon-flag"></span> {{ _('My Recent Submissions') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('wiki_help') }}#homework">
|
||||
<li class="menu__item"><a class="menu__link" href="/wiki/help#homework">
|
||||
<span class="icon icon-help"></span> {{ _('Help') }}
|
||||
</a></li>
|
||||
<li class="menu__seperator"></li>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<div data-fragment-id="problem_lucky">
|
||||
<p>
|
||||
<a href="{% if not category %}{{ reverse_url('problem_random') }}{% else %}{{ reverse_url('problem_category_random', category=category) }}{% endif %}" class="expanded button" target="_blank"><span class="icon icon-feeling-lucky"></span> {{ _('Lucky') }}</a>
|
||||
</p>
|
||||
<div class="typo supplementary">
|
||||
<p>{{ _('Pick a problem randomly based on current filter') }}</p>
|
||||
</div>
|
||||
<p>
|
||||
<a href="/problem/random{% if category %}?category={{ category }}{% endif %}" class="expanded button" target="_blank">
|
||||
<span class="icon icon-feeling-lucky"></span> {{ _('Lucky') }}
|
||||
</a>
|
||||
</p>
|
||||
<div class="typo supplementary">
|
||||
<p>{{ _('Pick a problem randomly based on current filter') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -70,7 +70,7 @@
|
||||
<span class="icon icon-copy"></span> {{ _('Copy Problem') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if handler.user._id == pdoc.owner or handler.has_perm(perm.PERM_EDIT_PROBLEM) %}
|
||||
{% if user._id == pdoc.owner or handler.has_perm(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') }}
|
||||
@@ -98,8 +98,8 @@
|
||||
<dt>{{ _('Submission') }}</dt>
|
||||
<dd>
|
||||
{% if pdoc['psdoc']['status'] %}
|
||||
<span class="icon record-status--icon {{ status.STATUS_CODES[pdoc['psdoc']['status']] }}"></span>
|
||||
<a href="/r/{{ pdoc['psdoc']['rid'] }}" class="record-status--text {{ status.STATUS_CODES[pdoc['psdoc']['status']] }}">
|
||||
<span class="icon record-status--icon {{ builtin.STATUS_CODES[pdoc['psdoc']['status']] }}"></span>
|
||||
<a href="/r/{{ pdoc['psdoc']['rid'] }}" class="record-status--text {{ builtin.STATUS_CODES[pdoc['psdoc']['status']] }}">
|
||||
{{ status.STATUS_TEXTS[pdoc['psdoc']['status']] }}
|
||||
</a>
|
||||
{% else %}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<blockquote class="warn">
|
||||
<p>{{ _('No dataset at present.') }}</p>
|
||||
</blockquote>
|
||||
{% elif pdoc.data.startsWith('from:') %}
|
||||
{% elif typeof(pdoc.data) == 'string' %}
|
||||
<blockquote class="note">
|
||||
<p>{{ _('Test data comes from') }} <a href="#" target="_blank">{{ pdoc.data.split(':')[1] }}</a></p>
|
||||
</blockquote>
|
||||
|
||||
@@ -6,19 +6,18 @@
|
||||
<div class="section__body">
|
||||
<form method="post">
|
||||
<div class="row">
|
||||
<div class="medium-8 columns">
|
||||
<div class="medium-7 columns">
|
||||
<label>
|
||||
{{ _('Title') }}
|
||||
<input name="title" placeholder="{{ _('title') }}" value="{{ pdoc['title']|default('') }}" class="textbox" autofocus>
|
||||
</label>
|
||||
</div>
|
||||
{% if page_name == 'problem_create' %}
|
||||
<div class="medium-2 columns">
|
||||
<div class="medium-3 columns">
|
||||
<label>
|
||||
{{ _('Settings') }}
|
||||
{{ _('pid') }}
|
||||
<br>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" name="numeric_pid" value="on" checked>{{ _('Numeric PID') }}
|
||||
<label>
|
||||
<input name="pid" placeholder="{{ _('Leave blank to use numeric pid.') }}" value="{{ pdoc['pid']|default('') }}" class="textbox">
|
||||
</label>
|
||||
</label>
|
||||
</div>
|
||||
@@ -30,7 +29,6 @@
|
||||
</label>
|
||||
</label>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="row"><div class="columns">
|
||||
<label>
|
||||
@@ -49,9 +47,6 @@
|
||||
{{ _('Create') }}
|
||||
</button>
|
||||
{% endif %}
|
||||
<button type="button" class="rounded button" onclick="window.history.go(-1)">
|
||||
{{ _('Cancel') }}
|
||||
</button>
|
||||
</div></div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -61,7 +56,6 @@
|
||||
{% if page_name == 'problem_edit' %}
|
||||
{% set owner_udoc=udoc %}{% set owner_dudoc=dudoc %}
|
||||
{% include "partials/problem_sidebar.html" %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
{% include 'components/md_hint.html' %}
|
||||
{% endif %}
|
||||
|
||||
+16
-17
@@ -1,3 +1,4 @@
|
||||
{% set page_name = "problem_main" %}
|
||||
{% import "components/record.html" as record with context %}
|
||||
{% import "components/problem.html" as problem with context %}
|
||||
{% extends "layout/basic.html" %}
|
||||
@@ -24,23 +25,21 @@
|
||||
</div>
|
||||
<div class="section__body">
|
||||
<ul class="group-list" data-widget-cf-container>
|
||||
{% for category in builtin.CATEGORIES %}
|
||||
{% for sub_categories in category %}
|
||||
<li class="group-list__item">
|
||||
<h2 class="section__title">
|
||||
<a href="/p?category={{ category|urlencode }}">{{ category }}</a>
|
||||
</h2>
|
||||
{% if sub_categories | length > 0 %}
|
||||
<ol class="chip-list">
|
||||
{% for sub_category in sub_categories %}
|
||||
<li class="chip-list__item">
|
||||
<a class="typo-a" href="/p?category={{ category|urlencode + ',' + sub_category|urlencode }}">{{ sub_category }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% for category, sub_categories in builtin.CATEGORIES %}
|
||||
<li class="group-list__item">
|
||||
<h2 class="section__title">
|
||||
<a href="/p?category={{ category|urlencode }}">{{ category }}</a>
|
||||
</h2>
|
||||
{% if sub_categories | length > 0 %}
|
||||
<ol class="chip-list">
|
||||
{% for sub_category in sub_categories %}
|
||||
<li class="chip-list__item">
|
||||
<a class="typo-a" href="/p?category={{ category|urlencode + ',' + sub_category|urlencode }}">{{ sub_category }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{% set page_name = "problem_settings" %}
|
||||
{% extends "layout/basic.html" %}
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
@@ -7,9 +8,9 @@
|
||||
<h1 class="section__title">{{ _('Dataset') }}</h1>
|
||||
</div>
|
||||
<div class="section__body">
|
||||
<iframe src="{{ reverse_url('problem_upload', pid=pdoc['_id']) }}" frameborder="0" style="width: 100%; height: 100px;"></iframe>
|
||||
<p class="help-text">{{ _('Hint') }}: <a href="{{ reverse_url('wiki_help') }}#upload">{{ _('Dataset Format') }}</a>, {{ _('An example of dataset') }}: <a href="https://github.com/vijos/jd4/blob/master/jd4/testdata/aplusb-legacy.zip?raw=true">{{ _('Download') }}</a></p>
|
||||
{% if pdoc and (handler.own(pdoc, perm.PERM_READ_PROBLEM_DATA_SELF) or handler.has_perm(perm.PERM_READ_PROBLEM_DATA) or handler.has_priv(vj4.model.builtin.PRIV_READ_PROBLEM_DATA)) %}
|
||||
<iframe src="/p/{{ pdoc.pid }}/upload" frameborder="0" style="width: 100%; height: 100px;"></iframe>
|
||||
<p class="help-text">{{ _('Hint') }}: <a href="/wiki/help#upload">{{ _('Dataset Format') }}</a>, {{ _('An example of dataset') }}: <a href="https://github.com/vijos/jd4/blob/master/jd4/testdata/aplusb-legacy.zip?raw=true">{{ _('Download') }}</a></p>
|
||||
{% if pdoc and (pdoc.owner == user._id or handler.has_perm(perm.PERM_READ_PROBLEM_DATA)) %}
|
||||
<p><a href="/p/{{ pdoc.pid }}/data" class="expanded button">{{ _('Download Dataset') }}</a></p>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -20,27 +21,31 @@
|
||||
</div>
|
||||
<div class="section__body">
|
||||
<form method="post">
|
||||
{{ form.form_checkbox(label='Hidden', help_text='Note: Problem title may not be hidden.', name='hidden', value=pdoc['hidden']) }}
|
||||
{{ form.form_text(columns=12, label='Category', help_text='Format: category 1, sub category 1.1, sub category 1.2, ..., sub category 1.x, ..., category n, sub category n.1, sub category n.2, ..., sub category n.m, ...', name='category', value=pdoc['category']|join(', ')) }}
|
||||
{{ form.form_text(columns=12, label='Tags', help_text='Splitted by \', \'.', name='tag', value=pdoc['tag']|join(', ')) }}
|
||||
{{ form.form_text(columns=12, label='AC Message', help_text='This message will be presented to those whose submissions are accepted.', name='ac_msg', value=(pdoc['ac_msg'] or '' )) }}
|
||||
<div class="row">
|
||||
{{ form.form_select(columns=4, label='Difficulty', name='difficulty_setting', options=vj4.model.adaptor.problem.SETTING_DIFFICULTY_RANGE.items(), value=pdoc['difficulty_setting']|default(0), row=false) }}
|
||||
{{ form.form_text(columns=4, label='Evaluated difficulty', help_text='Effects only when Difficulty is not \'Use algorithm calculated\'.', name='difficulty_admin', placeholder='9', value=pdoc['difficulty_admin'] or '', row=False) }}
|
||||
<div class="medium-4 columns">
|
||||
<p class="help-text">{{ _('Difficulty displayed') }}: {{ pdoc['difficulty'] or _('(None)') }}</p>
|
||||
<p class="help-text">{{ _('Difficulty by algorithm') }}: {{ pdoc['difficulty_algo'] or _('(None)') }}</p>
|
||||
<p class="help-text">{{ _('Difficulty by admin') }}: {{ pdoc['difficulty_admin'] or _('(None)') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{{ form.form_checkbox({
|
||||
label:'Hidden',
|
||||
help_text:'Note: Problem title may not be hidden.',
|
||||
name:'hidden',
|
||||
value:pdoc['hidden']
|
||||
}) }}
|
||||
{{ form.form_text({
|
||||
columns:12,
|
||||
label:'Category',
|
||||
help_text:'Format: category 1, sub category 1.1, sub category 1.2, ..., sub category 1.x, ..., category n, sub category n.1, sub category n.2, ..., sub category n.m, ...',
|
||||
name:'category',
|
||||
value:pdoc['category']|join(', ')
|
||||
}) }}
|
||||
{{ form.form_text({
|
||||
columns:12,
|
||||
label:'Tags',
|
||||
help_text:'Splitted by \', \'.',
|
||||
name:'tag',
|
||||
value:pdoc['tag']|join(', ')
|
||||
}) }}
|
||||
<div class="row"><div class="columns">
|
||||
<input type="hidden" name="csrf_token" value="{{ handler.csrf_token }}">
|
||||
<button type="submit" class="rounded primary button">
|
||||
{{ _('Update') }}
|
||||
</button>
|
||||
<button type="button" class="rounded button" onclick="window.history.go(-1)">
|
||||
{{ _('Cancel') }}
|
||||
</button>
|
||||
</div></div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -50,8 +55,7 @@
|
||||
<h1 class="section__title">{{ _('Categories') }} ({{ _('click to add') }})</h1>
|
||||
</div>
|
||||
<div class="section__body">
|
||||
<!-- TODO: chip -->
|
||||
{% for category, sub_categories in categories.items() %}
|
||||
{% for category, sub_categories in builtin.CATEGORIES %}
|
||||
<div>
|
||||
<h2 class="section__title"><a class="category-a" href="javascript:;" data-category="{{ category }}">{{ category }}</a></h2>
|
||||
<ol>
|
||||
@@ -67,7 +71,6 @@
|
||||
<div class="medium-3 columns">
|
||||
{% set owner_udoc=udoc %}{% set owner_dudoc=dudoc %}
|
||||
{% include "partials/problem_sidebar.html" %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -6,7 +6,7 @@
|
||||
{% if not tdoc or rdocs %}
|
||||
<div class="section">
|
||||
<div class="section__header">
|
||||
<h1 class="section__title">{{ _('Recent {0} Records').format(10) }}</h1>
|
||||
<h1 class="section__title">{{ _('Recent 10 Records') }}</h1>
|
||||
</div>
|
||||
<div class="section__body no-padding">
|
||||
{% if not rdocs %}
|
||||
@@ -23,7 +23,6 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col--status record-status--border">{{ _('Status') }}</th>
|
||||
<th class="col--pretest">{{ _('Pretest?') }}</th>
|
||||
<th class="col--memory">{{ _('Peak Memory') }}</th>
|
||||
<th class="col--time">{{ _('Total Time') }}</th>
|
||||
<th class="col--submit-at">{{ _('Submit At') }}</th>
|
||||
@@ -45,8 +44,8 @@
|
||||
</div>
|
||||
<div class="section__body">
|
||||
<form method="post">
|
||||
{{ form.form_select(label='Code language', options=builtin.LANGS, name='lang', value=handler.user.codeLang) }}
|
||||
{{ form.form_textarea(label='Code', columns=12, name='code', extra_class='monospace', autofocus=true) }}
|
||||
{{ form.form_select({label:'Code language', options:builtin.LANG_TEXTS, name:'lang', value:handler.user.codeLang}) }}
|
||||
{{ form.form_textarea({label:'Code', columns:12, name:'code', extra_class:'monospace', autofocus:true}) }}
|
||||
<div class="row"><div class="columns">
|
||||
<input type="hidden" name="csrf_token" value="{{ handler.csrf_token }}">
|
||||
<input type="submit" class="rounded primary button" value="{{ _('Submit') }}">
|
||||
|
||||
@@ -14,5 +14,5 @@
|
||||
<td class="col--time">
|
||||
{{ (rdoc.time / 1000)|round(1) }}s
|
||||
</td>
|
||||
<td class="col--submit-at">{{ datetime_span(rdoc['_id'].generation_time) }}</td>
|
||||
<td class="col--submit-at">{{ datetime_span(rdoc['_id'].generationTime) }}</td>
|
||||
</tr>
|
||||
@@ -1,17 +1,16 @@
|
||||
{% extends 'layout/simple.html' %}
|
||||
{% block body %}
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
{% if pdoc['data'].startsWith('from:') %}
|
||||
<p><b>{{ _('Current dataset comes from') }} <a href="{{ pdoc.data.split('from:')[1] }}" target="_blank">{{ pdoc.data.split('from:')[1] }}</a></b></p>
|
||||
{% else %}
|
||||
<p>{{ _('Current dataset: {0}').format(pdoc['data']|default(_('(None)'))) }}</p>
|
||||
<p>{{ _('MD5: {0}').format(md5|default(_('(None)'))) }}</p>
|
||||
{% endif %}
|
||||
<p>
|
||||
{{ _('New dataset') }}:
|
||||
<input type="hidden" name="csrf_token" value="{{ handler.csrf_token }}">
|
||||
<input type="file" name="file">
|
||||
<input type="submit" value="{{ _('Upload') }}" class="rounded primary button">
|
||||
</p>
|
||||
{% 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>
|
||||
{% else %}
|
||||
<p>{{ _('Current dataset: {0}').format(pdoc['data'].toString()|default(_('(None)'))) }}</p>
|
||||
<p>{{ _('MD5: {0}').format(md5|default(_('(None)'))) }}</p>
|
||||
{% endif %}
|
||||
<p>{{ _('New dataset') }}:
|
||||
<input type="hidden" name="csrf_token" value="{{ handler.csrf_token }}">
|
||||
<input type="file" name="file">
|
||||
<input type="submit" value="{{ _('Upload') }}" class="rounded primary button">
|
||||
</p>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -1,5 +1,6 @@
|
||||
{% extends "layout/basic.html" %}
|
||||
{% set page_name = "record_detail" %}
|
||||
{% import "components/problem.html" as problem with context %}
|
||||
{% extends "layout/basic.html" %}
|
||||
{% block content %}
|
||||
<script>
|
||||
var Context = {
|
||||
@@ -77,10 +78,7 @@
|
||||
</dd>
|
||||
{% endif %}{# tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST #}
|
||||
{% endif %}{# tdoc #}
|
||||
{% if rdoc['type'] == vj4.constant.record.TYPE_PRETEST and (handler.own(rdoc, priv=vj4.model.builtin.PRIV_READ_PRETEST_DATA_SELF, field='uid') or handler.has_priv(vj4.model.builtin.PRIV_READ_PRETEST_DATA)) %}
|
||||
<dt>{{ _('Pretest Data') }}</dt>
|
||||
<dd><a href="{{ reverse_url('record_pretest_data', rid=rdoc['_id']) }}"><span class="icon icon-download"></span> {{ _('Download') }}</a></dd>
|
||||
{% elif rdoc['type'] == vj4.constant.record.TYPE_SUBMISSION and pdoc and (handler.own(pdoc, perm.PERM_READ_PROBLEM_DATA_SELF) or handler.has_perm(perm.PERM_READ_PROBLEM_DATA) or handler.has_priv(vj4.model.builtin.PRIV_READ_PROBLEM_DATA)) %}
|
||||
{% if pdoc and (user._id == pdoc.owner or handler.has_perm(perm.PERM_READ_PROBLEM_DATA)) %}
|
||||
<dt>{{ _('Problem Data') }}</dt>
|
||||
<dd><a href="/p/{{ pdoc.pid }}/data"><span class="icon icon-download"></span> {{ _('Download') }}</a></dd>
|
||||
{% endif %}
|
||||
@@ -92,11 +90,11 @@
|
||||
{{ _('Submit At') }}
|
||||
</dt>
|
||||
<dd>
|
||||
{{ datetime_span(rdoc['_id'].generation_time) }}
|
||||
{{ datetime_span(rdoc['_id'].generationTime)|safe }}
|
||||
</dd>
|
||||
{% if rdoc['judge_at'] %}
|
||||
{% if rdoc.judgeAt %}
|
||||
<dt>{{ _('Judged At') }}</dt>
|
||||
<dd>{{ datetime_span(rdoc['judge_at']) }}</dd>
|
||||
<dd>{{ datetime_span(rdoc.judgeAt)|safe }}</dd>
|
||||
{% endif %}
|
||||
{% if judge_udoc %}
|
||||
<dt>{{ _('Judged By') }}</dt>
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
<div class="section visible" id="status">
|
||||
<div class="section__header">
|
||||
<h1 class="section__title">
|
||||
<span class="icon record-status--icon {{ status.STATUS_CODES[rdoc['status']] }}"></span>
|
||||
<span class="record-status--text {{ status.STATUS_CODES[rdoc['status']] }}">
|
||||
{{ status.STATUS_TEXTS[rdoc['status']] }}
|
||||
<span class="icon record-status--icon {{ builtin.STATUS_CODES[rdoc['status']] }}"></span>
|
||||
<span class="record-status--text {{ builtin.STATUS_CODES[rdoc['status']] }}">
|
||||
{{ builtin.STATUS_TEXTS[rdoc['status']] }}
|
||||
</span>
|
||||
</h1>
|
||||
</div>
|
||||
{% if rdoc.compilerTexts %}
|
||||
<div class="section__body">
|
||||
<pre class="compiler-text">{{ rdoc.compilerTexts|join('\n') }}</pre>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if rdoc.judgeTexts %}
|
||||
<div class="section__body">
|
||||
<pre class="judge-text">{{ rdoc.judgeTexts|join('\n') }}</pre>
|
||||
@@ -35,16 +37,16 @@
|
||||
<tbody>
|
||||
{% for rcdoc in rdoc['cases'] %}
|
||||
<tr>
|
||||
<td class="col--case record-status--border {{ status.STATUS_CODES[rcdoc['status']] }}">
|
||||
<td class="col--case record-status--border {{ builtin.STATUS_CODES[rcdoc['status']] }}">
|
||||
#{{ loop.index }}
|
||||
</td>
|
||||
<td class="col--status">
|
||||
<span class="icon record-status--icon {{ status.STATUS_CODES[rcdoc['status']] }}"></span>
|
||||
<span class="record-status--text {{ status.STATUS_CODES[rcdoc['status']] }}">
|
||||
<span class="icon record-status--icon {{ builtin.STATUS_CODES[rcdoc['status']] }}"></span>
|
||||
<span class="record-status--text {{ builtin.STATUS_CODES[rcdoc['status']] }}">
|
||||
{{ status.STATUS_TEXTS[rcdoc['status']] }}
|
||||
</span>
|
||||
<span>{{ rcdoc['judge_text'] }}</span>
|
||||
<a class="tool-button" href="{{ reverse_url('wiki_help') }}#status"><span class="icon icon-help"></span></a>
|
||||
<span>{{ rcdoc['judgeText'] }}</span>
|
||||
<a class="tool-button" href="/wiki/help#status"><span class="icon icon-help"></span></a>
|
||||
</td>
|
||||
<td class="col--time">{% 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'] }}ms</td>
|
||||
<td class="col--memory">{% 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['memory_kb']|format_size(1024) }}</td>
|
||||
|
||||
@@ -29,5 +29,5 @@
|
||||
<td class="col--time">{% if rdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rdoc.time }}ms</td>
|
||||
<td class="col--memory">{% if rdoc['status'] == status.STATUS_TIME_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_MEMORY_LIMIT_EXCEEDED or rdoc['status'] == status.STATUS_OUTPUT_LIMIT_EXCEEDED %}≥{% endif %}{{ rdoc.memory|format_size(1024) }}</td>
|
||||
<td class="col--lang">{{ vj4.constant.language.LANG_TEXTS[rdoc['lang']] }}</td>
|
||||
<td class="col--submit-at">{{ datetime_span(rdoc['_id'].generation_time) }}</td>
|
||||
<td class="col--submit-at">{{ datetime_span(rdoc['_id'].generationTime) }}</td>
|
||||
</tr>
|
||||
@@ -180,7 +180,7 @@
|
||||
<span class="icon icon-edit"></span> {{ _('Edit') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('wiki_help') }}#training">
|
||||
<li class="menu__item"><a class="menu__link" href="/wiki/help#training">
|
||||
<span class="icon icon-help"></span> {{ _('Help') }}
|
||||
</a></li>
|
||||
<li class="menu__seperator"></li>
|
||||
|
||||
@@ -153,10 +153,13 @@ binary-extensions@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c"
|
||||
integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==
|
||||
|
||||
blueimp-md5@^2.13.0:
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.13.0.tgz#07314b0c64dda0bf1733f96ce40d5af94eb28965"
|
||||
integrity sha512-lmp0m647R5e77ORduxLW5mISIDcvgJZa52vMBv5uVI3UmSWTQjkJsZVBfaFqQPw/QFogJwvY6e3Gl9nP+Loe+Q==
|
||||
bl@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-2.2.0.tgz#e1a574cdf528e4053019bb800b041c0ac88da493"
|
||||
integrity sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==
|
||||
dependencies:
|
||||
readable-stream "^2.3.5"
|
||||
safe-buffer "^5.1.1"
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
@@ -174,9 +177,9 @@ braces@~3.0.2:
|
||||
fill-range "^7.0.1"
|
||||
|
||||
bson@^1.1.1:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/bson/-/bson-1.1.3.tgz#aa82cb91f9a453aaa060d6209d0675114a8154d3"
|
||||
integrity sha512-TdiJxMVnodVS7r0BdL42y/pqC9cL2iKynVwA0Ho3qbsQYr428veL3l7BQyuqiw+Q5SqqoT0m4srSY/BlZ9AxXg==
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/bson/-/bson-1.1.4.tgz#f76870d799f15b854dffb7ee32f0a874797f7e89"
|
||||
integrity sha512-S/yKGU1syOMzO86+dGpg2qGoDL0zvzcb262G+gqEy6TgP6rt6z6qxSFX/8X6vLC91P7G7C3nLs0+bvDzmvBA3Q==
|
||||
|
||||
bson@^4.0.2:
|
||||
version "4.0.2"
|
||||
@@ -310,6 +313,11 @@ cookies@~0.8.0:
|
||||
depd "~2.0.0"
|
||||
keygrip "~1.1.0"
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
|
||||
|
||||
cross-spawn@^6.0.5:
|
||||
version "6.0.5"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
|
||||
@@ -364,6 +372,11 @@ delegates@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
|
||||
integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
|
||||
|
||||
denque@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf"
|
||||
integrity sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==
|
||||
|
||||
depd@^1.1.2, depd@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
||||
@@ -743,7 +756,7 @@ inflight@^1.0.4:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2, inherits@2.0.4:
|
||||
inherits@2, inherits@2.0.4, inherits@~2.0.3:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
@@ -826,6 +839,11 @@ isarray@0.0.1:
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
|
||||
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
|
||||
|
||||
isarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
|
||||
|
||||
isexe@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
@@ -1068,12 +1086,14 @@ mkdirp@^0.5.1:
|
||||
dependencies:
|
||||
minimist "0.0.8"
|
||||
|
||||
mongodb@^3.3.4:
|
||||
version "3.3.4"
|
||||
resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.3.4.tgz#f52eec4a04005101e63715d4d1f67bf784fa0aef"
|
||||
integrity sha512-6fmHu3FJTpeZxacJcfjUGIP3BSteG0l2cxLkSrf1nnnS1OrlnVGiP9P/wAC4aB6dM6H4vQ2io8YDjkuPkje7AA==
|
||||
mongodb@^3.5.5:
|
||||
version "3.5.5"
|
||||
resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.5.5.tgz#1334c3e5a384469ac7ef0dea69d59acc829a496a"
|
||||
integrity sha512-GCjDxR3UOltDq00Zcpzql6dQo1sVry60OXJY3TDmFc2SWFY6c8Gn1Ardidc5jDirvJrx2GC3knGOImKphbSL3A==
|
||||
dependencies:
|
||||
bl "^2.2.0"
|
||||
bson "^1.1.1"
|
||||
denque "^1.4.1"
|
||||
require_optional "^1.0.1"
|
||||
safe-buffer "^5.1.2"
|
||||
optionalDependencies:
|
||||
@@ -1242,6 +1262,11 @@ prelude-ls@~1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
|
||||
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
|
||||
|
||||
process-nextick-args@~2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
||||
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
|
||||
|
||||
progress@^2.0.0:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
|
||||
@@ -1267,6 +1292,19 @@ raw-body@^2.2.0:
|
||||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
readable-stream@^2.3.5:
|
||||
version "2.3.7"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
||||
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.3"
|
||||
isarray "~1.0.0"
|
||||
process-nextick-args "~2.0.0"
|
||||
safe-buffer "~5.1.1"
|
||||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
readdirp@~3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.3.0.tgz#984458d13a1e42e2e9f5841b129e162f369aff17"
|
||||
@@ -1334,12 +1372,12 @@ rxjs@^6.4.0:
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
safe-buffer@5.1.2:
|
||||
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
safe-buffer@>=5.1.0, safe-buffer@^5.1.2:
|
||||
safe-buffer@>=5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
|
||||
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
|
||||
@@ -1446,6 +1484,13 @@ string-width@^4.1.0:
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
string_decoder@~1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
|
||||
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
|
||||
dependencies:
|
||||
safe-buffer "~5.1.0"
|
||||
|
||||
strip-ansi@^5.1.0, strip-ansi@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
|
||||
@@ -1577,6 +1622,11 @@ urijs@^1.19.0:
|
||||
resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.2.tgz#f9be09f00c4c5134b7cb3cf475c1dd394526265a"
|
||||
integrity sha512-s/UIq9ap4JPZ7H1EB5ULo/aOUbWqfDi7FKzMC2Nz+0Si8GiT1rIEaprt8hy3Vy2Ex2aJPpOQv4P4DuOZ+K1c6w==
|
||||
|
||||
util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
uuid@^3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||
|
||||
Reference in new issue
Block a user