mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
Contest rule: ACM/ICPC
This commit is contained in:
1 parent
d1ec9064fc
commit
e06283c0cc
43 files changed
+158
-891
No files matched your search
@@ -69,7 +69,7 @@ class ContestDetailHandler extends ContestHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async postAttend() {
|
async postAttend() {
|
||||||
if (contest.is_done(this.tdoc)) throw new ContestNotLiveError(this.tdoc._id);
|
if (contest.isDone(this.tdoc)) throw new ContestNotLiveError(this.tdoc._id);
|
||||||
await contest.attend(this.tdoc._id, this.user._id);
|
await contest.attend(this.tdoc._id, this.user._id);
|
||||||
this.back();
|
this.back();
|
||||||
}
|
}
|
||||||
@@ -88,6 +88,17 @@ class ContestScoreboardHandler extends ContestDetailHandler {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
class ContestScoreboardDownloadHandler extends ContestDetailHandler {
|
||||||
|
async get({ tid, ext }) {
|
||||||
|
const getContent = {
|
||||||
|
csv: async (rows) => `\uFEFF${rows.map((c) => (c.map((i) => i.value).join(','))).join('\n')}`,
|
||||||
|
html: (rows) => this.renderHTML('contest_scoreboard_download_html.html', { rows }),
|
||||||
|
};
|
||||||
|
if (!getContent[ext]) throw new ValidationError('ext');
|
||||||
|
const [, rows] = await this.getScoreboard(tid, true);
|
||||||
|
this.binary(await getContent[ext](rows), `${this.tdoc.title}.${ext}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
class ContestEditHandler extends ContestDetailHandler {
|
class ContestEditHandler extends ContestDetailHandler {
|
||||||
async prepare({ tid }) {
|
async prepare({ tid }) {
|
||||||
const tdoc = await contest.get(tid);
|
const tdoc = await contest.get(tid);
|
||||||
@@ -150,9 +161,9 @@ class ContestProblemHandler extends ContestDetailHandler {
|
|||||||
]);
|
]);
|
||||||
this.attended = this.tsdoc && this.tsdoc.attend === 1;
|
this.attended = this.tsdoc && this.tsdoc.attend === 1;
|
||||||
this.response.template = 'problem_detail.html';
|
this.response.template = 'problem_detail.html';
|
||||||
if (contest.is_done(this.tdoc)) {
|
if (contest.isDone(this.tdoc)) {
|
||||||
if (!this.attended) throw new ContestNotAttendedError(this.tdoc._id);
|
if (!this.attended) throw new ContestNotAttendedError(this.tdoc._id);
|
||||||
if (!contest.is_ongoing(this.tdoc)) throw new ContestNotLiveError(this.tdoc._id);
|
if (!contest.isOngoing(this.tdoc)) throw new ContestNotLiveError(this.tdoc._id);
|
||||||
}
|
}
|
||||||
console.log(this.tdoc.pids, this.pdoc._id);
|
console.log(this.tdoc.pids, this.pdoc._id);
|
||||||
if (!this.tdoc.pids.map((s) => s.toString()).includes(this.pdoc._id.toString())) {
|
if (!this.tdoc.pids.map((s) => s.toString()).includes(this.pdoc._id.toString())) {
|
||||||
@@ -260,6 +271,7 @@ Route('/c', ContestListHandler);
|
|||||||
Route('/c/:tid', ContestDetailHandler);
|
Route('/c/:tid', ContestDetailHandler);
|
||||||
Route('/c/:tid/edit', ContestEditHandler);
|
Route('/c/:tid/edit', ContestEditHandler);
|
||||||
Route('/c/:tid/scoreboard', ContestScoreboardHandler);
|
Route('/c/:tid/scoreboard', ContestScoreboardHandler);
|
||||||
|
Route('/c/:tid/export/:ext', ContestScoreboardDownloadHandler);
|
||||||
Route('/c/:tid/p/:pid', ContestProblemHandler);
|
Route('/c/:tid/p/:pid', ContestProblemHandler);
|
||||||
Route('/c/:tid/p/:pid/submit', ContestProblemSubmitHandler);
|
Route('/c/:tid/p/:pid/submit', ContestProblemSubmitHandler);
|
||||||
Route('/contest/create', ContestCreateHandler);
|
Route('/contest/create', ContestCreateHandler);
|
||||||
+23
-271
@@ -83,9 +83,12 @@ async function updateStatus(tid, uid, rid, pid, accept, score) {
|
|||||||
}, { upsert: true });
|
}, { upsert: true });
|
||||||
if (!tsdoc.value.attend) throw new ContestNotAttendedError(tid, uid);
|
if (!tsdoc.value.attend) throw new ContestNotAttendedError(tid, uid);
|
||||||
}
|
}
|
||||||
|
function getStatus(tid, uid) {
|
||||||
|
return collStatus.findOne({ tid, uid });
|
||||||
|
}
|
||||||
async function getListStatus(uid, tids) {
|
async function getListStatus(uid, tids) {
|
||||||
const r = {};
|
const r = {};
|
||||||
for (const tid of tids) r[tid] = await this.getStatus(tid, uid); // eslint-disable-line no-await-in-loop
|
for (const tid of tids) r[tid] = await getStatus(tid, uid); // eslint-disable-line no-await-in-loop
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
async function attend(tid, uid) {
|
async function attend(tid, uid) {
|
||||||
@@ -99,24 +102,24 @@ async function attend(tid, uid) {
|
|||||||
function getMultiStatus(query) {
|
function getMultiStatus(query) {
|
||||||
return collStatus.find(query);
|
return collStatus.find(query);
|
||||||
}
|
}
|
||||||
function is_new(tdoc, days = 1) {
|
function isNew(tdoc, days = 1) {
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
const readyAt = tdoc.beginAt.getTime();
|
const readyAt = tdoc.beginAt.getTime();
|
||||||
return (now < readyAt - days * 24 * 3600 * 1000);
|
return (now < readyAt - days * 24 * 3600 * 1000);
|
||||||
}
|
}
|
||||||
function is_upcoming(tdoc, days = 1) {
|
function isUpcoming(tdoc, days = 1) {
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
const readyAt = tdoc.beginAt.getTime();
|
const readyAt = tdoc.beginAt.getTime();
|
||||||
return (now > readyAt - days * 24 * 3600 * 1000 && now < tdoc.beginAt);
|
return (now > readyAt - days * 24 * 3600 * 1000 && now < tdoc.beginAt);
|
||||||
}
|
}
|
||||||
function is_not_started(tdoc) {
|
function isNotStarted(tdoc) {
|
||||||
return (new Date()) < tdoc.beginAt;
|
return (new Date()) < tdoc.beginAt;
|
||||||
}
|
}
|
||||||
function is_ongoing(tdoc) {
|
function isOngoing(tdoc) {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
return (tdoc.beginAt <= now && now < tdoc.endAt);
|
return (tdoc.beginAt <= now && now < tdoc.endAt);
|
||||||
}
|
}
|
||||||
function is_done(tdoc) {
|
function isDone(tdoc) {
|
||||||
return tdoc.endAt <= new Date();
|
return tdoc.endAt <= new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,168 +172,38 @@ module.exports = {
|
|||||||
edit,
|
edit,
|
||||||
get,
|
get,
|
||||||
updateStatus,
|
updateStatus,
|
||||||
|
getStatus,
|
||||||
count: (query) => coll.find(query).count(),
|
count: (query) => coll.find(query).count(),
|
||||||
getMulti: (query) => coll.find(query),
|
getMulti: (query) => coll.find(query),
|
||||||
getStatus: (tid, uid) => coll_status.findOne({ tid, uid }),
|
setStatus: (tid, uid, $set) => collStatus.findOneAndUpdate({ tid, uid }, { $set }),
|
||||||
setStatus: (tid, uid, $set) => coll_status.findOneAndUpdate({ tid, uid }, { $set }),
|
isNew,
|
||||||
is_new,
|
isUpcoming,
|
||||||
is_upcoming,
|
isNotStarted,
|
||||||
is_not_started,
|
isOngoing,
|
||||||
is_ongoing,
|
isDone,
|
||||||
is_done,
|
statusText: (tdoc) => (
|
||||||
status_text: (tdoc) => (
|
isNew(tdoc)
|
||||||
is_new(tdoc)
|
|
||||||
? 'New'
|
? 'New'
|
||||||
: is_upcoming(tdoc)
|
: isUpcoming(tdoc)
|
||||||
? 'Ready (☆▽☆)'
|
? 'Ready (☆▽☆)'
|
||||||
: is_ongoing(tdoc)
|
: isOngoing(tdoc)
|
||||||
? 'Live...'
|
? 'Live...'
|
||||||
: 'Done'),
|
: 'Done'),
|
||||||
get_status: (tdoc) => (
|
getStatusText: (tdoc) => (
|
||||||
is_not_started(tdoc)
|
isNotStarted(tdoc)
|
||||||
? 'not_started'
|
? 'not_started'
|
||||||
: is_ongoing(tdoc)
|
: isOngoing(tdoc)
|
||||||
? 'ongoing'
|
? 'ongoing'
|
||||||
: 'finished'),
|
: 'finished'),
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
journal_key_func = lambda j: j['rid']
|
|
||||||
|
|
||||||
Rule = collections.namedtuple('Rule', ['show_record_func',
|
|
||||||
'show_scoreboard_func',
|
|
||||||
'stat_func',
|
|
||||||
'status_sort',
|
|
||||||
'rank_func',
|
|
||||||
'scoreboard_func'])
|
|
||||||
|
|
||||||
def _oi_equ_func(a, b):
|
|
||||||
return a.get('score', 0) == b.get('score', 0)
|
|
||||||
|
|
||||||
RULES = {
|
|
||||||
constant.contest.RULE_OI: Rule(lambda tdoc, now: now > tdoc['end_at'],
|
|
||||||
lambda tdoc, now: now > tdoc['end_at'],
|
|
||||||
_oi_stat,
|
|
||||||
[('score', -1)],
|
|
||||||
functools.partial(rank.ranked, equ_func=_oi_equ_func),
|
|
||||||
_oi_scoreboard),
|
|
||||||
constant.contest.RULE_ACM: Rule(lambda tdoc, now: now >= tdoc['begin_at'],
|
|
||||||
lambda tdoc, now: now >= tdoc['begin_at'],
|
|
||||||
_acm_stat,
|
|
||||||
[('accept', -1), ('time', 1)],
|
|
||||||
functools.partial(enumerate, start=1),
|
|
||||||
_acm_scoreboard),
|
|
||||||
constant.contest.RULE_ASSIGNMENT: Rule(lambda tdoc, now: now >= tdoc['begin_at'],
|
|
||||||
lambda tdoc, now: False, # TODO: show scoreboard according to assignment preference
|
|
||||||
_assignment_stat,
|
|
||||||
[('penalty_score', -1), ('time', 1)],
|
|
||||||
functools.partial(enumerate, start=1),
|
|
||||||
_assignment_scoreboard),
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_multi(domainId: str, doc_type: int, fields=None, **kwargs):
|
|
||||||
# TODO(twd2): projection.
|
|
||||||
if doc_type not in [document.TYPE_CONTEST, document.TYPE_HOMEWORK]:
|
|
||||||
raise error.InvalidArgumentError('doc_type')
|
|
||||||
return document.get_multi(domainId=domainId,
|
|
||||||
doc_type=doc_type,
|
|
||||||
fields=fields,
|
|
||||||
**kwargs) \
|
|
||||||
.sort([('doc_id', -1)])
|
|
||||||
|
|
||||||
|
|
||||||
@argmethod.wrap
|
|
||||||
async def attend(domainId: str, doc_type: int, cid: objeccid.Objeccid, uid: int):
|
|
||||||
# TODO(iceboy): check time.
|
|
||||||
if doc_type not in [document.TYPE_CONTEST, document.TYPE_HOMEWORK]:
|
|
||||||
raise error.InvalidArgumentError('doc_type')
|
|
||||||
try:
|
|
||||||
await document.capped_inc_status(domainId, doc_type, cid,
|
|
||||||
uid, 'attend', 1, 0, 1)
|
|
||||||
except errors.DuplicateKeyError:
|
|
||||||
if doc_type == document.TYPE_CONTEST:
|
|
||||||
raise error.ContestAlreadyAttendedError(domainId, cid, uid) from None
|
|
||||||
elif doc_type == document.TYPE_HOMEWORK:
|
|
||||||
raise error.HomeworkAlreadyAttendedError(domainId, cid, uid) from None
|
|
||||||
return await document.inc(domainId, doc_type, cid, 'attend', 1)
|
|
||||||
|
|
||||||
|
|
||||||
@argmethod.wrap
|
|
||||||
async def get_status(domainId: str, doc_type: int, cid: objeccid.Objeccid, uid: int, fields=None):
|
|
||||||
if doc_type not in [document.TYPE_CONTEST, document.TYPE_HOMEWORK]:
|
|
||||||
raise error.InvalidArgumentError('doc_type')
|
|
||||||
return await document.get_status(domainId, doc_type, doc_id=cid,
|
|
||||||
uid=uid, fields=fields)
|
|
||||||
|
|
||||||
|
|
||||||
def get_multi_status(doc_type: int, *, fields=None, **kwargs):
|
|
||||||
if doc_type not in [document.TYPE_CONTEST, document.TYPE_HOMEWORK]:
|
|
||||||
raise error.InvalidArgumentError('doc_type')
|
|
||||||
return document.get_multi_status(doc_type=doc_type,
|
|
||||||
fields=fields, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
async def get_dict_status(domainId, uid, doc_type, cids, *, fields=None):
|
|
||||||
if doc_type not in [document.TYPE_CONTEST, document.TYPE_HOMEWORK]:
|
|
||||||
raise error.InvalidArgumentError('doc_type')
|
|
||||||
result = dict()
|
|
||||||
async for tsdoc in get_multi_status(domainId=domainId,
|
|
||||||
uid=uid,
|
|
||||||
doc_type=doc_type,
|
|
||||||
doc_id={'$in': list(set(cids))},
|
|
||||||
fields=fields):
|
|
||||||
result[tsdoc['doc_id']] = tsdoc
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
@argmethod.wrap
|
|
||||||
async def get_and_list_status(domainId: str, doc_type: int, cid: objeccid.Objeccid, fields=None):
|
|
||||||
# TODO(iceboy): projection, pagination.
|
|
||||||
if doc_type not in [document.TYPE_CONTEST, document.TYPE_HOMEWORK]:
|
|
||||||
raise error.InvalidArgumentError('doc_type')
|
|
||||||
tdoc = await get(domainId, doc_type, cid)
|
|
||||||
tsdocs = await document.get_multi_status(domainId=domainId,
|
|
||||||
doc_type=doc_type,
|
|
||||||
doc_id=tdoc['doc_id'],
|
|
||||||
fields=fields) \
|
|
||||||
.sort(RULES[tdoc['rule']].status_sort) \
|
|
||||||
.to_list()
|
|
||||||
return tdoc, tsdocs
|
|
||||||
|
|
||||||
|
|
||||||
def _get_status_journal(tsdoc):
|
def _get_status_journal(tsdoc):
|
||||||
# Sort and uniquify journal of the contest status document, by rid.
|
# Sort and uniquify journal of the contest status document, by rid.
|
||||||
return [list(g)[-1] for _, g in itertools.groupby(sorted(tsdoc['journal'], key=journal_key_func),
|
return [list(g)[-1] for _, g in itertools.groupby(sorted(tsdoc['journal'], key=journal_key_func),
|
||||||
key=journal_key_func)]
|
key=journal_key_func)]
|
||||||
|
|
||||||
|
|
||||||
@argmethod.wrap
|
|
||||||
async def update_status(domainId: str, doc_type: int, cid: objeccid.Objeccid, uid: int,
|
|
||||||
rid: objeccid.Objeccid, pid: document.convert_doc_id,
|
|
||||||
accept: bool, score: int):
|
|
||||||
"""This method returns None when the modification has been superseded by a parallel operation."""
|
|
||||||
if doc_type not in [document.TYPE_CONTEST, document.TYPE_HOMEWORK]:
|
|
||||||
raise error.InvalidArgumentError('doc_type')
|
|
||||||
tdoc = await document.get(domainId, doc_type, cid)
|
|
||||||
tsdoc = await document.rev_push_status(
|
|
||||||
domainId, tdoc['doc_type'], tdoc['doc_id'], uid,
|
|
||||||
'journal', {'rid': rid, 'pid': pid, 'accept': accept, 'score': score})
|
|
||||||
if 'attend' not in tsdoc or not tsdoc['attend']:
|
|
||||||
if tdoc['doc_type'] == document.TYPE_CONTEST:
|
|
||||||
raise error.ContestNotAttendedError(domainId, cid, uid)
|
|
||||||
elif tdoc['doc_type'] == document.TYPE_HOMEWORK:
|
|
||||||
raise error.HomeworkNotAttendedError(domainId, cid, uid)
|
|
||||||
else:
|
|
||||||
raise error.InvalidArgumentError('doc_type')
|
|
||||||
|
|
||||||
journal = _get_status_journal(tsdoc)
|
|
||||||
stats = RULES[tdoc['rule']].stat_func(tdoc, journal)
|
|
||||||
tsdoc = await document.rev_set_status(domainId, tdoc['doc_type'], cid, uid, tsdoc['rev'],
|
|
||||||
journal=journal, **stats)
|
|
||||||
return tsdoc
|
|
||||||
|
|
||||||
|
|
||||||
@argmethod.wrap
|
@argmethod.wrap
|
||||||
async def recalc_status(domainId: str, doc_type: int, cid: objeccid.Objeccid):
|
async def recalc_status(domainId: str, doc_type: int, cid: objeccid.Objeccid):
|
||||||
if doc_type not in [document.TYPE_CONTEST, document.TYPE_HOMEWORK]:
|
if doc_type not in [document.TYPE_CONTEST, document.TYPE_HOMEWORK]:
|
||||||
@@ -346,125 +219,4 @@ async def recalc_status(domainId: str, doc_type: int, cid: objeccid.Objeccid):
|
|||||||
stats = RULES[tdoc['rule']].stat_func(tdoc, journal)
|
stats = RULES[tdoc['rule']].stat_func(tdoc, journal)
|
||||||
await document.rev_set_status(domainId, doc_type, cid, tsdoc['uid'], tsdoc['rev'],
|
await document.rev_set_status(domainId, doc_type, cid, tsdoc['uid'], tsdoc['rev'],
|
||||||
return_doc=False, journal=journal, **stats)
|
return_doc=False, journal=journal, **stats)
|
||||||
|
|
||||||
|
|
||||||
def _parse_pids(pids_str):
|
|
||||||
pids = misc.dedupe(map(document.convert_doc_id, pids_str.split(',')))
|
|
||||||
return pids
|
|
||||||
|
|
||||||
|
|
||||||
def _format_pids(pids_list):
|
|
||||||
return ','.join([str(pid) for pid in pids_list])
|
|
||||||
|
|
||||||
|
|
||||||
class ContestStatusMixin(object):
|
|
||||||
@property
|
|
||||||
@functools.lru_cache()
|
|
||||||
def now(self):
|
|
||||||
# TODO(iceboy): This does not work on multi-machine environment.
|
|
||||||
return datetime.datetime.utcnow()
|
|
||||||
|
|
||||||
def is_new(self, tdoc):
|
|
||||||
ready_at = tdoc['begin_at'] - datetime.timedelta(days=1)
|
|
||||||
return self.now < ready_at
|
|
||||||
|
|
||||||
def is_upcoming(self, tdoc):
|
|
||||||
ready_at = tdoc['begin_at'] - datetime.timedelta(days=1)
|
|
||||||
return ready_at <= self.now < tdoc['begin_at']
|
|
||||||
|
|
||||||
def is_not_started(self, tdoc):
|
|
||||||
return self.now < tdoc['begin_at']
|
|
||||||
|
|
||||||
def is_ongoing(self, tdoc):
|
|
||||||
return tdoc['begin_at'] <= self.now < tdoc['end_at']
|
|
||||||
|
|
||||||
def is_done(self, tdoc):
|
|
||||||
return tdoc['end_at'] <= self.now
|
|
||||||
|
|
||||||
def is_homework_extended(self, tdoc):
|
|
||||||
return tdoc['penalty_since'] <= self.now < tdoc['end_at']
|
|
||||||
|
|
||||||
def status_text(self, tdoc):
|
|
||||||
if self.is_new(tdoc):
|
|
||||||
return 'New'
|
|
||||||
elif self.is_upcoming(tdoc):
|
|
||||||
return 'Ready (☆▽☆)'
|
|
||||||
elif self.is_ongoing(tdoc):
|
|
||||||
return 'Live...'
|
|
||||||
else:
|
|
||||||
return 'Done'
|
|
||||||
|
|
||||||
def get_status(self, tdoc):
|
|
||||||
if self.is_not_started(tdoc):
|
|
||||||
return 'not_started'
|
|
||||||
elif self.is_ongoing(tdoc):
|
|
||||||
return 'ongoing'
|
|
||||||
else:
|
|
||||||
return 'finished'
|
|
||||||
|
|
||||||
|
|
||||||
class ContestVisibilityMixin(object):
|
|
||||||
def can_view_hidden_scoreboard(self, tdoc):
|
|
||||||
if self.domainId != tdoc['domainId']:
|
|
||||||
return False
|
|
||||||
if tdoc['doc_type'] == document.TYPE_CONTEST:
|
|
||||||
return self.hasPerm(builtin.PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD)
|
|
||||||
elif tdoc['doc_type'] == document.TYPE_HOMEWORK:
|
|
||||||
return self.hasPerm(builtin.PERM_VIEW_HOMEWORK_HIDDEN_SCOREBOARD)
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def can_show_record(self, tdoc, allow_perm_override=True):
|
|
||||||
if RULES[tdoc['rule']].show_record_func(tdoc, datetime.datetime.utcnow()):
|
|
||||||
return True
|
|
||||||
if allow_perm_override and self.can_view_hidden_scoreboard(tdoc):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def can_show_scoreboard(self, tdoc, allow_perm_override=True):
|
|
||||||
if RULES[tdoc['rule']].show_scoreboard_func(tdoc, datetime.datetime.utcnow()):
|
|
||||||
return True
|
|
||||||
if allow_perm_override and self.can_view_hidden_scoreboard(tdoc):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
class ContestCommonOperationMixin(object):
|
|
||||||
async def get_scoreboard(self, doc_type: int, cid: objeccid.Objeccid, is_export: bool=False):
|
|
||||||
if doc_type not in [document.TYPE_CONTEST, document.TYPE_HOMEWORK]:
|
|
||||||
raise error.InvalidArgumentError('doc_type')
|
|
||||||
tdoc, tsdocs = await get_and_list_status(self.domainId, doc_type, cid)
|
|
||||||
if not self.can_show_scoreboard(tdoc):
|
|
||||||
if doc_type == document.TYPE_CONTEST:
|
|
||||||
raise error.ContestScoreboardHiddenError(self.domainId, cid)
|
|
||||||
elif doc_type == document.TYPE_HOMEWORK:
|
|
||||||
raise error.HomeworkScoreboardHiddenError(self.domainId, cid)
|
|
||||||
udict, dudict, pdict = await asyncio.gather(
|
|
||||||
user.get_dict([tsdoc['uid'] for tsdoc in tsdocs]),
|
|
||||||
domain.get_dict_user_by_uid(self.domainId, [tsdoc['uid'] for tsdoc in tsdocs]),
|
|
||||||
problem.get_dict(self.domainId, tdoc['pids']))
|
|
||||||
ranked_tsdocs = RULES[tdoc['rule']].rank_func(tsdocs)
|
|
||||||
rows = RULES[tdoc['rule']].scoreboard_func(is_export, self.translate, tdoc,
|
|
||||||
ranked_tsdocs, udict, dudict, pdict)
|
|
||||||
return tdoc, rows, udict
|
|
||||||
|
|
||||||
async def verify_problems(self, pids):
|
|
||||||
pdocs = await problem.get_multi(domainId=self.domainId, doc_id={'$in': pids},
|
|
||||||
fields={'doc_id': 1}) \
|
|
||||||
.sort('doc_id', 1) \
|
|
||||||
.to_list()
|
|
||||||
exist_pids = [pdoc['_id'] for pdoc in pdocs]
|
|
||||||
if len(pids) != len(exist_pids):
|
|
||||||
for pid in pids:
|
|
||||||
if pid not in exist_pids:
|
|
||||||
raise error.ProblemNotFoundError(self.domainId, pid)
|
|
||||||
return pids
|
|
||||||
|
|
||||||
async def hide_problems(self, pids):
|
|
||||||
await asyncio.gather(*[problem.set_hidden(self.domainId, pid, True) for pid in pids])
|
|
||||||
|
|
||||||
|
|
||||||
class ContestMixin(ContestStatusMixin, ContestVisibilityMixin, ContestCommonOperationMixin):
|
|
||||||
pass
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
@@ -7,8 +7,8 @@ async function get(_id) {
|
|||||||
if (doc) return doc.value;
|
if (doc) return doc.value;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
async function update(_id, update, config) {
|
async function update(_id, operation, config) {
|
||||||
await coll.findOneAndUpdate({ _id }, update, config);
|
await coll.findOneAndUpdate({ _id }, operation, config);
|
||||||
return get(_id);
|
return get(_id);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
|||||||
+31
-24
@@ -4,36 +4,42 @@ module.exports = {
|
|||||||
showScoreboard: () => true,
|
showScoreboard: () => true,
|
||||||
showRecord: (tdoc, now) => now > tdoc.endAt,
|
showRecord: (tdoc, now) => now > tdoc.endAt,
|
||||||
stat: (tdoc, journal) => {
|
stat: (tdoc, journal) => {
|
||||||
const naccept = {}; const effective = {}; const detail = []; let accept = 0; let time = 0;
|
const naccept = {};
|
||||||
|
const effective = {};
|
||||||
|
const detail = [];
|
||||||
|
let accept = 0;
|
||||||
|
let time = 0;
|
||||||
for (const j in journal) {
|
for (const j in journal) {
|
||||||
if (tdoc.pids.includes(j.pid) && !(effective.includes(j.pid) && effective[j.pid].accept)) { effective[j.pid] = j; }
|
if (tdoc.pids.includes(j.pid)
|
||||||
if (!j.accept) { naccept[j.pid]++; }
|
&& !(effective.includes(j.pid) && effective[j.pid].accept)) {
|
||||||
|
effective[j.pid] = j;
|
||||||
|
}
|
||||||
|
if (!j.accept) naccept[j.pid]++;
|
||||||
}
|
}
|
||||||
function _time(jdoc) {
|
function _time(jdoc) {
|
||||||
const real = jdoc.rid.generationTime - Math.floor(tdoc.begin_at / 1000);
|
const real = jdoc.rid.generationTime - Math.floor(tdoc.begin_at / 1000);
|
||||||
const penalty = 20 * 60 * naccept[jdoc.pid];
|
const penalty = 20 * 60 * naccept[jdoc.pid];
|
||||||
return real + penalty;
|
return real + penalty;
|
||||||
}
|
}
|
||||||
for (const j of effective) { detail.push({ ...j, naccept: naccept[j.pid], time: _time(j) }); }
|
for (const j of effective) detail.push({ ...j, naccept: naccept[j.pid], time: _time(j) });
|
||||||
for (const d of detail) {
|
for (const d of detail) {
|
||||||
accept += d.accept;
|
accept += d.accept;
|
||||||
if (d.accept) time += d.time;
|
if (d.accept) time += d.time;
|
||||||
}
|
}
|
||||||
return { accept, time, detail };
|
return { accept, time, detail };
|
||||||
},
|
},
|
||||||
scoreboard(is_export, _, tdoc, ranked_tsdocs, udict, dudict, pdict) {
|
scoreboard(isExport, _, tdoc, rankedTsdocs, udict, pdict) {
|
||||||
const columns = [
|
const columns = [
|
||||||
{ type: 'rank', value: _('Rank') },
|
{ type: 'rank', value: _('Rank') },
|
||||||
{ type: 'user', value: _('User') },
|
{ type: 'user', value: _('User') },
|
||||||
{ type: 'display_name', value: _('Display Name') },
|
|
||||||
{ type: 'solved_problems', value: _('Solved Problems') },
|
{ type: 'solved_problems', value: _('Solved Problems') },
|
||||||
];
|
];
|
||||||
if (is_export) {
|
if (isExport) {
|
||||||
columns.push({ type: 'total_time', value: _('Total Time (Seconds)') });
|
columns.push({ type: 'total_time', value: _('Total Time (Seconds)') });
|
||||||
columns.push({ type: 'total_time_str', value: _('Total Time') });
|
columns.push({ type: 'total_time_str', value: _('Total Time') });
|
||||||
}
|
}
|
||||||
for (const i in tdoc.pids) {
|
for (const i in tdoc.pids) {
|
||||||
if (is_export) {
|
if (isExport) {
|
||||||
columns.push({
|
columns.push({
|
||||||
type: 'problem_flag',
|
type: 'problem_flag',
|
||||||
value: '#{0} {1}'.format(i + 1, pdict[tdoc.pids[i]].title),
|
value: '#{0} {1}'.format(i + 1, pdict[tdoc.pids[i]].title),
|
||||||
@@ -55,44 +61,45 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const rows = [columns];
|
const rows = [columns];
|
||||||
for (const [rank, tsdoc] of ranked_tsdocs) {
|
for (const [rank, tsdoc] of rankedTsdocs) {
|
||||||
const tsddict = {};
|
const tsddict = {};
|
||||||
if (tdoc.detail) { for (const item of tsdoc.detail) tsddict[item.pid] = item; }
|
if (tdoc.detail) { for (const item of tsdoc.detail) tsddict[item.pid] = item; }
|
||||||
const row = [];
|
const row = [];
|
||||||
row.push(
|
row.push(
|
||||||
{ type: 'string', value: rank },
|
{ type: 'string', value: rank },
|
||||||
{ type: 'user', value: udict[tsdoc.uid].uname, raw: udict[tsdoc.uid] },
|
{ type: 'user', value: udict[tsdoc.uid].uname, raw: udict[tsdoc.uid] },
|
||||||
{ type: 'display_name', value: (dudict[tsdoc.uid] || {}).display_name || '' },
|
|
||||||
{ type: 'string', value: tsdoc.accept || 0 },
|
{ type: 'string', value: tsdoc.accept || 0 },
|
||||||
);
|
);
|
||||||
if (is_export) {
|
if (isExport) {
|
||||||
row.push(
|
row.push(
|
||||||
{ type: 'string', value: tsdoc.time || 0.0 },
|
{ type: 'string', value: tsdoc.time || 0.0 },
|
||||||
{ type: 'string', value: tsdoc.time || 0.0 },
|
{ type: 'string', value: tsdoc.time || 0.0 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (const pid of tdoc.pids) {
|
for (const pid of tdoc.pids) {
|
||||||
let rdoc; let col_accepted; let col_time; let
|
let rdoc;
|
||||||
col_time_str;
|
let colAccepted;
|
||||||
|
let colTime;
|
||||||
|
let colTimeStr;
|
||||||
if ((tsddict[pid] || {}).accept) {
|
if ((tsddict[pid] || {}).accept) {
|
||||||
rdoc = tsddict[pid].rid;
|
rdoc = tsddict[pid].rid;
|
||||||
col_accepted = _('Accepted');
|
colAccepted = _('Accepted');
|
||||||
col_time = tsddict[pid].time;
|
colTime = tsddict[pid].time;
|
||||||
col_time_str = col_time;
|
colTimeStr = colTime;
|
||||||
} else {
|
} else {
|
||||||
rdoc = null;
|
rdoc = null;
|
||||||
col_accepted = '-';
|
colAccepted = '-';
|
||||||
col_time = '-';
|
colTime = '-';
|
||||||
col_time_str = '-';
|
colTimeStr = '-';
|
||||||
}
|
}
|
||||||
if (is_export) {
|
if (isExport) {
|
||||||
row.push({ type: 'string', value: col_accepted });
|
row.push({ type: 'string', value: colAccepted });
|
||||||
row.push({ type: 'string', value: col_time });
|
row.push({ type: 'string', value: colTime });
|
||||||
row.push({ type: 'string', value: col_time_str });
|
row.push({ type: 'string', value: colTimeStr });
|
||||||
} else {
|
} else {
|
||||||
row.push({
|
row.push({
|
||||||
type: 'record',
|
type: 'record',
|
||||||
value: '{0}\n{1}'.format(col_accepted, col_time_str),
|
value: '{0}\n{1}'.format(colAccepted, colTimeStr),
|
||||||
raw: rdoc,
|
raw: rdoc,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +1,40 @@
|
|||||||
const { ValidationError } = require('../../error');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
TEXT: 'Assignment',
|
TEXT: 'Assignment',
|
||||||
check(data) {
|
check() {},
|
||||||
if (!data.penalty_since) throw new ValidationError('penalty_since');
|
|
||||||
if (data.penalty_since < data.begin_at) throw new ValidationError('penalty_since', 'begin_at');
|
|
||||||
if (data.penalty_since > data.end_at) throw new ValidationError('penalty_since', 'end_at');
|
|
||||||
},
|
|
||||||
stat(tdoc, journal) {
|
stat(tdoc, journal) {
|
||||||
const effective = {};
|
const effective = {};
|
||||||
const detail = [];
|
const detail = [];
|
||||||
let score = 0;
|
let score = 0;
|
||||||
let penalty_score = 0;
|
|
||||||
let time = 0;
|
let time = 0;
|
||||||
for (const j in journal) { if (tdoc.pids.includes(j.pid) && !(effective.includes(j.pid) && effective[j.pid].accept)) effective[j.pid] = j; }
|
for (const j in journal) {
|
||||||
const _time = (jdoc) => jdoc.rid.generationTime - Math.floor(tdoc.begin_at / 1000);
|
if (tdoc.pids.includes(j.pid)
|
||||||
function _penalty_score(jdoc) {
|
&& !(effective.includes(j.pid) && effective[j.pid].accept)) {
|
||||||
const exceed_seconds = jdoc.rid.generationTime - Math.floor(tdoc.penalty_since / 1000);
|
effective[j.pid] = j;
|
||||||
if (exceed_seconds < 0) return jdoc.score;
|
}
|
||||||
return 0.5 * jdoc.score;
|
|
||||||
}
|
}
|
||||||
|
const _time = (jdoc) => jdoc.rid.generationTime - Math.floor(tdoc.beginAt / 1000);
|
||||||
for (const j in effective) {
|
for (const j in effective) {
|
||||||
detail.push({ ...effective[j], penalty_score: _penalty_score(effective[j]), time: _time(effective[j]) });
|
detail.push({
|
||||||
|
...effective[j],
|
||||||
|
time: _time(effective[j]),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
for (const d of detail) {
|
for (const d of detail) {
|
||||||
score += d.score;
|
score += d.score;
|
||||||
penalty_score += d.penalty_score;
|
|
||||||
time += d.time;
|
time += d.time;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
score, penalty_score, time, detail,
|
score, time, detail,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
scoreboard(is_export, _, tdoc, ranked_tsdocs, udict, dudict, pdict) {
|
scoreboard(isExport, _, tdoc, rankedTsdocs, udict, pdict) {
|
||||||
const columns = [
|
const columns = [
|
||||||
{ type: 'rank', value: _('Rank') },
|
{ type: 'rank', value: _('Rank') },
|
||||||
{ type: 'user', value: _('User') },
|
{ type: 'user', value: _('User') },
|
||||||
{ type: 'display_name', value: _('Display Name') },
|
{ type: 'display_name', value: _('Display Name') },
|
||||||
{ type: 'total_score', value: _('Score') },
|
{ type: 'total_score', value: _('Score') },
|
||||||
];
|
];
|
||||||
if (is_export) {
|
if (isExport) {
|
||||||
columns.push(
|
columns.push(
|
||||||
{ type: 'total_original_score', value: _('Original Score') },
|
{ type: 'total_original_score', value: _('Original Score') },
|
||||||
{ type: 'total_time', value: _('Total Time (Seconds)') },
|
{ type: 'total_time', value: _('Total Time (Seconds)') },
|
||||||
@@ -47,7 +42,7 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
columns.push({ type: 'total_time_str', value: _('Total Time') });
|
columns.push({ type: 'total_time_str', value: _('Total Time') });
|
||||||
for (const i in tdoc.pids) {
|
for (const i in tdoc.pids) {
|
||||||
if (is_export) {
|
if (isExport) {
|
||||||
columns.push(
|
columns.push(
|
||||||
{ type: 'problem_score', value: '#{0} {1}'.format(i + 1, pdict[tdoc.pids[i]].title) },
|
{ type: 'problem_score', value: '#{0} {1}'.format(i + 1, pdict[tdoc.pids[i]].title) },
|
||||||
{ type: 'problem_original_score', value: '#{0} {1}'.format(i + 1, _('Original Score')) },
|
{ type: 'problem_original_score', value: '#{0} {1}'.format(i + 1, _('Original Score')) },
|
||||||
@@ -57,16 +52,15 @@ module.exports = {
|
|||||||
} else columns.push({ type: 'problem_detail', value: '#{0}'.format(i + 1), raw: pdict[tdoc.pids[i]] });
|
} else columns.push({ type: 'problem_detail', value: '#{0}'.format(i + 1), raw: pdict[tdoc.pids[i]] });
|
||||||
}
|
}
|
||||||
const rows = [columns];
|
const rows = [columns];
|
||||||
for (const [rank, tsdoc] in ranked_tsdocs) {
|
for (const [rank, tsdoc] in rankedTsdocs) {
|
||||||
const tsddict = {};
|
const tsddict = {};
|
||||||
if (tsdoc.detail) { for (const item of tsdoc.detail) tsddict[item.pid] = item; }
|
if (tsdoc.detail) { for (const item of tsdoc.detail) tsddict[item.pid] = item; }
|
||||||
const row = [
|
const row = [
|
||||||
{ type: 'string', value: rank },
|
{ type: 'string', value: rank },
|
||||||
{ type: 'user', value: udict[tsdoc.uid].uname, raw: udict[tsdoc.uid] },
|
{ type: 'user', value: udict[tsdoc.uid].uname, raw: udict[tsdoc.uid] },
|
||||||
{ type: 'display_name', value: (dudict[tsdoc.uid] || {}).display_name || '' },
|
|
||||||
{ type: 'string', value: tsdoc.penalty_score || 0 },
|
{ type: 'string', value: tsdoc.penalty_score || 0 },
|
||||||
];
|
];
|
||||||
if (is_export) {
|
if (isExport) {
|
||||||
row.push(
|
row.push(
|
||||||
{ type: 'string', value: tsdoc.score || 0 },
|
{ type: 'string', value: tsdoc.score || 0 },
|
||||||
{ type: 'string', value: tsdoc.time || 0.0 },
|
{ type: 'string', value: tsdoc.time || 0.0 },
|
||||||
@@ -75,21 +69,21 @@ module.exports = {
|
|||||||
row.push({ type: 'string', value: tsdoc.time || 0 });
|
row.push({ type: 'string', value: tsdoc.time || 0 });
|
||||||
for (const pid of tdoc.pids) {
|
for (const pid of tdoc.pids) {
|
||||||
const rdoc = (tsddict[pid] || {}).rid || null;
|
const rdoc = (tsddict[pid] || {}).rid || null;
|
||||||
const col_score = (tsddict[pid] || {}).penalty_score || '-';
|
const colScore = (tsddict[pid] || {}).penalty_score || '-';
|
||||||
const col_original_score = (tsddict[pid] || {}).score || '-';
|
const colOriginalScore = (tsddict[pid] || {}).score || '-';
|
||||||
const col_time = (tsddict[pid] || {}).time || '-';
|
const colTime = (tsddict[pid] || {}).time || '-';
|
||||||
const col_time_str = col_time != '-' ? col_time : '-';
|
const colTimeStr = colTime !== '-' ? colTime : '-';
|
||||||
if (is_export) {
|
if (isExport) {
|
||||||
row.push(
|
row.push(
|
||||||
{ type: 'string', value: col_score },
|
{ type: 'string', value: colScore },
|
||||||
{ type: 'string', value: col_original_score },
|
{ type: 'string', value: colOriginalScore },
|
||||||
{ type: 'string', value: col_time },
|
{ type: 'string', value: colTime },
|
||||||
{ type: 'string', value: col_time_str },
|
{ type: 'string', value: colTimeStr },
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
row.push({
|
row.push({
|
||||||
type: 'record',
|
type: 'record',
|
||||||
value: '{0} / {1}\n{2}'.format(col_score, col_original_score, col_time_str),
|
value: '{0} / {1}\n{2}'.format(colScore, colOriginalScore, colTimeStr),
|
||||||
raw: rdoc,
|
raw: rdoc,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,6 +115,13 @@ class Handler {
|
|||||||
this.response.redirect = this.request.headers.referer || '/';
|
this.response.redirect = this.request.headers.referer || '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binary(data, name) {
|
||||||
|
this.response.body = data;
|
||||||
|
this.response.template = null;
|
||||||
|
this.response.type = 'application/octet-stream';
|
||||||
|
this.response.disposition = `attachment; filename="${name}"`;
|
||||||
|
}
|
||||||
|
|
||||||
async ___prepare() {
|
async ___prepare() {
|
||||||
this.now = new Date();
|
this.now = new Date();
|
||||||
this._handler.sid = this.request.cookies.get('sid');
|
this._handler.sid = this.request.cookies.get('sid');
|
||||||
@@ -170,6 +177,7 @@ class Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async putResponse() {
|
async putResponse() {
|
||||||
|
if (this.response.disposition) this.ctx.set('Content-Disposition', this.response.disposition);
|
||||||
if (this.response.redirect && !this.preferJson) {
|
if (this.response.redirect && !this.preferJson) {
|
||||||
this.ctx.response.type = 'application/octet-stream';
|
this.ctx.response.type = 'application/octet-stream';
|
||||||
this.ctx.response.status = 302;
|
this.ctx.response.status = 302;
|
||||||
|
|||||||
Vendored
-3
@@ -1,3 +0,0 @@
|
|||||||
export interface {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{% import "components/user.html" as user with context %}
|
|
||||||
{% macro render_time(tdoc_time) %}
|
|
||||||
{{ datetime_span(tdoc_time, false, '%Y-%m-%d %H:%M') }}
|
|
||||||
{% endmacro %}
|
|
||||||
|
|
||||||
{% macro render_extension(tdoc) %}
|
|
||||||
{{ ((tdoc['end_at'] - tdoc['penalty_since']).total_seconds() / 3600)|round(1) }}
|
|
||||||
{% endmacro %}
|
|
||||||
@@ -9,19 +9,19 @@
|
|||||||
<div class="section">
|
<div class="section">
|
||||||
<div class="section__body typo">
|
<div class="section__body typo">
|
||||||
{{ tdoc['content']|markdown|safe }}
|
{{ tdoc['content']|markdown|safe }}
|
||||||
{% if model.contest.is_ongoing(tdoc) %}
|
{% if model.contest.isOngoing(tdoc) %}
|
||||||
<blockquote class="warn">
|
<blockquote class="warn">
|
||||||
<p>{{ _('We will check code similarity after this contest.') }}</p>
|
<p>{{ _('We will check code similarity after this contest.') }}</p>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if model.contest.is_ongoing(tdoc) or model.contest.is_done(tdoc) %}
|
{% if model.contest.isOngoing(tdoc) or model.contest.isDone(tdoc) %}
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<div class="section__header">
|
<div class="section__header">
|
||||||
<h1 class="section__title">{{ _('Problem') }}</h1>
|
<h1 class="section__title">{{ _('Problem') }}</h1>
|
||||||
</div>
|
</div>
|
||||||
{% if attended or model.contest.is_done(tdoc) %}
|
{% if attended or model.contest.isDone(tdoc) %}
|
||||||
<div class="section__body no-padding">
|
<div class="section__body no-padding">
|
||||||
<table class="data-table">
|
<table class="data-table">
|
||||||
<colgroup>
|
<colgroup>
|
||||||
|
|||||||
@@ -49,9 +49,9 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="medium-9 columns">
|
<div class="medium-9 columns">
|
||||||
{% for tdoc in tdocs %}
|
{% for tdoc in tdocs %}
|
||||||
{% if model.contest.is_ongoing(tdoc) %}
|
{% if model.contest.isOngoing(tdoc) %}
|
||||||
{{ render_important_contest('live', 'Live...', tdoc) }}
|
{{ render_important_contest('live', 'Live...', tdoc) }}
|
||||||
{% elif model.contest.is_upcoming(tdoc) %}
|
{% elif model.contest.isUpcoming(tdoc) %}
|
||||||
{{ render_important_contest('ready', 'Ready (☆▽☆)', tdoc) }}
|
{{ render_important_contest('ready', 'Ready (☆▽☆)', tdoc) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
<div class="row"><div class="medium-12 columns">
|
<div class="row"><div class="medium-12 columns">
|
||||||
<div class="section visible">
|
<div class="section visible">
|
||||||
<div class="section__header">
|
<div class="section__header">
|
||||||
<a class="button" target="_blank" href="/c/{{ tdoc._id }}/export?format=html">
|
<a class="button" target="_blank" href="/c/{{ tdoc._id }}/export/html">
|
||||||
<span class="icon icon-download"></span> {{ _('Export as HTML') }}
|
<span class="icon icon-download"></span> {{ _('Export as HTML') }}
|
||||||
</a>
|
</a>
|
||||||
<a class="button" href="/c/{{ tdoc._id }}/export?format=csv">
|
<a class="button" href="/c/{{ tdoc._id }}/export/csv">
|
||||||
<span class="icon icon-download"></span> {{ _('Export as CSV') }}
|
<span class="icon icon-download"></span> {{ _('Export as CSV') }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{%- for row in rows[1:] -%}
|
{%- for row in rows -%}{% if loop.index0 %}
|
||||||
<tr>
|
<tr>
|
||||||
{%- for column in row -%}
|
{%- for column in row -%}
|
||||||
<td>
|
<td>
|
||||||
@@ -43,6 +43,6 @@
|
|||||||
</td>
|
</td>
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
</tr>
|
</tr>
|
||||||
{%- endfor -%}
|
{% endif %}{%- endfor -%}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
{{ user.render_inline(udict[ddoc['owner_uid']], dudict[ddoc['owner_uid']]) }}
|
{{ user.render_inline(udict[ddoc['owner']]) }}
|
||||||
@
|
@
|
||||||
{{ datetime_span(ddoc['_id'].generationTime) }}
|
{{ datetime_span(ddoc['_id'].generationTime) }}
|
||||||
</li>
|
</li>
|
||||||
@@ -82,9 +82,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="medium-3 columns">
|
<div class="medium-3 columns">
|
||||||
{% with udoc = udict[ddoc['owner_uid']], dudoc = dudict[ddoc['owner_uid']] %}
|
{% with udoc = udict[ddoc['owner']] %}
|
||||||
<div class="section side">
|
<div class="section side">
|
||||||
<div class="profile__bg user-profile-bg--{{ vj4.model.adaptor.setting.UserSetting(udoc, dudoc).get_setting('background_img') }}"></div>
|
<div class="profile__bg user-profile-bg--{{ vj4.model.adaptor.setting.UserSetting(udoc).get_setting('background_img') }}"></div>
|
||||||
<div class="section__body">
|
<div class="section__body">
|
||||||
<div class="media sidebar-user-profile">
|
<div class="media sidebar-user-profile">
|
||||||
<div class="media__left">
|
<div class="media__left">
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="media__body">
|
<div class="media__body">
|
||||||
<p>
|
<p>
|
||||||
{{ user.render_inline(udoc, dudict[ddoc['owner_uid']], avatar=false, modbadge=false) }}
|
{{ user.render_inline(udoc, avatar=false, modbadge=false) }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -101,19 +101,19 @@
|
|||||||
<div class="balancer sidebar-user-stat">
|
<div class="balancer sidebar-user-stat">
|
||||||
<div class="balancer__body">
|
<div class="balancer__body">
|
||||||
<div class="numbox">
|
<div class="numbox">
|
||||||
<div class="numbox__num medium">{{ dudict[ddoc['owner_uid']].nAccept or 0 }}</div>
|
<div class="numbox__num medium">{{ udict[ddoc['owner']].nAccept or 0 }}</div>
|
||||||
<div class="numbox__text">{{ _('Accepted') }}</div>
|
<div class="numbox__text">{{ _('Accepted') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="balancer__body">
|
<div class="balancer__body">
|
||||||
<div class="numbox">
|
<div class="numbox">
|
||||||
<div class="numbox__num medium">{{ dudict[ddoc['owner_uid']]['num_liked'] or 0 }}</div>
|
<div class="numbox__num medium">{{ udict[ddoc['owner']].nLike or 0 }}</div>
|
||||||
<div class="numbox__text">{{ _('Solutions Liked') }}</div>
|
<div class="numbox__text">{{ _('Solutions Liked') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="balancer__body">
|
<div class="balancer__body">
|
||||||
<div class="numbox">
|
<div class="numbox">
|
||||||
<div class="numbox__num medium">{{ dudict[ddoc['owner_uid']]['num_problems'] or 0 }}</div>
|
<div class="numbox__num medium">{{ udict[ddoc['owner']].nProblems or 0 }}</div>
|
||||||
<div class="numbox__text">{{ _('Upload Problem') }}</div>
|
<div class="numbox__text">{{ _('Upload Problem') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -122,7 +122,7 @@
|
|||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
{% if vnode['doc_type'] == vj4.model.document.TYPE_PROBLEM %}
|
{% if vnode['doc_type'] == vj4.model.document.TYPE_PROBLEM %}
|
||||||
{% with pdoc=vnode, owner_udoc=udict[vnode['owner_uid']], owner_dudoc=dudict[vnode['owner_uid']] %}
|
{% with pdoc=vnode, owner_udoc=udict[vnode['owner']] %}
|
||||||
{% include "partials/problem_sidebar.html" %}
|
{% include "partials/problem_sidebar.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -8,11 +8,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="medium-3 columns">
|
<div class="medium-3 columns">
|
||||||
{% if vnode['doc_type'] == vj4.model.document.TYPE_PROBLEM %}
|
{% if vnode['doc_type'] == vj4.model.document.TYPE_PROBLEM %}
|
||||||
{% with pdoc=vnode, owner_udoc=udict[vnode['owner_uid']], owner_dudoc=dudict[vnode['owner_uid']] if dudict %}
|
{% with pdoc=vnode, owner_udoc=udict[vnode['owner']] %}
|
||||||
{% include "partials/problem_sidebar.html" %}
|
{% include "partials/problem_sidebar.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% elif vnode['doc_type'] == vj4.model.document.TYPE_CONTEST %}
|
{% elif vnode['doc_type'] == vj4.model.document.TYPE_CONTEST %}
|
||||||
{% with tdoc=vnode, owner_udoc=udict[vnode['owner_uid']], owner_dudoc=dudict[vnode['owner_uid']] if dudict %}
|
{% with tdoc=vnode, owner_udoc=udict[vnode['owner']] %}
|
||||||
{% include "partials/contest_sidebar.html" %}
|
{% include "partials/contest_sidebar.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% elif vnode['doc_type'] == vj4.model.document.TYPE_DISCUSSION_NODE %}
|
{% elif vnode['doc_type'] == vj4.model.document.TYPE_DISCUSSION_NODE %}
|
||||||
|
|||||||
@@ -57,52 +57,6 @@
|
|||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not htdocs %}
|
|
||||||
<!-- no homework -->
|
|
||||||
{% else %}
|
|
||||||
<div class="section page--homework_main">
|
|
||||||
<div class="section__header">
|
|
||||||
<h1 class="section__title">{{ _('Homework') }}</h1>
|
|
||||||
</div>
|
|
||||||
<ol class="section__list homework__list">
|
|
||||||
{% for tdoc in htdocs %}
|
|
||||||
<li class="section__list__item homework__item">
|
|
||||||
<div class="media">
|
|
||||||
<div class="media__left medium">
|
|
||||||
<div class="homework__date numbox">
|
|
||||||
<div class="numbox__num large">{{ datetime_span(tdoc['penalty_since'], false, '%d') }}</div>
|
|
||||||
<div class="numbox__text">{{ datetime_span(tdoc['penalty_since'], false, '%Y-%m') }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="media__body medium">
|
|
||||||
<h1 class="homework__title"><a href="{{ reverse_url('homework_detail', tid=tdoc['doc_id']) }}" data-emoji-enabled>{{ tdoc['title'] }}</a></h1>
|
|
||||||
<ul class="supplementary list">
|
|
||||||
<li>
|
|
||||||
{{ _('Status') }}: <span class="homework-status--text {{ handler.get_status(tdoc) }}">{{ _(handler.status_text(tdoc)) }}</span>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
{{ _('Open Since') }}: {{ datetime_span(tdoc['begin_at']) }}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
{% if handler.is_homework_extended(tdoc) or handler.is_done(tdoc) %}
|
|
||||||
{{ _('Hard Deadline') }}: {{ datetime_span(tdoc['end_at']) }}
|
|
||||||
{% else %}
|
|
||||||
{{ _('Deadline') }}: {{ datetime_span(tdoc['penalty_since']) }}
|
|
||||||
{% endif %}
|
|
||||||
</li>
|
|
||||||
{% if htsdict[tdoc['doc_id']]['attend'] == 1 %}
|
|
||||||
<li class="homework__info-attended">
|
|
||||||
<span class="icon icon-check"></span> {{ _('Claimed') }}
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if not trdocs %}
|
{% if not trdocs %}
|
||||||
<!-- no training -->
|
<!-- no training -->
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for role in roles %}
|
{% for role in roles %}
|
||||||
{% for rudoc in rudocs[role] %}
|
{% for rudoc in rudocs[role] %}
|
||||||
{% set is_disabled=(rudoc['uid'] == handler.domain['owner_uid']) %}
|
{% set is_disabled=(rudoc['uid'] == handler.domain['owner']) %}
|
||||||
<tr data-uid="{{ rudoc['uid'] }}">
|
<tr data-uid="{{ rudoc['uid'] }}">
|
||||||
<td class="col--checkbox">
|
<td class="col--checkbox">
|
||||||
<label class="compact checkbox">
|
<label class="compact checkbox">
|
||||||
|
|||||||
@@ -61,7 +61,7 @@
|
|||||||
<span data-tooltip="{{ _('This is a built-in domain that every user is in.') }}">{{ ddoc['name'] }} ({{ ddoc['_id'] }})</span>
|
<span data-tooltip="{{ _('This is a built-in domain that every user is in.') }}">{{ ddoc['name'] }} ({{ ddoc['_id'] }})</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="col--role">
|
<td class="col--role">
|
||||||
{{ dudict.get(ddoc['_id'], {})['role']|default(vj4.model.builtin.ROLE_DEFAULT) }}
|
{{ udict.get(ddoc['_id'], {})['role']|default(model.builtin.ROLE_DEFAULT) }}
|
||||||
</td>
|
</td>
|
||||||
<td class="col--action">
|
<td class="col--action">
|
||||||
<a class="typo-a" href="{{ reverse_url('domain_main', domain_id=ddoc['_id']) }}">{{ _('Visit') }}</a>
|
<a class="typo-a" href="{{ reverse_url('domain_main', domain_id=ddoc['_id']) }}">{{ _('Visit') }}</a>
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
{{ ddoc['name'] }} ({{ ddoc['_id'] }})
|
{{ ddoc['name'] }} ({{ ddoc['_id'] }})
|
||||||
</td>
|
</td>
|
||||||
<td class="col--role">
|
<td class="col--role">
|
||||||
{{ dudict.get(ddoc['_id'], {})['role']|default(vj4.model.builtin.ROLE_DEFAULT) }}
|
{{ udict.get(ddoc['_id'], {})['role']|default(model.builtin.ROLE_DEFAULT) }}
|
||||||
</td>
|
</td>
|
||||||
<td class="col--action">
|
<td class="col--action">
|
||||||
<a class="typo-a" href="{{ reverse_url('domain_main', domain_id=ddoc['_id']) }}">{{ _('Visit') }}</a>
|
<a class="typo-a" href="{{ reverse_url('domain_main', domain_id=ddoc['_id']) }}">{{ _('Visit') }}</a>
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
{% extends "layout/basic.html" %}
|
|
||||||
{% import "components/record.html" as record with context %}
|
|
||||||
{% import "components/problem.html" as problem with context %}
|
|
||||||
{% block content %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="medium-9 columns">
|
|
||||||
<div class="section">
|
|
||||||
<div class="section__header">
|
|
||||||
<h1 class="section__title">{{ _('Homework Introduction') }}</h1>
|
|
||||||
</div>
|
|
||||||
<div class="section__body typo">
|
|
||||||
{{ tdoc['content']|markdown }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="section">
|
|
||||||
<div class="section__header">
|
|
||||||
<h1 class="section__title">{{ _('Problem') }}</h1>
|
|
||||||
</div>
|
|
||||||
{% if handler.is_not_started(tdoc) %}
|
|
||||||
{{ nothing.render('This homework is not open and you cannot view problems.') }}
|
|
||||||
{% else %}
|
|
||||||
{% if attended or model.contest.is_done(tdoc) %}
|
|
||||||
<div class="section__body no-padding">
|
|
||||||
<table class="data-table">
|
|
||||||
<colgroup>
|
|
||||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
|
||||||
<col class="col--status">
|
|
||||||
<col class="col--submit-at">
|
|
||||||
{% endif %}
|
|
||||||
<col class="col--problem">
|
|
||||||
</colgroup>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
{% if handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
|
||||||
<th class="col--status record-status--border">{{ _('Status') }}</th>
|
|
||||||
<th class="col--submit-at">{{ _('Last Submit At') }}</th>
|
|
||||||
{% endif %}
|
|
||||||
<th class="col--problem">{{ _('Problem') }}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% 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 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 %}
|
|
||||||
{% else %}
|
|
||||||
<td class="col--status record-status--border">{{ _('No Submissions') }}</td>
|
|
||||||
<td class="col--submit-at">-</td>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
<td class="col--problem col--problem-name">
|
|
||||||
#{{ loop.index }}:
|
|
||||||
{{ problem.render_problem_title(pdict[pid], tdoc=tdoc, show_invisible_flag=false, show_tags=false) }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
{{ nothing.render('Please claim the assignment to see the problems.') }}
|
|
||||||
{% endif %}{# attended or handler.is_done(tdoc) #}
|
|
||||||
{% endif %}{# handler.is_not_started(tdoc) #}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="medium-3 columns">
|
|
||||||
{% include "partials/homework_sidebar.html" %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
{% extends "layout/basic.html" %}
|
|
||||||
{% block content %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="medium-9 columns">
|
|
||||||
<div class="section">
|
|
||||||
<div class="section__body">
|
|
||||||
<form method="post">
|
|
||||||
{{ form.form_text(columns=8, label='Title', name='title', placeholder=_('title'), value=tdoc['title']|default(''), autofocus=True) }}
|
|
||||||
<div class="row">
|
|
||||||
{{ form.form_text(columns=4, label='Begin Date', name='begin_at_date', placeholder='YYYY-mm-dd', value=date_begin_text, date=True, row=False) }}
|
|
||||||
{{ form.form_text(columns=4, last_column=True, label='Begin Time', name='begin_at_time', placeholder='HH:MM', value=time_begin_text, time=True, row=False) }}
|
|
||||||
</div><div class="row">
|
|
||||||
{{ form.form_text(columns=4, label='End Date', name='penalty_since_date', placeholder='YYYY-mm-dd', value=date_penalty_text, date=True, row=False) }}
|
|
||||||
{{ form.form_text(columns=4, last_column=True, label='End Time', name='penalty_since_time', placeholder='HH:MM', value=time_penalty_text, time=True, row=False) }}
|
|
||||||
</div>
|
|
||||||
{{ form.form_text(columns=4, label='Extension (days)', name='extension_days', value=extension_days) }}
|
|
||||||
<div class="row"><div class="columns">
|
|
||||||
<label>
|
|
||||||
{{ _('Extension Score Penalty') }}
|
|
||||||
<textarea name="penalty_rules" class="monospace textbox">{% if tdoc %}{{ penalty_rules }}{% else %}{% include 'partials/homework_default_penalty_rules.yaml' %}{% endif %}</textarea>
|
|
||||||
</label>
|
|
||||||
</div></div>
|
|
||||||
{{ form.form_text(columns=None, label='Problems', name='pids', value=pids) }}
|
|
||||||
{{ form.form_textarea(columns=None, label='Content', name='content', value=tdoc['content']|default(''), markdown=True) }}
|
|
||||||
<div class="row"><div class="columns">
|
|
||||||
<input type="hidden" name="csrfToken" value="{{ handler.csrfToken }}">
|
|
||||||
{% if page_name == 'homework_edit' %}
|
|
||||||
<button type="submit" class="rounded primary button">
|
|
||||||
{{ _('Update') }}
|
|
||||||
</button>
|
|
||||||
{% else %}
|
|
||||||
<button type="submit" class="rounded primary button">
|
|
||||||
{{ _('Create') }}
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
<button type="button" class="rounded button" onclick="window.history.go(-1)">
|
|
||||||
{{ _('Cancel') }}
|
|
||||||
</button>
|
|
||||||
</div></div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="medium-3 columns">
|
|
||||||
{% include 'components/md_hint.html' %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
{% extends "layout/basic.html" %}
|
|
||||||
{% block content %}
|
|
||||||
<script>
|
|
||||||
var Context = {{ {
|
|
||||||
'docs': calendar_tdocs,
|
|
||||||
}|json|safe }};
|
|
||||||
</script>
|
|
||||||
<div class="row">
|
|
||||||
<div class="medium-12 columns">
|
|
||||||
<div class="section">
|
|
||||||
<div class="section__header">
|
|
||||||
<h1 class="section__title">{{ _('All Homeworks') }}</h1>
|
|
||||||
<div class="section__tools">
|
|
||||||
<span class="nojs-hide">
|
|
||||||
{{ _('View') }}:
|
|
||||||
<select class="inline compact select" name="homework_display">
|
|
||||||
<option value="calendar">{{ _('Calendar View') }}</option>
|
|
||||||
<option value="list">{{ _('List View') }}</option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
{% if handler.hasPerm(perm.PERM_CREATE_HOMEWORK) %}
|
|
||||||
<a class="compact button" href="{{ reverse_url('homework_create') }}">{{ _('Create Homework') }}</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="section__body no-padding">
|
|
||||||
{% if not tdocs %}
|
|
||||||
{{ nothing.render('There is no homework so far ╰(*°▽°*)╯') }}
|
|
||||||
{% else %}
|
|
||||||
<ol class="section__list homework__list hasjs--hide">
|
|
||||||
{% for tdoc in tdocs %}
|
|
||||||
<li class="section__list__item homework__item">
|
|
||||||
<div class="media">
|
|
||||||
<div class="media__left medium">
|
|
||||||
<div class="homework__date numbox">
|
|
||||||
<div class="numbox__num large">{{ datetime_span(tdoc['penalty_since'], false, '%d') }}</div>
|
|
||||||
<div class="numbox__text">{{ datetime_span(tdoc['penalty_since'], false, '%Y-%m') }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="media__body medium">
|
|
||||||
<h1 class="homework__title"><a href="{{ reverse_url('homework_detail', tid=tdoc['doc_id']) }}" data-emoji-enabled>{{ tdoc['title'] }}</a></h1>
|
|
||||||
<ul class="supplementary list">
|
|
||||||
<li>
|
|
||||||
{{ _('Status') }}: <span class="homework-status--text {{ handler.get_status(tdoc) }}">{{ _(handler.status_text(tdoc)) }}</span>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
{{ _('Open Since') }}: {{ datetime_span(tdoc['begin_at']) }}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
{% if handler.is_homework_extended(tdoc) or model.homework.is_done(tdoc) %}
|
|
||||||
{{ _('Hard Deadline') }}: {{ datetime_span(tdoc['end_at']) }}
|
|
||||||
{% else %}
|
|
||||||
{{ _('Deadline') }}: {{ datetime_span(tdoc['penalty_since']) }}
|
|
||||||
{% endif %}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ol>
|
|
||||||
<div name="calendar_entry"></div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
<span class="icon icon-award"></span> {{ _('View Contest') }}
|
<span class="icon icon-award"></span> {{ _('View Contest') }}
|
||||||
</a></li>
|
</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not attended and not model.contest.is_done(tdoc) %}
|
{% if not attended and not model.contest.isDone(tdoc) %}
|
||||||
<li class="menu__item">
|
<li class="menu__item">
|
||||||
{% if handler.hasPerm(perm.PERM_ATTEND_CONTEST) and handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
{% if handler.hasPerm(perm.PERM_ATTEND_CONTEST) and handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
||||||
<form action="/c/{{ tdoc._id }}" method="POST">
|
<form action="/c/{{ tdoc._id }}" method="POST">
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="section__body typo">
|
<div class="section__body typo">
|
||||||
<dl class="large horizontal">
|
<dl class="large horizontal">
|
||||||
<dt>{{ _('Status') }}</dt><dd>{{ _(model.contest.status_text(tdoc)) }}</dd>
|
<dt>{{ _('Status') }}</dt><dd>{{ _(model.contest.statusText(tdoc)) }}</dd>
|
||||||
<dt>{{ _('Rule') }}</dt><dd>{{ model.contest.RULES[tdoc.rule].TEXT }}</dd>
|
<dt>{{ _('Rule') }}</dt><dd>{{ model.contest.RULES[tdoc.rule].TEXT }}</dd>
|
||||||
<dt>{{ _('Problem') }}</dt><dd>{{ tdoc.pids.length }}</dd>
|
<dt>{{ _('Problem') }}</dt><dd>{{ tdoc.pids.length }}</dd>
|
||||||
<dt>{{ _('Start at') }}</dt><dd>{{ contest.render_time(tdoc.beginAt) }}</dd>
|
<dt>{{ _('Start at') }}</dt><dd>{{ contest.render_time(tdoc.beginAt) }}</dd>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
{{ user.render_inline(udict[ddoc['owner_uid']], dudoc=dudoc if dudoc else dudict[ddoc['owner_uid']], badge=false) }}
|
{{ user.render_inline(udict[ddoc['owner']], badge=false) }}
|
||||||
@
|
@
|
||||||
{{ datetime_span(ddoc['update_at'] or ddoc['_id'].generationTime) }}
|
{{ datetime_span(ddoc['update_at'] or ddoc['_id'].generationTime) }}
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
# Format:
|
|
||||||
# hours: coefficient
|
|
||||||
1: 0.9
|
|
||||||
3: 0.8
|
|
||||||
12: 0.75
|
|
||||||
9999: 0.5
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
{% import "components/user.html" as user with context %}
|
|
||||||
{% import "components/homework.html" as homework with context %}
|
|
||||||
<div class="section side">
|
|
||||||
{% if page_name != 'homework_detail' %}
|
|
||||||
<a class="contest-sidebar__bg" href="{{ reverse_url('homework_detail', tid=tdoc['doc_id']) }}">
|
|
||||||
<div class="section__body">
|
|
||||||
<h1>{{ tdoc['title'] }}</h1>
|
|
||||||
<div class="contest-sidebar__status">
|
|
||||||
{% if attended %}
|
|
||||||
<span class="icon icon-check"></span> {{ _('Claimed') }}
|
|
||||||
{% else %}
|
|
||||||
<span class="icon icon-schedule"></span> {{ _('Not Claimed') }}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
<div>
|
|
||||||
<ol class="menu">
|
|
||||||
{% if page_name != 'homework_detail' %}
|
|
||||||
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('homework_detail', tid=tdoc['doc_id']) }}">
|
|
||||||
<span class="icon icon-homework"></span> {{ _('View Homework') }}
|
|
||||||
</a></li>
|
|
||||||
{% endif %}
|
|
||||||
{% if not attended and not model.contest.is_done(tdoc) %}
|
|
||||||
<li class="menu__item">
|
|
||||||
{% if handler.hasPerm(perm.PERM_ATTEND_HOMEWORK) and handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
|
||||||
<form action="{{ reverse_url('homework_detail', tid=tdoc['doc_id']) }}" method="POST">
|
|
||||||
<input type="hidden" name="operation" value="attend">
|
|
||||||
<input type="hidden" name="csrfToken" value="{{ handler.csrfToken }}">
|
|
||||||
<button class="menu__link" type="submit">
|
|
||||||
<span class="icon icon-add"></span> {{ _('Claim Homework') }}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
{% elif not handler.hasPerm(perm.PERM_LOGGEDIN) %}
|
|
||||||
<a class="menu__link" href="javascript:showSignInDialog()">
|
|
||||||
<span class="icon icon-add"></span> {{ _('Login to Claim Homework') }}
|
|
||||||
</a>
|
|
||||||
{% else %}
|
|
||||||
<a class="menu__link disabled" href="javascript:;">
|
|
||||||
<span class="icon icon-add"></span> {{ _('No Permission to Claim Homework') }}
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
{% if handler.canShowScoreboard(tdoc, False) %}
|
|
||||||
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('homework_scoreboard', tid=tdoc['doc_id']) }}">
|
|
||||||
<span class="icon icon-statistics"></span> {{ _('Scoreboard') }}
|
|
||||||
</a></li>
|
|
||||||
{% elif handler.canViewHiddenScoreboard(tdoc) %}
|
|
||||||
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('homework_scoreboard', tid=tdoc['doc_id']) }}">
|
|
||||||
<span class="icon icon-statistics"></span> {{ _('Scoreboard (Hidden)') }}
|
|
||||||
</a></li>
|
|
||||||
{% endif %}
|
|
||||||
{% if handler.own(tdoc, perm.PERM_EDIT_HOMEWORK_SELF) or handler.hasPerm(perm.PERM_EDIT_HOMEWORK) %}
|
|
||||||
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('homework_edit', tid=tdoc['doc_id']) }}">
|
|
||||||
<span class="icon icon-edit"></span> {{ _('Edit Homework') }}
|
|
||||||
</a></li>
|
|
||||||
{% endif %}
|
|
||||||
{% if handler.hasPerm(perm.PERM_READ_RECORD_CODE) %}
|
|
||||||
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('homework_code', tid=tdoc['doc_id']) }}">
|
|
||||||
<span class="icon icon-download"></span> {{ _('Export All Code') }}
|
|
||||||
</a></li>
|
|
||||||
{% endif %}
|
|
||||||
{% if attended %}
|
|
||||||
<li class="menu__item"><a class="menu__link" href="/r?uid_or_name={{ handler.user['_id'] }}&tid={{ tdoc['doc_id'] }}">
|
|
||||||
<span class="icon icon-flag"></span> {{ _('My Recent Submissions') }}
|
|
||||||
</a></li>
|
|
||||||
{% endif %}
|
|
||||||
<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>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
<div class="section__body typo">
|
|
||||||
<dl class="large horizontal">
|
|
||||||
<dt>{{ _('Status') }}</dt><dd><span class="homework-status--text {{ handler.get_status(tdoc) }}">{{ _(handler.status_text(tdoc)) }}</span></dd>
|
|
||||||
<dt>{{ _('Problem') }}</dt><dd>{{ tdoc['pids']|length }}</dd>
|
|
||||||
<dt>{{ _('Open Since') }}</dt><dd>{{ homework.render_time(tdoc['begin_at']) }}</dd>
|
|
||||||
<dt>{{ _('Deadline') }}</dt><dd>{{ homework.render_time(tdoc['penalty_since']) }}</dd>
|
|
||||||
<dt>{{ _('Can be Extended For') }}</dt><dd>{{ homework.render_extension(tdoc) }} {{ _('hour(s)') }}</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -23,9 +23,6 @@
|
|||||||
{% if handler.hasPerm(perm.PERM_VIEW_CONTEST) %}
|
{% if handler.hasPerm(perm.PERM_VIEW_CONTEST) %}
|
||||||
{{ nav_item('/c', 'contest_main', 'contest') }}
|
{{ nav_item('/c', 'contest_main', 'contest') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if handler.hasPerm(perm.PERM_VIEW_HOMEWORK) %}
|
|
||||||
{{ nav_item('/h', 'homework_main', 'homework') }}
|
|
||||||
{% endif %}
|
|
||||||
{% if handler.hasPerm(perm.PERM_EDIT_DESCRIPTION) or handler.hasPerm(perm.PERM_EDIT_PERM) %}
|
{% if handler.hasPerm(perm.PERM_EDIT_DESCRIPTION) or handler.hasPerm(perm.PERM_EDIT_PERM) %}
|
||||||
{{ nav_item('/d', 'domain_manage', 'domain_manage') }}
|
{{ nav_item('/d', 'domain_manage', 'domain_manage') }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -1,15 +1,7 @@
|
|||||||
{% if tdoc %}
|
{% if tdoc %}
|
||||||
{% if tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST %}
|
{% include "partials/problem_sidebar_contest.html" %}
|
||||||
{% include "partials/problem_sidebar_contest.html" %}
|
{% set owner_udoc = udoc %}
|
||||||
{% set owner_udoc=udoc %}
|
{% include "partials/contest_sidebar.html" %}
|
||||||
{% set owner_dudoc=dudoc %}
|
|
||||||
{% include "partials/contest_sidebar.html" %}
|
|
||||||
{% else %}
|
|
||||||
{% include "partials/problem_sidebar_homework.html" %}
|
|
||||||
{% set owner_udoc=udoc %}
|
|
||||||
{% set owner_dudoc=dudoc %}
|
|
||||||
{% include "partials/homework_sidebar.html" %}
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
{% else %}
|
||||||
{% include "partials/problem_sidebar_normal.html" %}
|
{% include "partials/problem_sidebar_normal.html" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<div>
|
<div>
|
||||||
<ol class="menu">
|
<ol class="menu">
|
||||||
{% if model.contest.is_ongoing(tdoc) %}
|
{% if model.contest.isOngoing(tdoc) %}
|
||||||
{% if page_name == 'contest_detail_problem' %}
|
{% if page_name == 'contest_detail_problem' %}
|
||||||
<li class="menu__item scratchpad--hide"><a class="menu__link highlight" name="problem-sidebar__open-scratchpad" href="javascript:;" data-global-hotkey="alt+e">
|
<li class="menu__item scratchpad--hide"><a class="menu__link highlight" name="problem-sidebar__open-scratchpad" href="javascript:;" data-global-hotkey="alt+e">
|
||||||
<span class="icon icon-enlarge"></span> {{ _('Open Scratchpad') }} (Alt+E)
|
<span class="icon icon-enlarge"></span> {{ _('Open Scratchpad') }} (Alt+E)
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<li class="menu__item scratchpad--hide"><a class="menu__link{% if page_name == 'contest_detail_problem_submit' %} active{% endif %}" href="/c/{{ tdoc._id }}/p/{{ pdoc._id }}/submit">
|
<li class="menu__item scratchpad--hide"><a class="menu__link{% if page_name == 'contest_detail_problem_submit' %} active{% endif %}" href="/c/{{ tdoc._id }}/p/{{ pdoc._id }}/submit">
|
||||||
<span class="icon icon-send"></span> {{ _('Submit') }}
|
<span class="icon icon-send"></span> {{ _('Submit') }}
|
||||||
</a></li>
|
</a></li>
|
||||||
{% elif model.contest.is_done(tdoc) %}
|
{% elif model.contest.isDone(tdoc) %}
|
||||||
<li class="menu__item scratchpad--hide"><a class="menu__link" href="/p/{{ pdoc.pid }}">
|
<li class="menu__item scratchpad--hide"><a class="menu__link" href="/p/{{ pdoc.pid }}">
|
||||||
<span class="icon icon-send"></span> {{ _('Open in Problem Set') }}
|
<span class="icon icon-send"></span> {{ _('Open in Problem Set') }}
|
||||||
</a></li>
|
</a></li>
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
{% import "components/user.html" as user with context %}
|
|
||||||
<div class="section side section--problem-sidebar">
|
|
||||||
{% if page_name != 'problem_detail' and page_name != 'homework_detail_problem' %}
|
|
||||||
<div class="section__header">
|
|
||||||
<h1 class="section__title">{{ pdoc['title'] }}</h1>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<div>
|
|
||||||
<ol class="menu">
|
|
||||||
{% if handler.is_ongoing(tdoc) or handler.is_homework_extended(tdoc) %}
|
|
||||||
{% if page_name == 'homework_detail_problem' %}
|
|
||||||
<li class="menu__item scratchpad--hide"><a class="menu__link highlight" name="problem-sidebar__open-scratchpad" href="javascript:;" data-global-hotkey="alt+e">
|
|
||||||
<span class="icon icon-enlarge"></span> {{ _('Open Scratchpad') }} (Alt+E)
|
|
||||||
</a></li>
|
|
||||||
<li class="menu__item non-scratchpad--hide"><a class="menu__link" name="problem-sidebar__quit-scratchpad" href="javascript:;" data-global-hotkey="alt+q">
|
|
||||||
<span class="icon icon-enlarge"></span> {{ _('Quit Scratchpad') }} (Alt+Q)
|
|
||||||
</a></li>
|
|
||||||
{% else %}
|
|
||||||
<li class="menu__item"><a class="menu__link" href="{{ reverse_url('homework_detail_problem', tid=tdoc['doc_id'], pid=pdoc['_id']) }}">
|
|
||||||
<span class="icon icon-flag"></span> {{ _('View Problem') }}
|
|
||||||
</a></li>
|
|
||||||
{% endif %}
|
|
||||||
<li class="menu__item scratchpad--hide"><a class="menu__link{% if page_name == 'homework_detail_problem_submit' %} active{% endif %}" href="{{ reverse_url('homework_detail_problem_submit', tid=tdoc['doc_id'], pid=pdoc['_id']) }}">
|
|
||||||
<span class="icon icon-send"></span> {{ _('Submit') }}
|
|
||||||
</a></li>
|
|
||||||
{% elif model.contest.is_done(tdoc) %}
|
|
||||||
<li class="menu__item scratchpad--hide"><a class="menu__link" href="/p/{{ pdoc.pid }}">
|
|
||||||
<span class="icon icon-send"></span> {{ _('Open in Problem Set') }}
|
|
||||||
</a></li>
|
|
||||||
{% endif %}
|
|
||||||
{% if handler.own(pdoc, perm.PERM_EDIT_PROBLEM_SELF) or handler.hasPerm(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') }}
|
|
||||||
</a></li>
|
|
||||||
<li class="menu__item"><a class="menu__link{% if page_name == 'problem_settings' %} active{% endif %}" href="/p/{{ pdoc.pid }}/settings">
|
|
||||||
<span class="icon icon-wrench"></span> {{ _('Settings') }}
|
|
||||||
</a></li>
|
|
||||||
{% endif %}
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -152,9 +152,9 @@
|
|||||||
<dt>{{ _('Be Copied') }}</dt>
|
<dt>{{ _('Be Copied') }}</dt>
|
||||||
<dd>{{ pdoc['num_be_copied'] }}</dd>
|
<dd>{{ pdoc['num_be_copied'] }}</dd>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if pdoc['owner_uid'] != 0 %}
|
{% if pdoc['owner'] != 0 %}
|
||||||
<dt>{{ _('Uploaded By') }}</dt>
|
<dt>{{ _('Uploaded By') }}</dt>
|
||||||
<dd>{{ user.render_inline(owner_udoc, owner_dudoc, badge=false) }}</dd>
|
<dd>{{ user.render_inline(owner_udoc, badge=false) }}</dd>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
@@ -177,12 +177,6 @@
|
|||||||
<p><a href="/c/{{ tdoc._id }}">{{ tdoc['title'] }}</a></p>
|
<p><a href="/c/{{ tdoc._id }}">{{ tdoc['title'] }}</a></p>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if htdocs %}
|
|
||||||
<p>{{ _('In following homework') }}: </p>
|
|
||||||
{% for tdoc in htdocs %}
|
|
||||||
<p><a href="{{ reverse_url('homework_detail', tid=tdoc['doc_id']) }}">{{ tdoc['title'] }}</a></p>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -30,11 +30,11 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if tdoc %}
|
{% if tdoc %}
|
||||||
{% if tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST %}
|
{% if tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST %}
|
||||||
{% if model.contest.is_done(tdoc) %}
|
{% if model.contest.isDone(tdoc) %}
|
||||||
<blockquote class="note">
|
<blockquote class="note">
|
||||||
<p>{{ _('You cannot submit for this problem because the contest is ended. You can click "Open in Problem Set" to view this problem in normal mode.') }}</p>
|
<p>{{ _('You cannot submit for this problem because the contest is ended. You can click "Open in Problem Set" to view this problem in normal mode.') }}</p>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
{% endif %}{# handler.is_done(tdoc) #}
|
{% endif %}{# handler.isDone(tdoc) #}
|
||||||
{% endif %}{# tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST #}
|
{% endif %}{# tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST #}
|
||||||
{% endif %}{# tdoc #}
|
{% endif %}{# tdoc #}
|
||||||
{{ pdoc['content']|markdown|safe }}
|
{{ pdoc['content']|markdown|safe }}
|
||||||
@@ -44,7 +44,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="medium-3 columns">
|
<div class="medium-3 columns">
|
||||||
{% set owner_udoc = udoc %}
|
{% set owner_udoc = udoc %}
|
||||||
{% set owner_dudoc = dudoc %}
|
|
||||||
{% include "partials/problem_sidebar.html" %}
|
{% include "partials/problem_sidebar.html" %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="medium-3 columns">
|
<div class="medium-3 columns">
|
||||||
{% if page_name == 'problem_edit' %}
|
{% if page_name == 'problem_edit' %}
|
||||||
{% set owner_udoc=udoc %}{% set owner_dudoc=dudoc %}
|
{% set owner_udoc = udoc %}
|
||||||
{% include "partials/problem_sidebar.html" %}
|
{% include "partials/problem_sidebar.html" %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% include 'components/md_hint.html' %}
|
{% include 'components/md_hint.html' %}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="medium-3 columns">
|
<div class="medium-3 columns">
|
||||||
{% set owner_udoc=udoc %}{% set owner_dudoc=dudoc %}
|
{% set owner_udoc = udoc %}
|
||||||
{% include "partials/problem_sidebar.html" %}
|
{% include "partials/problem_sidebar.html" %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="medium-3 columns">
|
<div class="medium-3 columns">
|
||||||
{% set owner_udoc=udoc %}{% set owner_dudoc=dudoc %}
|
{% set owner_udoc = udoc %}
|
||||||
{% include "partials/problem_sidebar.html" %}
|
{% include "partials/problem_sidebar.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -60,7 +60,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="medium-3 columns">
|
<div class="medium-3 columns">
|
||||||
{% set owner_udoc=udoc %}{% set owner_dudoc=dudoc %}
|
{% set owner_udoc = udoc %}
|
||||||
{% include "partials/problem_sidebar.html" %}
|
{% include "partials/problem_sidebar.html" %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
<div class="section__body typo">
|
<div class="section__body typo">
|
||||||
<dl class="large horizontal">
|
<dl class="large horizontal">
|
||||||
<dt>{{ _('Submit By') }}</dt>
|
<dt>{{ _('Submit By') }}</dt>
|
||||||
<dd>{{ user.render_inline(udoc, dudoc, modbadge=false) }}</dd>
|
<dd>{{ user.render_inline(udoc, modbadge=false) }}</dd>
|
||||||
{% if pdoc %}
|
{% if pdoc %}
|
||||||
<dt>{{ _('Problem') }}</dt>
|
<dt>{{ _('Problem') }}</dt>
|
||||||
<dd>
|
<dd>
|
||||||
@@ -64,18 +64,11 @@
|
|||||||
</dd>
|
</dd>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if tdoc %}
|
{% if tdoc %}
|
||||||
{% if tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST %}
|
|
||||||
<dt>{{ _('Contest') }}</dt>
|
<dt>{{ _('Contest') }}</dt>
|
||||||
<dd>
|
<dd>
|
||||||
<a href="/c/{{ tdoc._id }}">{{ tdoc['title'] }}</a>
|
<a href="/c/{{ tdoc._id }}">{{ tdoc['title'] }}</a>
|
||||||
</dd>
|
</dd>
|
||||||
{% else %}{# tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST #}
|
{% endif %}
|
||||||
<dt>{{ _('Homework') }}</dt>
|
|
||||||
<dd>
|
|
||||||
<a href="{{ reverse_url('homework_detail', tid=tdoc['doc_id']) }}">{{ tdoc['title'] }}</a>
|
|
||||||
</dd>
|
|
||||||
{% endif %}{# tdoc['doc_type'] == vj4.model.document.TYPE_CONTEST #}
|
|
||||||
{% endif %}{# tdoc #}
|
|
||||||
{% if pdoc and (user._id == pdoc.owner or handler.hasPerm(perm.PERM_READ_PROBLEM_DATA)) %}
|
{% if pdoc and (user._id == pdoc.owner or handler.hasPerm(perm.PERM_READ_PROBLEM_DATA)) %}
|
||||||
<dt>{{ _('Problem Data') }}</dt>
|
<dt>{{ _('Problem Data') }}</dt>
|
||||||
<dd><a href="/p/{{ pdoc.pid }}/data"><span class="icon icon-download"></span> {{ _('Download') }}</a></dd>
|
<dd><a href="/p/{{ pdoc.pid }}/data"><span class="icon icon-download"></span> {{ _('Download') }}</a></dd>
|
||||||
|
|||||||
@@ -21,15 +21,15 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for node in tdoc['dag'] %}
|
{% for node in tdoc['dag'] %}
|
||||||
<div class="training__section {% if nsdict[node['_id']]['is_done'] %}done{% elif nsdict[node['_id']]['is_progress'] %}progress{% elif nsdict[node['_id']]['is_open'] %}open{% else %}invalid{% endif %} {% if nsdict[node['_id']]['is_progress'] or nsdict[node['_id']]['is_open'] %}expanded{% else %}collapsed{% endif %}">
|
<div class="training__section {% if nsdict[node['_id']]['isDone'] %}done{% elif nsdict[node['_id']]['is_progress'] %}progress{% elif nsdict[node['_id']]['is_open'] %}open{% else %}invalid{% endif %} {% if nsdict[node['_id']]['is_progress'] or nsdict[node['_id']]['is_open'] %}expanded{% else %}collapsed{% endif %}">
|
||||||
<div class="section__header clearfix">
|
<div class="section__header clearfix">
|
||||||
<div class="float-left">
|
<div class="float-left">
|
||||||
<h1 class="section__title">{{ _('Section') }} {{ node['_id'] }}. {{ node['title'] }}</h1>
|
<h1 class="section__title">{{ _('Section') }} {{ node['_id'] }}. {{ node['title'] }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="float-right">
|
<div class="float-right">
|
||||||
<h1 class="section__title training-section-status--text {% if nsdict[node['_id']]['is_done'] %}done{% elif nsdict[node['_id']]['is_progress'] %}progress{% elif nsdict[node['_id']]['is_open'] %}open{% else %}invalid{% endif %}">
|
<h1 class="section__title training-section-status--text {% if nsdict[node['_id']]['isDone'] %}done{% elif nsdict[node['_id']]['is_progress'] %}progress{% elif nsdict[node['_id']]['is_open'] %}open{% else %}invalid{% endif %}">
|
||||||
<span class="icon training-section-status--icon {% if nsdict[node['_id']]['is_done'] %}done{% elif nsdict[node['_id']]['is_progress'] %}progress{% elif nsdict[node['_id']]['is_open'] %}open{% else %}invalid{% endif %}"></span>
|
<span class="icon training-section-status--icon {% if nsdict[node['_id']]['isDone'] %}done{% elif nsdict[node['_id']]['is_progress'] %}progress{% elif nsdict[node['_id']]['is_open'] %}open{% else %}invalid{% endif %}"></span>
|
||||||
{% if nsdict[node['_id']]['is_done'] %}
|
{% if nsdict[node['_id']]['isDone'] %}
|
||||||
{{ _('Completed') }}
|
{{ _('Completed') }}
|
||||||
{% elif nsdict[node['_id']]['is_progress'] %}
|
{% elif nsdict[node['_id']]['is_progress'] %}
|
||||||
{{ _('In Progress') }}
|
{{ _('In Progress') }}
|
||||||
@@ -196,7 +196,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<dt>{{ _('Enrollees') }}</dt><dd>{{ tdoc['enroll']|default(0) }}</dd>
|
<dt>{{ _('Enrollees') }}</dt><dd>{{ tdoc['enroll']|default(0) }}</dd>
|
||||||
<dt>{{ _('Created By') }}</dt>
|
<dt>{{ _('Created By') }}</dt>
|
||||||
<dd>{{ user.render_inline(owner_udoc, owner_dudoc, badge=false) }}</dd>
|
<dd>{{ user.render_inline(owner_udoc, badge=false) }}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export default function (env = {}) {
|
|||||||
return {
|
return {
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
options: {
|
options: {
|
||||||
...require(root('package.json')).babelForProject, // eslint-disable-line
|
...require(root('package.json')).babelForProject, // eslint-disable-line
|
||||||
cacheDirectory,
|
cacheDirectory,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -165,11 +165,6 @@ export default function (env = {}) {
|
|||||||
// don't include momentjs locale files
|
// don't include momentjs locale files
|
||||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||||
|
|
||||||
new MonacoWebpackPlugin({
|
|
||||||
// available options are documented at https://github.com/Microsoft/monaco-editor-webpack-plugin#options
|
|
||||||
languages: ['cpp', 'csharp', 'java', 'javascript', 'python', 'rust', 'ruby', 'php', 'pascal', 'go'],
|
|
||||||
}),
|
|
||||||
|
|
||||||
new ExtractCssPlugin({
|
new ExtractCssPlugin({
|
||||||
filename: 'vj4.css?[contenthash:10]',
|
filename: 'vj4.css?[contenthash:10]',
|
||||||
allChunks: true,
|
allChunks: true,
|
||||||
@@ -204,9 +199,6 @@ export default function (env = {}) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
env.production
|
|
||||||
? new UglifyJsPlugin({ sourceMap: true })
|
|
||||||
: function () { },
|
|
||||||
env.production
|
env.production
|
||||||
? new OptimizeCssAssetsPlugin()
|
? new OptimizeCssAssetsPlugin()
|
||||||
: function () { },
|
: function () { },
|
||||||
@@ -228,6 +220,11 @@ export default function (env = {}) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
new MonacoWebpackPlugin({
|
||||||
|
// available options are documented at https://github.com/Microsoft/monaco-editor-webpack-plugin#options
|
||||||
|
languages: ['cpp', 'csharp', 'java', 'javascript', 'python', 'rust', 'ruby', 'php', 'pascal', 'go'],
|
||||||
|
}),
|
||||||
|
|
||||||
// Finally, output asset hashes
|
// Finally, output asset hashes
|
||||||
new StaticManifestPlugin({
|
new StaticManifestPlugin({
|
||||||
fileName: 'static-manifest.json',
|
fileName: 'static-manifest.json',
|
||||||
|
|||||||
@@ -252,10 +252,6 @@ $training-section-status-icon = {
|
|||||||
$contest-color = {
|
$contest-color = {
|
||||||
'acm': #6BB67A,
|
'acm': #6BB67A,
|
||||||
'oi': #F5C735,
|
'oi': #F5C735,
|
||||||
|
'homework': #FFCDCD,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Homework Status
|
|
||||||
$homework-status-color = {
|
|
||||||
'not_started': $text-2-color,
|
|
||||||
'ongoing': $success-color,
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
.homework-status--text
|
|
||||||
for key, value in $homework-status-color
|
|
||||||
&.{key}
|
|
||||||
color: value
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
.page--homework_detail
|
|
||||||
.col--status
|
|
||||||
width: rem(150px)
|
|
||||||
|
|
||||||
.col--submit-at
|
|
||||||
width: rem(150px)
|
|
||||||
|
|
||||||
.col--problem
|
|
||||||
border-left: 1px solid $table-border-color
|
|
||||||
|
|
||||||
+mobile()
|
|
||||||
.col--submit-at
|
|
||||||
display: none
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import { NamedPage } from 'vj/misc/PageLoader';
|
|
||||||
import Calendar from 'vj/components/calendar';
|
|
||||||
import i18n from 'vj/utils/i18n';
|
|
||||||
import { parse as parseMongoId } from 'vj/utils/mongoId';
|
|
||||||
|
|
||||||
const page = new NamedPage('homework_main', () => {
|
|
||||||
// Homework Calendar
|
|
||||||
if (Context.docs) {
|
|
||||||
const events = Context.docs.map((doc) => ({
|
|
||||||
beginAt: doc.begin_at * 1000,
|
|
||||||
endAt: doc.end_at * 1000,
|
|
||||||
title: doc.title,
|
|
||||||
maskFrom: doc.penalty_since ? doc.penalty_since * 1000 : null,
|
|
||||||
maskTitle: i18n('Time Extension'),
|
|
||||||
colorIndex: parseMongoId(doc.id).timestamp % 12,
|
|
||||||
link: doc.url,
|
|
||||||
}));
|
|
||||||
const calendar = new Calendar(events);
|
|
||||||
calendar.getDom().appendTo('[name="calendar_entry"]');
|
|
||||||
$('[name="homework_display"]').change((ev) => {
|
|
||||||
switch (ev.currentTarget.value) {
|
|
||||||
case 'calendar':
|
|
||||||
$('.homework__list').hide();
|
|
||||||
$('[name="calendar_entry"]').show();
|
|
||||||
break;
|
|
||||||
case 'list':
|
|
||||||
$('.homework__list').show();
|
|
||||||
$('[name="calendar_entry"]').hide();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error('Unexpected display parameter');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default page;
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
$highlight-button-color = #F6DF45
|
|
||||||
|
|
||||||
.page--homework_main
|
|
||||||
.homework__title
|
|
||||||
line-height: 1.2em
|
|
||||||
margin-bottom: rem(10px)
|
|
||||||
font-size: rem($font-size-title)
|
|
||||||
|
|
||||||
.homework__info-attended
|
|
||||||
color: $success-color
|
|
||||||
|
|
||||||
.homework__date
|
|
||||||
margin-right: rem(10px)
|
|
||||||
white-space: nowrap
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
.page--homework_scoreboard
|
|
||||||
.col--rank
|
|
||||||
width: rem(70px)
|
|
||||||
border-right: 1px solid $table-border-color
|
|
||||||
|
|
||||||
.col--user
|
|
||||||
width: rem(140px)
|
|
||||||
|
|
||||||
.col--total_score, .col--total_time_str, .col--solved_problems
|
|
||||||
width: rem(100px)
|
|
||||||
text-align: center
|
|
||||||
|
|
||||||
.col--problem_detail
|
|
||||||
border-left: 1px solid $table-border-color
|
|
||||||
text-align: center
|
|
||||||
|
|
||||||
+mobile()
|
|
||||||
.col--user
|
|
||||||
width: auto
|
|
||||||
|
|
||||||
.col--problem_detail
|
|
||||||
display: none
|
|
||||||
Reference in new issue
Block a user