mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
core&ui: contest: tsdoc projection for contest
This commit is contained in:
1 parent
8f7d81ddca
commit
dbe369d0c6
9 files changed
+57
-28
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hydrooj",
|
"name": "hydrooj",
|
||||||
"version": "3.17.5",
|
"version": "3.17.6",
|
||||||
"bin": "bin/hydrooj.js",
|
"bin": "bin/hydrooj.js",
|
||||||
"main": "src/loader",
|
"main": "src/loader",
|
||||||
"module": "src/loader",
|
"module": "src/loader",
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ export class ContestDetailHandler extends ContestDetailBaseHandler {
|
|||||||
this.response.template = 'contest_detail.html';
|
this.response.template = 'contest_detail.html';
|
||||||
const udict = await user.getList(domainId, [this.tdoc.owner]);
|
const udict = await user.getList(domainId, [this.tdoc.owner]);
|
||||||
this.response.body = {
|
this.response.body = {
|
||||||
tdoc: this.tdoc, tsdoc: this.tsdoc, udict,
|
tdoc: this.tdoc, tsdoc: pick(this.tsdoc, ['attend', 'startAt']), udict,
|
||||||
};
|
};
|
||||||
if (!this.request.json) {
|
if (!this.request.json) {
|
||||||
this.response.body.tdoc.content = this.response.body.tdoc.content
|
this.response.body.tdoc.content = this.response.body.tdoc.content
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import AdmZip from 'adm-zip';
|
|||||||
import { readFile, statSync } from 'fs-extra';
|
import { readFile, statSync } from 'fs-extra';
|
||||||
import { isBinaryFile } from 'isbinaryfile';
|
import { isBinaryFile } from 'isbinaryfile';
|
||||||
import {
|
import {
|
||||||
escapeRegExp, flattenDeep, intersection, isSafeInteger,
|
escapeRegExp, flattenDeep, intersection, isSafeInteger, pick,
|
||||||
} from 'lodash';
|
} from 'lodash';
|
||||||
import { FilterQuery, ObjectID } from 'mongodb';
|
import { FilterQuery, ObjectID } from 'mongodb';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
@@ -372,12 +372,12 @@ export class ProblemDetailHandler extends ContestDetailBaseHandler {
|
|||||||
this.response.body = {
|
this.response.body = {
|
||||||
pdoc: this.pdoc,
|
pdoc: this.pdoc,
|
||||||
udoc: this.udoc,
|
udoc: this.udoc,
|
||||||
psdoc: this.psdoc,
|
psdoc: tid ? null : this.psdoc,
|
||||||
title: this.pdoc.title,
|
title: this.pdoc.title,
|
||||||
solutionCount: scnt,
|
solutionCount: scnt,
|
||||||
discussionCount: dcnt,
|
discussionCount: dcnt,
|
||||||
tdoc: this.tdoc,
|
tdoc: this.tdoc,
|
||||||
tsdoc: this.tsdoc,
|
tsdoc: pick(this.tsdoc, ['attend', 'startAt']),
|
||||||
};
|
};
|
||||||
this.response.template = 'problem_detail.html';
|
this.response.template = 'problem_detail.html';
|
||||||
this.UiContext.extraTitleContent = this.pdoc.title;
|
this.UiContext.extraTitleContent = this.pdoc.title;
|
||||||
@@ -429,8 +429,10 @@ export class ProblemDetailHandler extends ContestDetailBaseHandler {
|
|||||||
if (this.psdoc?.rid) {
|
if (this.psdoc?.rid) {
|
||||||
this.response.body.rdoc = await record.get(this.args.domainId, this.psdoc.rid);
|
this.response.body.rdoc = await record.get(this.args.domainId, this.psdoc.rid);
|
||||||
}
|
}
|
||||||
this.response.body.ctdocs = await contest.getRelated(this.args.domainId, this.pdoc.docId);
|
[this.response.body.ctdocs, this.response.body.htdocs] = await Promise.all([
|
||||||
this.response.body.htdocs = await contest.getRelated(this.args.domainId, this.pdoc.docId, 'homework');
|
contest.getRelated(this.args.domainId, this.pdoc.docId),
|
||||||
|
contest.getRelated(this.args.domainId, this.pdoc.docId, 'homework'),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -495,12 +495,16 @@ export interface ContestRule<T = any> {
|
|||||||
TEXT: string;
|
TEXT: string;
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
check: (args: any) => any;
|
check: (args: any) => any;
|
||||||
statusSort: any;
|
statusSort: Record<string, 1 | -1>;
|
||||||
submitAfterAccept: boolean;
|
submitAfterAccept: boolean;
|
||||||
showScoreboard: (tdoc: Tdoc<30>, now: Date) => boolean;
|
showScoreboard: (tdoc: Tdoc<30>, now: Date) => boolean;
|
||||||
showSelfRecord: (tdoc: Tdoc<30>, now: Date) => boolean;
|
showSelfRecord: (tdoc: Tdoc<30>, now: Date) => boolean;
|
||||||
showRecord: (tdoc: Tdoc<30>, now: Date) => boolean;
|
showRecord: (tdoc: Tdoc<30>, now: Date) => boolean;
|
||||||
stat: (this: ContestRule<T>, tdoc: Tdoc<30>, journal: any[], ignoreLock?: boolean) => ContestStat & T;
|
stat: (this: ContestRule<T>, tdoc: Tdoc<30>, journal: any[], ignoreLock?: boolean) => ContestStat & T;
|
||||||
|
scoreboardHeader: (
|
||||||
|
this: ContestRule<T>, isExport: boolean, _: (s: string) => string,
|
||||||
|
tdoc: Tdoc<30>, pdict: ProblemDict,
|
||||||
|
) => Promise<ScoreboardRow>;
|
||||||
scoreboard: (
|
scoreboard: (
|
||||||
this: ContestRule<T>, isExport: boolean, _: (s: string) => string,
|
this: ContestRule<T>, isExport: boolean, _: (s: string) => string,
|
||||||
tdoc: Tdoc<30>, pdict: ProblemDict, cursor: Cursor<ContestStat & T>, page: number,
|
tdoc: Tdoc<30>, pdict: ProblemDict, cursor: Cursor<ContestStat & T>, page: number,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { sumBy } from 'lodash';
|
||||||
import { FilterQuery, ObjectID } from 'mongodb';
|
import { FilterQuery, ObjectID } from 'mongodb';
|
||||||
import { Counter, formatSeconds, Time } from '@hydrooj/utils/lib/utils';
|
import { Counter, formatSeconds, Time } from '@hydrooj/utils/lib/utils';
|
||||||
import {
|
import {
|
||||||
@@ -79,10 +80,7 @@ const acm = buildContestRule({
|
|||||||
}
|
}
|
||||||
return { accept, time, detail };
|
return { accept, time, detail };
|
||||||
},
|
},
|
||||||
async scoreboard(isExport, _, tdoc, pdict, cursor, page) {
|
async scoreboardHeader(isExport, _, tdoc, pdict) {
|
||||||
const [rankedTsdocs, nPages] = await ranked(cursor, (a, b) => a.score === b.score && a.time === b.time, page);
|
|
||||||
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
|
|
||||||
const udict = await user.getList(tdoc.domainId, uids);
|
|
||||||
const columns: ScoreboardRow = [
|
const columns: ScoreboardRow = [
|
||||||
{ type: 'rank', value: '#' },
|
{ type: 'rank', value: '#' },
|
||||||
{ type: 'user', value: _('User') },
|
{ type: 'user', value: _('User') },
|
||||||
@@ -110,7 +108,12 @@ const acm = buildContestRule({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return columns;
|
||||||
|
},
|
||||||
|
async scoreboard(isExport, _, tdoc, pdict, cursor, page) {
|
||||||
|
const [rankedTsdocs, nPages] = await ranked(cursor, (a, b) => a.score === b.score && a.time === b.time, page);
|
||||||
|
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
|
||||||
|
const udict = await user.getList(tdoc.domainId, uids);
|
||||||
// Find first accept
|
// Find first accept
|
||||||
const first = {};
|
const first = {};
|
||||||
const data = await document.collStatus.aggregate([
|
const data = await document.collStatus.aggregate([
|
||||||
@@ -130,6 +133,7 @@ const acm = buildContestRule({
|
|||||||
]).toArray();
|
]).toArray();
|
||||||
for (const t of data) first[t._id] = t.first.generationTime;
|
for (const t of data) first[t._id] = t.first.generationTime;
|
||||||
|
|
||||||
|
const columns = await this.scoreboardHeader(isExport, _, tdoc, pdict);
|
||||||
const rows: ScoreboardRow[] = [columns];
|
const rows: ScoreboardRow[] = [columns];
|
||||||
for (const [rank, tsdoc] of rankedTsdocs) {
|
for (const [rank, tsdoc] of rankedTsdocs) {
|
||||||
const tsddict: Record<number, AcmDetail> = {};
|
const tsddict: Record<number, AcmDetail> = {};
|
||||||
@@ -197,10 +201,7 @@ const oi = buildContestRule({
|
|||||||
showScoreboard: (tdoc, now) => now > tdoc.endAt,
|
showScoreboard: (tdoc, now) => now > tdoc.endAt,
|
||||||
showSelfRecord: (tdoc, now) => now > tdoc.endAt,
|
showSelfRecord: (tdoc, now) => now > tdoc.endAt,
|
||||||
showRecord: (tdoc, now) => now > tdoc.endAt,
|
showRecord: (tdoc, now) => now > tdoc.endAt,
|
||||||
async scoreboard(isExport, _, tdoc, pdict, cursor, page) {
|
async scoreboardHeader(isExport, _, tdoc, pdict) {
|
||||||
const [rankedTsdocs, nPages] = await ranked(cursor, (a, b) => a.score === b.score, page);
|
|
||||||
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
|
|
||||||
const udict = await user.getList(tdoc.domainId, uids);
|
|
||||||
const columns: ScoreboardNode[] = [
|
const columns: ScoreboardNode[] = [
|
||||||
{ type: 'rank', value: '#' },
|
{ type: 'rank', value: '#' },
|
||||||
{ type: 'user', value: _('User') },
|
{ type: 'user', value: _('User') },
|
||||||
@@ -221,6 +222,12 @@ const oi = buildContestRule({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return columns;
|
||||||
|
},
|
||||||
|
async scoreboard(isExport, _, tdoc, pdict, cursor, page) {
|
||||||
|
const [rankedTsdocs, nPages] = await ranked(cursor, (a, b) => a.score === b.score, page);
|
||||||
|
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
|
||||||
|
const udict = await user.getList(tdoc.domainId, uids);
|
||||||
const psdict = {};
|
const psdict = {};
|
||||||
const first = {};
|
const first = {};
|
||||||
for (const pid of tdoc.pids) {
|
for (const pid of tdoc.pids) {
|
||||||
@@ -243,6 +250,7 @@ const oi = buildContestRule({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const columns = await this.scoreboardHeader(isExport, _, tdoc, pdict);
|
||||||
const rows: ScoreboardNode[][] = [columns];
|
const rows: ScoreboardNode[][] = [columns];
|
||||||
for (const [rank, tsdoc] of rankedTsdocs) {
|
for (const [rank, tsdoc] of rankedTsdocs) {
|
||||||
const tsddict = {};
|
const tsddict = {};
|
||||||
@@ -250,7 +258,7 @@ const oi = buildContestRule({
|
|||||||
if (!tsddict[item.pid] || tsddict[item.pid].score < item.score || this.submitAfterAccept) tsddict[item.pid] = item;
|
if (!tsddict[item.pid] || tsddict[item.pid].score < item.score || this.submitAfterAccept) tsddict[item.pid] = item;
|
||||||
}
|
}
|
||||||
const row: ScoreboardNode[] = [
|
const row: ScoreboardNode[] = [
|
||||||
{ type: 'string', value: rank.toString() },
|
{ type: 'rank', value: rank.toString() },
|
||||||
{ type: 'user', value: udict[tsdoc.uid].uname, raw: tsdoc.uid },
|
{ type: 'user', value: udict[tsdoc.uid].uname, raw: tsdoc.uid },
|
||||||
];
|
];
|
||||||
if (isExport) row.push({ type: 'email', value: udict[tsdoc.uid].mail });
|
if (isExport) row.push({ type: 'email', value: udict[tsdoc.uid].mail });
|
||||||
@@ -337,8 +345,8 @@ const homework = buildContestRule({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
score: Math.sum(detail.map((d) => d.score)),
|
score: sumBy(detail, 'score'),
|
||||||
penaltyScore: Math.sum(detail.map((d) => d.penaltyScore)),
|
penaltyScore: sumBy(detail, 'penaltyScore'),
|
||||||
time: Math.sum(detail.map((d) => d.time)),
|
time: Math.sum(detail.map((d) => d.time)),
|
||||||
detail,
|
detail,
|
||||||
};
|
};
|
||||||
@@ -346,10 +354,7 @@ const homework = buildContestRule({
|
|||||||
showScoreboard: () => true,
|
showScoreboard: () => true,
|
||||||
showSelfRecord: () => true,
|
showSelfRecord: () => true,
|
||||||
showRecord: () => true,
|
showRecord: () => true,
|
||||||
async scoreboard(isExport, _, tdoc, pdict, cursor, page) {
|
async scoreboardHeader(isExport, _, tdoc, pdict) {
|
||||||
const [rankedTsdocs, nPages] = await ranked(cursor, (a, b) => a.score === b.score, page);
|
|
||||||
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
|
|
||||||
const udict = await user.getList(tdoc.domainId, uids);
|
|
||||||
const columns: ScoreboardNode[] = [
|
const columns: ScoreboardNode[] = [
|
||||||
{ type: 'rank', value: _('Rank') },
|
{ type: 'rank', value: _('Rank') },
|
||||||
{ type: 'user', value: _('User') },
|
{ type: 'user', value: _('User') },
|
||||||
@@ -384,6 +389,13 @@ const homework = buildContestRule({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return columns;
|
||||||
|
},
|
||||||
|
async scoreboard(isExport, _, tdoc, pdict, cursor, page) {
|
||||||
|
const [rankedTsdocs, nPages] = await ranked(cursor, (a, b) => a.score === b.score, page);
|
||||||
|
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
|
||||||
|
const udict = await user.getList(tdoc.domainId, uids);
|
||||||
|
const columns = await this.scoreboardHeader(isExport, _, tdoc, pdict);
|
||||||
const rows: ScoreboardRow[] = [columns];
|
const rows: ScoreboardRow[] = [columns];
|
||||||
for (const [rank, tsdoc] of rankedTsdocs) {
|
for (const [rank, tsdoc] of rankedTsdocs) {
|
||||||
const tsddict = {};
|
const tsddict = {};
|
||||||
@@ -391,7 +403,7 @@ const homework = buildContestRule({
|
|||||||
if (!tsddict[item.pid] || tsddict[item.pid].score <= item.score) tsddict[item.pid] = item;
|
if (!tsddict[item.pid] || tsddict[item.pid].score <= item.score) tsddict[item.pid] = item;
|
||||||
}
|
}
|
||||||
const row: ScoreboardRow = [
|
const row: ScoreboardRow = [
|
||||||
{ type: 'string', value: rank },
|
{ type: 'rank', value: rank.toString() },
|
||||||
{
|
{
|
||||||
type: 'user',
|
type: 'user',
|
||||||
value: udict[tsdoc.uid].uname,
|
value: udict[tsdoc.uid].uname,
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
import { FilterQuery } from 'mongodb';
|
import { FilterQuery } from 'mongodb';
|
||||||
import type {
|
import type {
|
||||||
DomainDoc, ProblemStatusDoc, RecordDoc,
|
DomainDoc, ProblemStatusDoc, RecordDoc,
|
||||||
Udoc,
|
Tdoc, Udoc,
|
||||||
} from './interface';
|
} from './interface';
|
||||||
|
import * as contest from './model/contest';
|
||||||
import * as document from './model/document';
|
import * as document from './model/document';
|
||||||
import domain from './model/domain';
|
import domain from './model/domain';
|
||||||
import problem, { Field, ProblemDoc } from './model/problem';
|
import problem, { Field, ProblemDoc } from './model/problem';
|
||||||
@@ -20,6 +21,15 @@ export async function iterateAllUser(cb: (udoc: Udoc, current?: number, total?:
|
|||||||
for (const i in udocs) await cb(udocs[i], +i, udocs.length);
|
for (const i in udocs) await cb(udocs[i], +i, udocs.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function iterateAllContest(cb: (tdoc: Tdoc) => Promise<any>) {
|
||||||
|
await iterateAllDomain(async (ddoc) => {
|
||||||
|
const tdocs = await contest.getMulti(ddoc._id, {}).toArray();
|
||||||
|
for (const tdoc of tdocs) {
|
||||||
|
await cb(tdoc);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export async function iterateAllPsdoc(filter: FilterQuery<ProblemStatusDoc>, cb: (psdoc: ProblemStatusDoc) => Promise<any>) {
|
export async function iterateAllPsdoc(filter: FilterQuery<ProblemStatusDoc>, cb: (psdoc: ProblemStatusDoc) => Promise<any>) {
|
||||||
await iterateAllDomain(async ({ _id: domainId }) => {
|
await iterateAllDomain(async ({ _id: domainId }) => {
|
||||||
const cursor = document.getMultiStatus(domainId, document.TYPE_PROBLEM, filter);
|
const cursor = document.getMultiStatus(domainId, document.TYPE_PROBLEM, filter);
|
||||||
|
|||||||
@@ -831,6 +831,7 @@ const scripts: UpgradeScript[] = [
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
async function _67_78() {
|
async function _67_78() {
|
||||||
|
const _FRESH_INSTALL_IGNORE = 1;
|
||||||
const rdocs = RecordModel.coll.find({ code: /^@@hydro_submission_file@@/ });
|
const rdocs = RecordModel.coll.find({ code: /^@@hydro_submission_file@@/ });
|
||||||
let rdoc;
|
let rdoc;
|
||||||
while (rdoc = await rdocs.next()) { // eslint-disable-line
|
while (rdoc = await rdocs.next()) { // eslint-disable-line
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@hydrooj/ui-default",
|
"name": "@hydrooj/ui-default",
|
||||||
"version": "4.40.5",
|
"version": "4.40.6",
|
||||||
"author": "undefined <i@undefined.moe>",
|
"author": "undefined <i@undefined.moe>",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "hydro.js",
|
"main": "hydro.js",
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
{%- for column in rows[0] -%}
|
{%- for column in rows[0] -%}
|
||||||
<th class="col--{{ column.type }}">
|
<th class="col--{{ column.type }}">
|
||||||
{% if column.type == 'problem' %}
|
{% if column.type == 'problem' and column.raw %}
|
||||||
<a href="{{ url('problem_detail', pid=column.raw, query={tid:tdoc.docId}) }}" data-tooltip="{{ pdict[column.raw].title }}">
|
<a href="{{ url('problem_detail', pid=column.raw, query={tid:tdoc.docId}) }}" data-tooltip="{{ pdict[column.raw].title }}">
|
||||||
{{ column.value }}<br />{{ pdict[column.raw].nAccept }}/{{ pdict[column.raw].nSubmit }}
|
{{ column.value }}<br />{{ pdict[column.raw].nAccept }}/{{ pdict[column.raw].nSubmit }}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
Reference in new issue
Block a user