mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 09:36:08 +08:00
比赛功能也太难写了
This commit is contained in:
1 parent
f73859a42f
commit
32ad47e031
16 files changed
+195
-176
No files matched your search
+19
-11
@@ -1,6 +1,6 @@
|
||||
const
|
||||
{ ContestNotLiveError, ValidationError, ProblemNotFoundError,
|
||||
ContestNotAttendedError,ContestScoreboardHiddenError } = require('../error'),
|
||||
ContestNotAttendedError, ContestScoreboardHiddenError } = require('../error'),
|
||||
{ PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD, PERM_CREATE_CONTEST,
|
||||
PERM_EDIT_CONTEST } = require('../permission'),
|
||||
{ constants } = require('../options'),
|
||||
@@ -22,7 +22,7 @@ class ContestHandler extends Handler {
|
||||
return false;
|
||||
}
|
||||
canShowScoreboard(tdoc, allowPermOverride = true) {
|
||||
if (contest.RULES[tdoc.rule].showScoreboardFunc(tdoc, new Date())) return true;
|
||||
if (contest.RULES[tdoc.rule].showScoreboard(tdoc, new Date())) return true;
|
||||
if (allowPermOverride && this.canViewHiddenScoreboard(tdoc)) return true;
|
||||
return false;
|
||||
}
|
||||
@@ -37,12 +37,17 @@ class ContestHandler extends Handler {
|
||||
return tdoc, rows, udict;
|
||||
}
|
||||
async verifyProblems(pids) {
|
||||
let pdocs = await problem.getMulti({ _id: { $in: pids } }).sort({ doc_id: 1 }).toArray();
|
||||
console.log(pids);
|
||||
let pdocs = await problem.getMulti({
|
||||
$or: [
|
||||
{ _id: { $in: pids } }, { pid: { $in: pids } }
|
||||
]
|
||||
}).sort({ _id: 1 }).toArray();
|
||||
if (pids.length != pdocs.length)
|
||||
for (let pid of pids) {
|
||||
let p = false;
|
||||
for (let pdoc of pdocs)
|
||||
if (pid == pdoc._id) {
|
||||
if (pid == pdoc._id || pid == pdoc.pid) {
|
||||
p = true;
|
||||
break;
|
||||
}
|
||||
@@ -105,7 +110,7 @@ class ContestDetailHandler extends ContestHandler {
|
||||
};
|
||||
}
|
||||
async post_attend() {
|
||||
if (contest.isDone(this.tdoc)) throw new ContestNotLiveError(this.tdoc._id);
|
||||
if (contest.is_done(this.tdoc)) throw new ContestNotLiveError(this.tdoc._id);
|
||||
await contest.attend(this.tdoc._id, this.uid);
|
||||
this.back();
|
||||
}
|
||||
@@ -141,7 +146,7 @@ class ContestEditHandler extends ContestDetailHandler {
|
||||
let dt = this.tdoc.beginAt;
|
||||
this.response.body = {
|
||||
rules, tdoc: this.tdoc, duration, path, pids: this.tdoc.pids.join(','),
|
||||
date_text: `${dt.getFullYear()}-${dt.getMonth()}-${dt.getDate()}`,
|
||||
date_text: `${dt.getFullYear()}-${dt.getMonth() + 1}-${dt.getDate()}`,
|
||||
time_text: `${dt.getHours()}:${dt.getMinutes()}`,
|
||||
page_name: 'contest_edit'
|
||||
};
|
||||
@@ -149,7 +154,7 @@ class ContestEditHandler extends ContestDetailHandler {
|
||||
async post({ beginAtDate, beginAtTime, duration, title, content, rule, pids }) {
|
||||
let beginAt, endAt;
|
||||
try {
|
||||
beginAt = Date.parse(`${beginAtDate}T${beginAtTime}+8:00`);
|
||||
beginAt = new Date(Date.parse(`${beginAtDate} ${beginAtTime.replace('-', ':')}`));
|
||||
} catch (e) {
|
||||
throw new ValidationError('beginAtDate', 'beginAtTime');
|
||||
}
|
||||
@@ -237,19 +242,22 @@ class ContestCreateHandler extends ContestHandler {
|
||||
let dt = new Date(ts);
|
||||
this.response.body = {
|
||||
rules, page_name: 'contest_create',
|
||||
date_text: `${dt.getFullYear()}-${dt.getMonth()}-${dt.getDate()}`,
|
||||
time_text: `${dt.getHours()}-${dt.getMinutes()}`,
|
||||
date_text: `${dt.getFullYear()}-${dt.getMonth() + 1}-${dt.getDate()}`,
|
||||
time_text: `${dt.getHours()}:${dt.getMinutes()}`,
|
||||
pids: '1000, 1001'
|
||||
};
|
||||
}
|
||||
async post({ title, content, rule, beginAtDate, beginAtTime, duration, pids }) {
|
||||
let beginAt, endAt;
|
||||
console.log(beginAtDate, beginAtTime);
|
||||
try {
|
||||
beginAt = Date.parse(`${beginAtDate}T${beginAtTime}+8:00`);
|
||||
beginAt = new Date(Date.parse(`${beginAtDate} ${beginAtTime.replace('-', ':')}`));
|
||||
console.log(beginAt, duration);
|
||||
} catch (e) {
|
||||
throw new ValidationError('beginAtDate', 'beginAtTime');
|
||||
}
|
||||
endAt = new Date(beginAt + duration * 3600 * 1000);
|
||||
endAt = new Date(beginAt.getTime() + duration * 3600 * 1000);
|
||||
console.log(endAt);
|
||||
if (beginAt >= endAt) throw new ValidationError('duration');
|
||||
await this.verifyProblems(pids);
|
||||
let tid = await contest.add(title, content, this.user._id, rule, beginAt, endAt, pids);
|
||||
|
||||
@@ -52,14 +52,11 @@ class ProblemRandomHandler extends ProblemHandler {
|
||||
}
|
||||
|
||||
class ProblemDetailHandler extends ProblemHandler {
|
||||
constructor(ctx) {
|
||||
super(ctx);
|
||||
this.uid = ctx.state.user._id;
|
||||
this.pid = ctx.params.pid;
|
||||
async _prepare({ pid }) {
|
||||
this.response.template = 'problem_detail.html';
|
||||
}
|
||||
async _prepare() {
|
||||
if (this.pid) this.pdoc = await problem.get(this);
|
||||
this.uid = this.user._id;
|
||||
this.pid = pid;
|
||||
if (pid) this.pdoc = await problem.get(this);
|
||||
if (this.pdoc.hidden && this.pdoc.owner != this.uid) this.checkPerm(PERM_VIEW_PROBLEM_HIDDEN);
|
||||
if (this.pdoc) this.udoc = await user.getById(this.pdoc.owner);
|
||||
this.response.body = {
|
||||
|
||||
+2
-2
@@ -2,13 +2,13 @@ const md5 = require('./md5');
|
||||
exports.gravatar_url = function (email, size) {
|
||||
return `//gravatar.loli.net/avatar/${md5((email || '').toString().trim().toLowerCase())}?d=mm&s=${size}`;
|
||||
};
|
||||
exports.datetime_span = function (dt, { relative = true } = {}) {
|
||||
exports.datetime_span = function (dt, relative = true, format = '%Y-%m-%d %H:%M:%S') {
|
||||
if (dt.generationTime) dt = new Date(dt.generationTime * 1000);
|
||||
else if (typeof dt == 'number' || typeof dt == 'string') dt = new Date(dt);
|
||||
return '<span class="time{0}" data-timestamp="{1}">{2}</span>'.format(
|
||||
relative ? ' relative' : '',
|
||||
dt.getTime() / 1000,
|
||||
dt.toLocaleString()
|
||||
dt.format(format)
|
||||
);
|
||||
};
|
||||
exports.paginate = function* (page, num_pages) {
|
||||
|
||||
+77
-63
@@ -12,82 +12,96 @@ const RULES = {
|
||||
acm: require('../module/contest/acm')
|
||||
};
|
||||
|
||||
async function add(title, content, owner, rule,
|
||||
beginAt = new Date(), endAt = new Date(), pids = [], data = {}) {
|
||||
validator.checkTitle(title);
|
||||
validator.checkContent(content);
|
||||
if (!this.RULES[rule]) throw new ValidationError('rule');
|
||||
if (beginAt >= endAt) throw new ValidationError('beginAt', 'endAt');
|
||||
Object.assign(data, { content, owner, title, rule, beginAt, endAt, pids, attend: 0 });
|
||||
this.RULES[rule].check(data);
|
||||
let res = await coll.insertOne(data);
|
||||
return res.insertedId;
|
||||
}
|
||||
async function edit(tid, $set) {
|
||||
if ($set.title) validator.checkTitle($set.title);
|
||||
if ($set.content) validator.checkIntro($set.content);
|
||||
if ($set.rule)
|
||||
if (!this.RULES[$set.rule]) throw new ValidationError('rule');
|
||||
if ($set.beginAt && $set.endAt)
|
||||
if ($set.beginAt >= $set.endAt) throw new ValidationError('beginAt', 'endAt');
|
||||
let tdoc = await coll.findOne({ tid });
|
||||
if (!tdoc) throw new ContestNotFoundError(tid);
|
||||
this.RULES[$set.rule || tdoc.rule].check(Object.assign(tdoc, $set));
|
||||
await coll.findOneAndUpdate({ tid }, { $set });
|
||||
return tdoc;
|
||||
}
|
||||
async function get(tid) {
|
||||
let tdoc = await coll.findOne({ _id: tid });
|
||||
if (!tdoc) throw new ContestNotFoundError(tid);
|
||||
return tdoc;
|
||||
}
|
||||
async function get_random_id(query) {
|
||||
let pdocs = coll.find(query);
|
||||
let pcount = await pdocs.count();
|
||||
if (pcount) {
|
||||
let pdoc = await pdocs.skip(Math.floor(Math.random() * pcount)).limit(1).toArray()[0];
|
||||
return pdoc.pid;
|
||||
} else return null;
|
||||
}
|
||||
async function getListStatus(uid, tids) {
|
||||
let r = {};
|
||||
for (let tid of tids) r[tid] = await this.getStatus(tid, uid);
|
||||
return r;
|
||||
}
|
||||
async function attend(tid, uid) {
|
||||
try {
|
||||
await coll_status.insertOne({ tid, uid });
|
||||
} catch (e) {
|
||||
throw new ContestAlreadyAttendedError(tid, uid);
|
||||
}
|
||||
await coll.findOneAndUpdate({ _id: tid }, { $inc: { attend: 1 } });
|
||||
}
|
||||
function is_new(tdoc, days = 1) {
|
||||
let now = new Date().getTime();
|
||||
let readyAt = tdoc.beginAt.getTime();
|
||||
return (now < readyAt - days * 24 * 3600 * 1000);
|
||||
}
|
||||
function is_upcoming(tdoc, days = 1) {
|
||||
let now = new Date().getTime();
|
||||
let readyAt = tdoc.beginAt.getTime();
|
||||
return (now > readyAt + days * 24 * 3600 * 1000 && now < tdoc.beginAt);
|
||||
}
|
||||
function is_not_started(tdoc) {
|
||||
return new Date() < tdoc.beginAt;
|
||||
}
|
||||
function is_ongoing(tdoc) {
|
||||
let now = new Date();
|
||||
return (tdoc.beginAt <= now && now < tdoc.endAt);
|
||||
}
|
||||
function is_done(tdoc) {
|
||||
return tdoc.endAt <= new Date();
|
||||
}
|
||||
module.exports = {
|
||||
RULES,
|
||||
async add(title, content, owner, rule,
|
||||
beginAt = new Date(), endAt = new Date(), pids = [], data) {
|
||||
validator.checkTitle(title);
|
||||
validator.checkContent(content);
|
||||
if (!this.RULES[rule]) throw new ValidationError('rule');
|
||||
if (beginAt >= endAt) throw new ValidationError('beginAt', 'endAt');
|
||||
Object.assign(data, { content, owner, title, rule, beginAt, endAt, pids, attend: 0 });
|
||||
this.RULES[rule].check(data);
|
||||
return await coll.insertOne(data);
|
||||
},
|
||||
RULES, add, getListStatus, attend, edit, get, get_random_id,
|
||||
count: query => coll.find(query).count(),
|
||||
async edit(tid, $set) {
|
||||
if ($set.title) validator.checkTitle($set.title);
|
||||
if ($set.content) validator.checkIntro($set.content);
|
||||
if ($set.rule)
|
||||
if (!this.RULES[$set.rule]) throw new ValidationError('rule');
|
||||
if ($set.beginAt && $set.endAt)
|
||||
if ($set.beginAt >= $set.endAt) throw new ValidationError('beginAt', 'endAt');
|
||||
let tdoc = await coll.findOne({ tid });
|
||||
if (!tdoc) throw new ContestNotFoundError(tid);
|
||||
this.RULES[$set.rule || tdoc.rule].check(Object.assign(tdoc, $set));
|
||||
await coll.findOneAndUpdate({ tid }, { $set });
|
||||
return tdoc;
|
||||
},
|
||||
async get(tid) {
|
||||
let tdoc = await coll.findOne({ tid });
|
||||
if (!tdoc) throw new ContestNotFoundError(tid);
|
||||
return tdoc;
|
||||
},
|
||||
getMulti: query => coll.find(query),
|
||||
getMultiStatus: query => coll_status.find(query),
|
||||
async get_random_id(query) {
|
||||
let pdocs = coll.find(query);
|
||||
let pcount = await pdocs.count();
|
||||
if (pcount) {
|
||||
let pdoc = await pdocs.skip(Math.floor(Math.random() * pcount)).limit(1).toArray()[0];
|
||||
return pdoc.pid;
|
||||
} else return null;
|
||||
},
|
||||
getStatus: (tid, uid) => coll_status.findOne({ tid, uid }),
|
||||
setStatus: (tid, uid, $set) => coll_status.findOneAndUpdate({ tid, uid }, { $set }),
|
||||
async getListStatus(uid, tids) {
|
||||
let r = {};
|
||||
for (let tid of tids) r[tid] = await this.getStatus(tid, uid);
|
||||
return r;
|
||||
},
|
||||
is_new(tdoc, days = 1) {
|
||||
let now = new Date().getTime();
|
||||
let readyAt = tdoc.beginAt.getTime();
|
||||
return (now < readyAt - days * 24 * 3600 * 1000);
|
||||
},
|
||||
is_upcoming(tdoc, days = 1) {
|
||||
let now = new Date().getTime();
|
||||
let readyAt = tdoc.beginAt.getTime();
|
||||
return (now > readyAt + days * 24 * 3600 * 1000 && now < tdoc.beginAt);
|
||||
},
|
||||
is_not_started: tdoc => new Date() < tdoc.beginAt,
|
||||
is_ongoing(tdoc) {
|
||||
let now = new Date();
|
||||
return (tdoc.beginAt <= now && now < tdoc.endAt);
|
||||
},
|
||||
is_done: tdoc => tdoc.endAt <= new Date(),
|
||||
is_new, is_upcoming, is_not_started, is_ongoing, is_done,
|
||||
status_text: tdoc =>
|
||||
this.is_new(tdoc)
|
||||
is_new(tdoc)
|
||||
? 'New'
|
||||
: this.is_upcoming(tdoc)
|
||||
: is_upcoming(tdoc)
|
||||
? 'Ready (☆▽☆)'
|
||||
: this.is_ongoing(tdoc)
|
||||
: is_ongoing(tdoc)
|
||||
? 'Live...'
|
||||
: 'Done',
|
||||
get_status: tdoc =>
|
||||
this.is_not_started(tdoc)
|
||||
is_not_started(tdoc)
|
||||
? 'not_started'
|
||||
: this.is_ongoing(tdoc)
|
||||
: is_ongoing(tdoc)
|
||||
? 'ongoing'
|
||||
: 'finished',
|
||||
};
|
||||
|
||||
@@ -85,6 +85,11 @@ async function random(query) {
|
||||
return pdoc.pid;
|
||||
} else return null;
|
||||
}
|
||||
async function getList(pids) {
|
||||
let r = {};
|
||||
for (let pid of pids) r[pid] = await get({pid});
|
||||
return r;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
add,
|
||||
@@ -94,5 +99,6 @@ module.exports = {
|
||||
count,
|
||||
random,
|
||||
getById,
|
||||
getMulti
|
||||
getMulti,
|
||||
getList
|
||||
};
|
||||
@@ -10,6 +10,9 @@ module.exports = {
|
||||
}
|
||||
return { score, detail };
|
||||
},
|
||||
showScoreboard(tdoc,now){
|
||||
return now>tdoc.endAt;
|
||||
},
|
||||
scoreboard(is_export, _, tdoc, ranked_tsdocs, udict, dudict, pdict) {
|
||||
let columns = [
|
||||
{ type: 'rank', value: _('Rank') },
|
||||
|
||||
+27
-30
@@ -13,8 +13,7 @@ const
|
||||
blacklist = require('../model/blacklist'),
|
||||
token = require('../model/token'),
|
||||
opcount = require('../model/opcount'),
|
||||
{ UserNotFoundError, BlacklistedError, PermissionError,
|
||||
NotFoundError } = require('../error');
|
||||
{ UserNotFoundError, BlacklistedError, PermissionError } = require('../error');
|
||||
|
||||
const options = require('../options');
|
||||
let http = options.listen.https ? require('https') : require('http');
|
||||
@@ -128,40 +127,33 @@ class Handler {
|
||||
async ___cleanup() {
|
||||
try {
|
||||
await this.renderBody();
|
||||
} catch (e) {
|
||||
try {
|
||||
let error;
|
||||
if (this.response.body && this.response.body.error) error = this.response.body.error;
|
||||
else error = e;
|
||||
if (error instanceof NotFoundError) this.response.status = 404;
|
||||
if (error.name == 'Template render error') throw error;
|
||||
if (this.preferJson) this.response.body = { error };
|
||||
else await this.render('error.html', { error });
|
||||
} catch (error) {
|
||||
if (this.preferJson) this.response.body = { error };
|
||||
else await this.render('bsod.html', { error });
|
||||
}
|
||||
} catch (error) {
|
||||
if (this.preferJson) this.response.body = { error };
|
||||
else await this.render('bsod.html', { error });
|
||||
}
|
||||
await this.putResponse();
|
||||
await this.saveCookie();
|
||||
}
|
||||
async renderBody() {
|
||||
if (this.response.body || this.response.template) {
|
||||
if (this.request.query.noTemplate || this.preferJson) return;
|
||||
if (this.request.query.template || this.response.template) {
|
||||
this.response.body = this.response.body || {};
|
||||
await this.render(this.request.query.template || this.response.template, this.response.body);
|
||||
if (!this.preferJson)
|
||||
if (this.response.body || this.response.template) {
|
||||
if (this.request.query.noTemplate || this.preferJson) return;
|
||||
if (this.request.query.template || this.response.template) {
|
||||
this.response.body = this.response.body || {};
|
||||
await this.render(this.request.query.template || this.response.template, this.response.body);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
async putResponse() {
|
||||
if (this.response.redirect) {
|
||||
this.response.type = 'application/octet-stream';
|
||||
this.redirect(this.response.redirect);
|
||||
if (this.response.redirect && !this.preferJson) {
|
||||
this.ctx.response.type = 'application/octet-stream';
|
||||
this.ctx.response.status = 302;
|
||||
this.ctx.redirect(this.response.redirect);
|
||||
} else {
|
||||
if (this.response.body) this.ctx.body = this.response.body;
|
||||
if (this.response.type) this.ctx.response.type = this.response.type;
|
||||
if (this.response.status) this.ctx.response.status = this.response.status;
|
||||
}
|
||||
if (this.response.body) this.ctx.body = this.response.body;
|
||||
if (this.response.type) this.ctx.response.type = this.response.type;
|
||||
if (this.response.status) this.ctx.response.status = this.response.status;
|
||||
}
|
||||
async saveCookie() {
|
||||
if (this.session.sid)
|
||||
@@ -184,9 +176,11 @@ class Handler {
|
||||
}
|
||||
this.ctx.cookies.set('sid', this.session.sid, cookie);
|
||||
}
|
||||
async onerror(e) {
|
||||
console.error(e.message, e.params);
|
||||
console.error(e.stack);
|
||||
async onerror(error) {
|
||||
console.error(error.message, error.params);
|
||||
console.error(error.stack);
|
||||
this.response.template = 'error.html';
|
||||
this.response.body = { error };
|
||||
await this.___cleanup();
|
||||
}
|
||||
}
|
||||
@@ -203,6 +197,8 @@ function Route(route, handler) {
|
||||
if (args.page) args.page = parseInt(args.page);
|
||||
if (args.rid) args.rid = new ObjectID(args.rid);
|
||||
if (args.tid) args.tid = new ObjectID(args.tid);
|
||||
if (args.duration) args.duration = parseFloat(args.duration);
|
||||
if (args.pids) args.pids = args.pids.split(',').map(i => i.trim());
|
||||
|
||||
if (h.___prepare) await h.___prepare(args);
|
||||
if (h.__prepare) await h.__prepare(args);
|
||||
@@ -330,6 +326,7 @@ exports.Connection = Connection;
|
||||
exports.start = function start() {
|
||||
app.use(morgan(':method :url :status :res[content-length] - :response-time ms'));
|
||||
app.use(router.routes()).use(router.allowedMethods());
|
||||
Route('*', Handler);
|
||||
server.listen(options.listen.port);
|
||||
console.log('Server listening at: %s', options.listen.port);
|
||||
};
|
||||
@@ -27,3 +27,14 @@ Array.isDiff = function isDiff(a, b) {
|
||||
if (a[i] != b[i]) return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
Date.prototype.format = function (fmt = '%Y-%m-%d %H:%M:%S') {
|
||||
return fmt
|
||||
.replace('%Y', this.getFullYear())
|
||||
.replace('%m', this.getMonth() + 1)
|
||||
.replace('%D', this.getDate())
|
||||
.replace('%d', this.getDate())
|
||||
.replace('%H', this.getHours())
|
||||
.replace('%M', this.getMinutes())
|
||||
.replace('%S', this.getSeconds());
|
||||
};
|
||||
@@ -1,8 +1,8 @@
|
||||
{% import "components/user.html" as user with context %}
|
||||
{% macro render_time(tdoc_time) %}
|
||||
{{ datetime_span(tdoc_time, false, '%Y-%m-%d %H:%M') }}
|
||||
{{ datetime_span(tdoc_time, false, '%Y-%m-%d %H:%M')|safe }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_duration(tdoc) %}
|
||||
{{ ((tdoc['end_at'] - tdoc['begin_at']).total_seconds() / 3600)|round(1) }}
|
||||
{{ ((tdoc.endAt.getTime() - tdoc.beginAt.getTime()) /1000 / 3600)|round(1) }}
|
||||
{% endmacro %}
|
||||
@@ -1,3 +1,4 @@
|
||||
{% set page_name = "contest_detail" %}
|
||||
{% extends "layout/basic.html" %}
|
||||
{% import "components/contest.html" as contest with context %}
|
||||
{% import "components/record.html" as record with context %}
|
||||
@@ -7,20 +8,20 @@
|
||||
<div class="medium-9 columns">
|
||||
<div class="section">
|
||||
<div class="section__body typo">
|
||||
{{ tdoc['content']|markdown }}
|
||||
{% if handler.is_ongoing(tdoc) %}
|
||||
{{ tdoc['content']|markdown|safe }}
|
||||
{% if model.contest.is_ongoing(tdoc) %}
|
||||
<blockquote class="warn">
|
||||
<p>{{ _('We will check code similarity after this contest.') }}</p>
|
||||
</blockquote>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% if handler.is_ongoing(tdoc) or handler.is_done(tdoc) %}
|
||||
{% if model.contest.is_ongoing(tdoc) or model.contest.is_done(tdoc) %}
|
||||
<div class="section">
|
||||
<div class="section__header">
|
||||
<h1 class="section__title">{{ _('Problem') }}</h1>
|
||||
</div>
|
||||
{% if attended or handler.is_done(tdoc) %}
|
||||
{% if attended or model.contest.is_done(tdoc) %}
|
||||
<div class="section__body no-padding">
|
||||
<table class="data-table">
|
||||
<colgroup>
|
||||
@@ -40,18 +41,17 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for pid in tdoc['pids'] %}
|
||||
{% for pid in tdoc.pids %}
|
||||
<tr>
|
||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
{% if psdict[pid] and psdict[pid]['rid'] %}
|
||||
{% with rdoc=rdict[psdict[pid]['rid']] %}
|
||||
{% if psdict[pid] and psdict[pid].rid %}
|
||||
{% set rdoc = rdict[psdict[pid].rid] %}
|
||||
{% if handler.can_show_record(tdoc) %}
|
||||
{{ record.render_status_td(rdoc) }}
|
||||
{% else %}
|
||||
<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'].generationTime) }}</td>
|
||||
{% endwith %}
|
||||
<td class="col--submit-at">{{ datetime_span(rdoc._id) }}</td>
|
||||
{% else %}
|
||||
<td class="col--status record-status--border">{{ _('No Submissions') }}</td>
|
||||
<td class="col--submit-at">-</td>
|
||||
@@ -73,9 +73,8 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="medium-3 columns">
|
||||
{% with owner_udoc=udict[tdoc['owner_uid']], owner_dudoc=dudict[tdoc['owner_uid']] %}
|
||||
{% include "partials/contest_sidebar.html" %}
|
||||
{% endwith %}
|
||||
{% set owner_udoc = udict[tdoc.owner] %}
|
||||
{% include "partials/contest_sidebar.html" %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -17,11 +17,11 @@
|
||||
</li>
|
||||
<li>
|
||||
<span class="icon icon-calendar"></span>
|
||||
{{ _('Start at') }}: {{ model.contest.render_time(tdoc['beginAt']) }}
|
||||
{{ _('Start at') }}: {{ contest.render_time(tdoc.beginAt) }}
|
||||
</li>
|
||||
<li>
|
||||
<span class="icon icon-schedule--fill"></span>
|
||||
{{ _('Duration') }}: {{ model.contest.render_duration(tdoc) }} {{ _('hour(s)') }}
|
||||
{{ _('Duration') }}: {{ contest.render_duration(tdoc) }} {{ _('hour(s)') }}
|
||||
</li>
|
||||
<li>
|
||||
<span class="icon icon-user--multiple"></span>
|
||||
@@ -32,7 +32,7 @@
|
||||
</div>
|
||||
<div class="media__right middle">
|
||||
<div class="section__body">
|
||||
<a href="/t/{{ tdoc._id }}" class="detail-button">{{ _('View Details') }}</a>
|
||||
<a href="/c/{{ tdoc._id }}" class="detail-button">{{ _('View Details') }}</a>
|
||||
<div class="typo text-center">
|
||||
{% if tsdict[tdoc._id].attend %}
|
||||
<p><span class="icon icon-check"></span> {{ _('Attended') }}</p>
|
||||
@@ -49,9 +49,9 @@
|
||||
<div class="row">
|
||||
<div class="medium-9 columns">
|
||||
{% for tdoc in tdocs %}
|
||||
{% if handler.is_ongoing(tdoc) %}
|
||||
{% if model.contest.is_ongoing(tdoc) %}
|
||||
{{ render_important_contest('live', 'Live...', tdoc) }}
|
||||
{% elif handler.is_upcoming(tdoc) %}
|
||||
{% elif model.contest.is_upcoming(tdoc) %}
|
||||
{{ render_important_contest('ready', 'Ready (☆▽☆)', tdoc) }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
@@ -83,12 +83,12 @@
|
||||
<div class="media">
|
||||
<div class="media__left medium">
|
||||
<div class="contest__date numbox">
|
||||
<div class="numbox__num large">{{ datetime_span(tdoc['beginAt'], false, '%d') }}</div>
|
||||
<div class="numbox__text">{{ datetime_span(tdoc['beginAt'], false, '%Y-%m') }}</div>
|
||||
<div class="numbox__num large">{{ datetime_span(tdoc['beginAt'], false, '%d')|safe }}</div>
|
||||
<div class="numbox__text">{{ datetime_span(tdoc['beginAt'], false, '%Y-%m')|safe }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="media__body medium">
|
||||
<h1 class="contest__title"><a href="/t/{{ tdoc._id }}" data-emoji-enabled>{{ tdoc['title'] }}</a></h1>
|
||||
<h1 class="contest__title"><a href="/c/{{ tdoc._id }}" data-emoji-enabled>{{ tdoc['title'] }}</a></h1>
|
||||
<ul class="supplementary list">
|
||||
<li>
|
||||
<a href="?rule={{ tdoc['rule'] }}" class="contest-type-tag"><span class="icon icon-award"></span>{{ model.contest.RULES[tdoc.rule].TEXT }}</a>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="media__body medium">
|
||||
<h1 class="contest__title"><a href="/t/{{ tdoc._id }}" data-emoji-enabled>{{ tdoc['title'] }}</a></h1>
|
||||
<h1 class="contest__title"><a href="/c/{{ tdoc._id }}" data-emoji-enabled>{{ tdoc['title'] }}</a></h1>
|
||||
<ul class="supplementary list">
|
||||
<li>
|
||||
<a href="{{ reverse_url('contest_main') }}?rule={{ tdoc['rule'] }}" class="contest-type-tag"><span class="icon icon-award"></span>{{ vj4.constant.contest.RULE_TEXTS[tdoc['rule']] }}</a>
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
</div>
|
||||
<div class="error__text-container">
|
||||
<h1>{{ _('Oops!') }}</h1>
|
||||
<p>{{ _(error.message).format(error.args) }}</p>
|
||||
<p>{{ _(error.message).format(error.params) }}</p>
|
||||
<p>{{ _('Technical Information') }}:</p>
|
||||
<p>{{ _('Type') }}: {{ error.code }}</p>
|
||||
<p>{{ _('Arguments') }}:
|
||||
<ol>
|
||||
{% for arg in error.args %}
|
||||
<li>{{ arg }}</li>
|
||||
{% for param in error.params %}
|
||||
<li>{{ param }}</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
</p>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{% import "components/contest.html" as contest with context %}
|
||||
<div class="section side">
|
||||
{% if page_name != 'contest_detail' %}
|
||||
<a class="contest-sidebar__bg" href="/t/{{ tdoc._id }}">
|
||||
<a class="contest-sidebar__bg" href="/c/{{ tdoc._id }}">
|
||||
<div class="section__body">
|
||||
<h1>{{ tdoc['title'] }}</h1>
|
||||
<div class="contest-sidebar__status">
|
||||
@@ -17,31 +17,15 @@
|
||||
{% endif %}
|
||||
<div>
|
||||
<ol class="menu">
|
||||
{% if page_name == 'discussion_node' %}
|
||||
{% if handler.hasPerm(perm.PERM_CREATE_DISCUSSION) %}
|
||||
<li class="menu__item"><a class="menu__link highlight" href="{{ reverse_url('discussion_create_document_as_node', doc_type=tdoc['doc_type'], doc_id=tdoc['doc_id']) }}">
|
||||
<span class="icon icon-add"></span> {{ _('Create a Discussion') }}
|
||||
</a></li>
|
||||
{% else %}
|
||||
{% if not handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
<li class="menu__item"><a class="menu__link" href="javascript:showSignInDialog();">
|
||||
<span class="icon icon-add"></span> {{ _('Login to Create a Discussion') }}</a></li>
|
||||
{% else %}
|
||||
<li class="menu__item"><a class="menu__link disabled" href="javascript:;">
|
||||
<span class="icon icon-add"></span> {{ _('No Permission to Create a Discussion') }}</a></li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<li class="menu__seperator"></li>
|
||||
{% endif %}
|
||||
{% if page_name != 'contest_detail' %}
|
||||
<li class="menu__item"><a class="menu__link" href="/t/{{ tdoc._id }}">
|
||||
<li class="menu__item"><a class="menu__link" href="/c/{{ tdoc._id }}">
|
||||
<span class="icon icon-award"></span> {{ _('View Contest') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if not attended and not handler.is_done(tdoc) %}
|
||||
{% if not attended and not model.contest.is_done(tdoc) %}
|
||||
<li class="menu__item">
|
||||
{% if handler.hasPerm(perm.PERM_ATTEND_CONTEST) and handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||
<form action="/t/{{ tdoc._id }}" method="POST">
|
||||
<form action="/c/{{ tdoc._id }}" method="POST">
|
||||
<input type="hidden" name="operation" value="attend">
|
||||
<input type="hidden" name="csrf_token" value="{{ handler.csrf_token }}">
|
||||
<button class="menu__link" type="submit">
|
||||
@@ -59,26 +43,26 @@
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if handler.can_show_scoreboard(tdoc, False) %}
|
||||
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('contest_scoreboard', tid=tdoc['doc_id']) }}">
|
||||
{% if handler.canShowScoreboard(tdoc, False) %}
|
||||
<li class="menu__item"><a class="menu__link" href="/c/{{ tdoc._id }}/scoreboard">
|
||||
<span class="icon icon-statistics"></span> {{ _('Scoreboard') }}
|
||||
</a></li>
|
||||
{% elif handler.can_view_hidden_scoreboard(tdoc) %}
|
||||
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('contest_scoreboard', tid=tdoc['doc_id']) }}">
|
||||
{% elif handler.canViewHiddenScoreboard(tdoc) %}
|
||||
<li class="menu__item"><a class="menu__link" href="/c/{{ tdoc._id }}/scoreboard">
|
||||
<span class="icon icon-statistics"></span> {{ _('Scoreboard (Hidden)') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if handler.own(tdoc, perm.PERM_EDIT_CONTEST_SELF) or handler.hasPerm(perm.PERM_EDIT_CONTEST) %}
|
||||
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('contest_edit', tid=tdoc['doc_id']) }}">
|
||||
{% if user._id == tdoc.owner or handler.hasPerm(perm.PERM_EDIT_CONTEST) %}
|
||||
<li class="menu__item"><a class="menu__link" href="/c/{{ tdoc._id }}/edit">
|
||||
<span class="icon icon-edit"></span> {{ _('Edit Contest') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if handler.hasPerm(perm.PERM_READ_RECORD_CODE) %}
|
||||
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('contest_code', tid=tdoc['doc_id']) }}">
|
||||
<li class="menu__item"><a class="menu__link" href="/c/{{ tdoc._id }}/code">
|
||||
<span class="icon icon-download"></span> {{ _('Export All Code') }}
|
||||
</a></li>
|
||||
{% endif %}
|
||||
<li class="menu__item"><a class="menu__link" href="/r?tid={{ tdoc['doc_id'] }}">
|
||||
<li class="menu__item"><a class="menu__link" href="/r?tid={{ tdoc._id }}">
|
||||
<span class="icon icon-flag"></span> {{ _('All Submissions') }}
|
||||
</a></li>
|
||||
<li class="menu__item"><a class="menu__link" href="/wiki/help#contest">
|
||||
@@ -89,13 +73,13 @@
|
||||
</div>
|
||||
<div class="section__body typo">
|
||||
<dl class="large horizontal">
|
||||
<dt>{{ _('Status') }}</dt><dd>{{ _(handler.status_text(tdoc)) }}</dd>
|
||||
<dt>{{ _('Rule') }}</dt><dd>{{ vj4.constant.contest.RULE_TEXTS[tdoc['rule']] }}</dd>
|
||||
<dt>{{ _('Problem') }}</dt><dd>{{ tdoc['pids']|length }}</dd>
|
||||
<dt>{{ _('Start at') }}</dt><dd>{{ contest.render_time(tdoc['begin_at']) }}</dd>
|
||||
<dt>{{ _('End at') }}</dt><dd>{{ contest.render_time(tdoc['end_at']) }}</dd>
|
||||
<dt>{{ _('Status') }}</dt><dd>{{ _(model.contest.status_text(tdoc)) }}</dd>
|
||||
<dt>{{ _('Rule') }}</dt><dd>{{ model.contest.RULES[tdoc.rule].TEXT }}</dd>
|
||||
<dt>{{ _('Problem') }}</dt><dd>{{ tdoc.pids.length }}</dd>
|
||||
<dt>{{ _('Start at') }}</dt><dd>{{ contest.render_time(tdoc.beginAt) }}</dd>
|
||||
<dt>{{ _('End at') }}</dt><dd>{{ contest.render_time(tdoc.endAt) }}</dd>
|
||||
<dt>{{ _('Duration') }}</dt><dd>{{ contest.render_duration(tdoc) }} {{ _('hour(s)') }}</dd>
|
||||
<dt>{{ _('Host') }}</dt><dd>{{ user.render_inline(owner_udoc, dudoc=owner_dudoc, badge=false) }}</dd>
|
||||
<dt>{{ _('Host') }}</dt><dd>{{ user.render_inline(owner_udoc, badge=false) }}</dd>
|
||||
<dt>{{ _('Partic.') }}</dt>
|
||||
<dd>
|
||||
{{ tdoc['attend']|default(0) }}
|
||||
|
||||
@@ -174,7 +174,7 @@
|
||||
{% if ctdocs %}
|
||||
<p>{{ _('In following contests') }}: </p>
|
||||
{% for tdoc in ctdocs %}
|
||||
<p><a href="/t/{{ tdoc._id }}">{{ tdoc['title'] }}</a></p>
|
||||
<p><a href="/c/{{ tdoc._id }}">{{ tdoc['title'] }}</a></p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if htdocs %}
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
{% if tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST %}
|
||||
<dt>{{ _('Contest') }}</dt>
|
||||
<dd>
|
||||
<a href="/t/{{ tdoc._id }}">{{ tdoc['title'] }}</a>
|
||||
<a href="/c/{{ tdoc._id }}">{{ tdoc['title'] }}</a>
|
||||
</dd>
|
||||
{% else %}{# tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST #}
|
||||
<dt>{{ _('Homework') }}</dt>
|
||||
|
||||
Reference in new issue
Block a user