mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-19 17:38:19 +08:00
bug fixes & improvements for on site contest
This commit is contained in:
19 files changed
+383
-68
No files matched your search
@@ -24,6 +24,22 @@ const status = {
|
||||
[STATUS.STATUS_ETC]: 'SYSTEM_ERROR',
|
||||
[STATUS.STATUS_CANCELED]: 'CANCELED',
|
||||
};
|
||||
const statusPrivate = {
|
||||
[STATUS.STATUS_WRONG_ANSWER]: 'REJECTED',
|
||||
[STATUS.STATUS_ACCEPTED]: 'CORRECT',
|
||||
[STATUS.STATUS_COMPILING]: 'PENDING',
|
||||
[STATUS.STATUS_WAITING]: 'PENDING',
|
||||
[STATUS.STATUS_JUDGING]: 'PENDING',
|
||||
[STATUS.STATUS_TIME_LIMIT_EXCEEDED]: 'REJECTED',
|
||||
[STATUS.STATUS_MEMORY_LIMIT_EXCEEDED]: 'REJECTED',
|
||||
[STATUS.STATUS_RUNTIME_ERROR]: 'REJECTED',
|
||||
[STATUS.STATUS_SYSTEM_ERROR]: 'SYSTEM_ERROR',
|
||||
[STATUS.STATUS_COMPILE_ERROR]: 'COMPILATION_ERROR',
|
||||
[STATUS.STATUS_FETCHED]: 'PENDING',
|
||||
[STATUS.STATUS_OUTPUT_LIMIT_EXCEEDED]: 'REJECTED',
|
||||
[STATUS.STATUS_ETC]: 'SYSTEM_ERROR',
|
||||
[STATUS.STATUS_CANCELED]: 'CANCELED',
|
||||
};
|
||||
|
||||
function submissionBase(tdoc: Tdoc, rdoc: RecordDoc, uid?: number) {
|
||||
// NOTE: rdoc can be either record, or a tsdoc detail entry
|
||||
@@ -39,6 +55,7 @@ function submissionBase(tdoc: Tdoc, rdoc: RecordDoc, uid?: number) {
|
||||
|
||||
async function loadContestState(tdoc: Tdoc, realtime: boolean) {
|
||||
const tsdocs = await ContestModel.getMultiStatus(tdoc.domainId, { docId: tdoc.docId }).toArray();
|
||||
const ended = ContestModel.isDone(tdoc);
|
||||
const udict = await UserModel.getList(tdoc.domainId, tsdocs.map((i) => i.uid));
|
||||
const teams = tsdocs.map((i) => {
|
||||
const udoc = udict[i.uid];
|
||||
@@ -46,11 +63,11 @@ async function loadContestState(tdoc: Tdoc, realtime: boolean) {
|
||||
team_id: `${udoc._id}`,
|
||||
name: udoc.uname,
|
||||
organization: udoc.school,
|
||||
members: udoc.members?.split(',').filter((t) => t) || [],
|
||||
members: 'members' in udoc ? (typeof udoc.members === 'string' ? udoc.members.split(',').filter((t) => t) : udoc.members) : [],
|
||||
coach: udoc.coach,
|
||||
badge: { url: avatar(udoc.avatar) },
|
||||
group: [
|
||||
...(udoc.group || []),
|
||||
...((udoc.group || []).filter((g) => !Number.isSafeInteger(+g))),
|
||||
i.unrank ? 'unofficial' : 'official',
|
||||
],
|
||||
};
|
||||
@@ -58,11 +75,11 @@ async function loadContestState(tdoc: Tdoc, realtime: boolean) {
|
||||
return {
|
||||
submissions: tsdocs.flatMap((i) => (i.journal || []).map((j) => {
|
||||
const submit = new ObjectId(j.rid as string).getTimestamp().getTime();
|
||||
const curStatus = status[j.status] || 'SYSTEM_ERROR';
|
||||
const curStatus = (ended ? status : statusPrivate)[j.status] || 'SYSTEM_ERROR';
|
||||
return {
|
||||
...submissionBase(tdoc, j, i.uid),
|
||||
status: (ContestModel.isLocked(tdoc) && submit > tdoc.lockAt.getTime() && !realtime)
|
||||
? 'PENDING'
|
||||
? 'FROZEN'
|
||||
: curStatus,
|
||||
};
|
||||
})),
|
||||
@@ -95,7 +112,7 @@ export async function apply(ctx: Context, config: ReturnType<typeof Config>) {
|
||||
const tdoc = await ContestModel.get(rdoc.domainId, rdoc.contest);
|
||||
const submit = new ObjectId(rdoc._id).getTimestamp().getTime();
|
||||
const isLocked = ContestModel.isLocked(tdoc) && submit > tdoc.lockAt.getTime();
|
||||
const statusStr = status[rdoc.status] || 'SYSTEM_ERROR';
|
||||
const statusStr = statusPrivate[rdoc.status] || 'SYSTEM_ERROR';
|
||||
if (realtime) {
|
||||
const found = realtime.submissions.find((i) => i.submission_id === rdoc._id.toHexString());
|
||||
if (found) found.status = statusStr;
|
||||
@@ -104,7 +121,7 @@ export async function apply(ctx: Context, config: ReturnType<typeof Config>) {
|
||||
if (pub) {
|
||||
const found = pub.submissions.find((i) => i.submission_id === rdoc._id.toHexString());
|
||||
if (found && !isLocked) found.status = statusStr;
|
||||
else pub.submissions.push({ ...submissionBase(tdoc, rdoc), status: isLocked ? 'PENDING' : statusStr });
|
||||
else pub.submissions.push({ ...submissionBase(tdoc, rdoc), status: isLocked ? 'FROZEN' : statusStr });
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -115,12 +132,14 @@ export async function apply(ctx: Context, config: ReturnType<typeof Config>) {
|
||||
groups: 'groups',
|
||||
json: Types.Boolean,
|
||||
realtime: Types.Boolean,
|
||||
badge: Types.Boolean,
|
||||
banner: Types.Boolean,
|
||||
gold: Schema.transform(Schema.union([Schema.string(), Schema.number().step(1).min(0)]), (v) => +v).default(0),
|
||||
silver: Schema.transform(Schema.union([Schema.string(), Schema.number().step(1).min(0)]), (v) => +v).default(0),
|
||||
bronze: Schema.transform(Schema.union([Schema.string(), Schema.number().step(1).min(0)]), (v) => +v).default(0),
|
||||
}, {
|
||||
async display({
|
||||
tdoc, groups, json, realtime, gold, silver, bronze,
|
||||
tdoc, groups, json, realtime, gold, silver, bronze, badge = true, banner = false,
|
||||
}) {
|
||||
if (realtime && !this.user.own(tdoc)) this.checkPerm(PERM.PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD);
|
||||
if (json || this.request.json) {
|
||||
@@ -143,6 +162,7 @@ export async function apply(ctx: Context, config: ReturnType<typeof Config>) {
|
||||
unofficial: '打星队伍',
|
||||
...Object.fromEntries(groups.filter((i) => relatedGroups.includes(i.name)).map((i) => [i.name, i.name])),
|
||||
},
|
||||
...(badge ? { badge: 'Badge' } : {}),
|
||||
organization: 'School',
|
||||
status_time_display: {
|
||||
correct: true,
|
||||
@@ -165,9 +185,7 @@ export async function apply(ctx: Context, config: ReturnType<typeof Config>) {
|
||||
logo: {
|
||||
preset: 'ICPC',
|
||||
},
|
||||
// banner: {
|
||||
// url: 'assets/banner.png',
|
||||
// },
|
||||
...(banner ? { banner: { url: 'banner.jpg' } } : {}),
|
||||
options: {
|
||||
submission_timestamp_unit: 'millisecond',
|
||||
},
|
||||
@@ -178,12 +196,15 @@ export async function apply(ctx: Context, config: ReturnType<typeof Config>) {
|
||||
this.response.template = 'xcpcio_board.html';
|
||||
let query = '';
|
||||
if (gold || silver || bronze) query = `&gold=${gold}&silver=${silver}&bronze=${bronze}`;
|
||||
if (badge) query += '&badge=true';
|
||||
if (banner) query += '&banner=true';
|
||||
if (realtime) query += '&realtime=true';
|
||||
const endpoint = `/d/${tdoc.domainId}/contest/${tdoc.docId}/scoreboard/xcpcio`;
|
||||
this.response.body = {
|
||||
dataSource: `${endpoint}?json=true${query}#allInOne=true`,
|
||||
js: indexJs,
|
||||
css: indexCss,
|
||||
realtime,
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
@@ -30,6 +30,22 @@
|
||||
</style>
|
||||
<script type="module" crossorigin src="{{ '/' if process.env.DEV else UiContext.cdn_prefix }}assets/index-{{ js }}.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="{{ '/' if process.env.DEV else UiContext.cdn_prefix }}assets/index-{{ css }}.css">
|
||||
{% if realtime and model.contest.isLocked(tdoc) and model.contest.isDone(tdoc) and handler.user.own(tdoc) %}
|
||||
<div class="main">
|
||||
<div class="row typo">
|
||||
<blockquote class="note">
|
||||
{{ _('You are viewing the realtime scoreboard while the scoreboard is currently frozen.') }} <br>
|
||||
{{ _('To allow contestants to view the scoreboard, you can unfreeze the scoreboard.') }} <br>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="operation" value="unlock">
|
||||
<button type="submit" class="button">
|
||||
<span class="icon icon-feeling-lucky"></span> {{ _('Unlock scoreboard') }}
|
||||
</button>
|
||||
</form>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div id="app"></div>
|
||||
<noscript>This website requires JavaScript to function properly. Please enable JavaScript tocontinue.</noscript>
|
||||
{% endblock %}
|
||||
Reference in new issue
Block a user