mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 17:36:08 +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",
|
||||
"version": "3.17.5",
|
||||
"version": "3.17.6",
|
||||
"bin": "bin/hydrooj.js",
|
||||
"main": "src/loader",
|
||||
"module": "src/loader",
|
||||
|
||||
@@ -119,7 +119,7 @@ export class ContestDetailHandler extends ContestDetailBaseHandler {
|
||||
this.response.template = 'contest_detail.html';
|
||||
const udict = await user.getList(domainId, [this.tdoc.owner]);
|
||||
this.response.body = {
|
||||
tdoc: this.tdoc, tsdoc: this.tsdoc, udict,
|
||||
tdoc: this.tdoc, tsdoc: pick(this.tsdoc, ['attend', 'startAt']), udict,
|
||||
};
|
||||
if (!this.request.json) {
|
||||
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 { isBinaryFile } from 'isbinaryfile';
|
||||
import {
|
||||
escapeRegExp, flattenDeep, intersection, isSafeInteger,
|
||||
escapeRegExp, flattenDeep, intersection, isSafeInteger, pick,
|
||||
} from 'lodash';
|
||||
import { FilterQuery, ObjectID } from 'mongodb';
|
||||
import { nanoid } from 'nanoid';
|
||||
@@ -372,12 +372,12 @@ export class ProblemDetailHandler extends ContestDetailBaseHandler {
|
||||
this.response.body = {
|
||||
pdoc: this.pdoc,
|
||||
udoc: this.udoc,
|
||||
psdoc: this.psdoc,
|
||||
psdoc: tid ? null : this.psdoc,
|
||||
title: this.pdoc.title,
|
||||
solutionCount: scnt,
|
||||
discussionCount: dcnt,
|
||||
tdoc: this.tdoc,
|
||||
tsdoc: this.tsdoc,
|
||||
tsdoc: pick(this.tsdoc, ['attend', 'startAt']),
|
||||
};
|
||||
this.response.template = 'problem_detail.html';
|
||||
this.UiContext.extraTitleContent = this.pdoc.title;
|
||||
@@ -429,8 +429,10 @@ export class ProblemDetailHandler extends ContestDetailBaseHandler {
|
||||
if (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.htdocs = await contest.getRelated(this.args.domainId, this.pdoc.docId, 'homework');
|
||||
[this.response.body.ctdocs, this.response.body.htdocs] = await Promise.all([
|
||||
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;
|
||||
hidden?: boolean;
|
||||
check: (args: any) => any;
|
||||
statusSort: any;
|
||||
statusSort: Record<string, 1 | -1>;
|
||||
submitAfterAccept: boolean;
|
||||
showScoreboard: (tdoc: Tdoc<30>, now: Date) => boolean;
|
||||
showSelfRecord: (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;
|
||||
scoreboardHeader: (
|
||||
this: ContestRule<T>, isExport: boolean, _: (s: string) => string,
|
||||
tdoc: Tdoc<30>, pdict: ProblemDict,
|
||||
) => Promise<ScoreboardRow>;
|
||||
scoreboard: (
|
||||
this: ContestRule<T>, isExport: boolean, _: (s: string) => string,
|
||||
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 { Counter, formatSeconds, Time } from '@hydrooj/utils/lib/utils';
|
||||
import {
|
||||
@@ -79,10 +80,7 @@ const acm = buildContestRule({
|
||||
}
|
||||
return { accept, time, detail };
|
||||
},
|
||||
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);
|
||||
async scoreboardHeader(isExport, _, tdoc, pdict) {
|
||||
const columns: ScoreboardRow = [
|
||||
{ type: 'rank', value: '#' },
|
||||
{ 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
|
||||
const first = {};
|
||||
const data = await document.collStatus.aggregate([
|
||||
@@ -130,6 +133,7 @@ const acm = buildContestRule({
|
||||
]).toArray();
|
||||
for (const t of data) first[t._id] = t.first.generationTime;
|
||||
|
||||
const columns = await this.scoreboardHeader(isExport, _, tdoc, pdict);
|
||||
const rows: ScoreboardRow[] = [columns];
|
||||
for (const [rank, tsdoc] of rankedTsdocs) {
|
||||
const tsddict: Record<number, AcmDetail> = {};
|
||||
@@ -197,10 +201,7 @@ const oi = buildContestRule({
|
||||
showScoreboard: (tdoc, now) => now > tdoc.endAt,
|
||||
showSelfRecord: (tdoc, now) => now > tdoc.endAt,
|
||||
showRecord: (tdoc, now) => now > tdoc.endAt,
|
||||
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);
|
||||
async scoreboardHeader(isExport, _, tdoc, pdict) {
|
||||
const columns: ScoreboardNode[] = [
|
||||
{ type: 'rank', value: '#' },
|
||||
{ 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 first = {};
|
||||
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];
|
||||
for (const [rank, tsdoc] of rankedTsdocs) {
|
||||
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;
|
||||
}
|
||||
const row: ScoreboardNode[] = [
|
||||
{ type: 'string', value: rank.toString() },
|
||||
{ type: 'rank', value: rank.toString() },
|
||||
{ type: 'user', value: udict[tsdoc.uid].uname, raw: tsdoc.uid },
|
||||
];
|
||||
if (isExport) row.push({ type: 'email', value: udict[tsdoc.uid].mail });
|
||||
@@ -337,8 +345,8 @@ const homework = buildContestRule({
|
||||
});
|
||||
}
|
||||
return {
|
||||
score: Math.sum(detail.map((d) => d.score)),
|
||||
penaltyScore: Math.sum(detail.map((d) => d.penaltyScore)),
|
||||
score: sumBy(detail, 'score'),
|
||||
penaltyScore: sumBy(detail, 'penaltyScore'),
|
||||
time: Math.sum(detail.map((d) => d.time)),
|
||||
detail,
|
||||
};
|
||||
@@ -346,10 +354,7 @@ const homework = buildContestRule({
|
||||
showScoreboard: () => true,
|
||||
showSelfRecord: () => true,
|
||||
showRecord: () => true,
|
||||
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);
|
||||
async scoreboardHeader(isExport, _, tdoc, pdict) {
|
||||
const columns: ScoreboardNode[] = [
|
||||
{ type: 'rank', value: _('Rank') },
|
||||
{ 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];
|
||||
for (const [rank, tsdoc] of rankedTsdocs) {
|
||||
const tsddict = {};
|
||||
@@ -391,7 +403,7 @@ const homework = buildContestRule({
|
||||
if (!tsddict[item.pid] || tsddict[item.pid].score <= item.score) tsddict[item.pid] = item;
|
||||
}
|
||||
const row: ScoreboardRow = [
|
||||
{ type: 'string', value: rank },
|
||||
{ type: 'rank', value: rank.toString() },
|
||||
{
|
||||
type: 'user',
|
||||
value: udict[tsdoc.uid].uname,
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
import { FilterQuery } from 'mongodb';
|
||||
import type {
|
||||
DomainDoc, ProblemStatusDoc, RecordDoc,
|
||||
Udoc,
|
||||
Tdoc, Udoc,
|
||||
} from './interface';
|
||||
import * as contest from './model/contest';
|
||||
import * as document from './model/document';
|
||||
import domain from './model/domain';
|
||||
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);
|
||||
}
|
||||
|
||||
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>) {
|
||||
await iterateAllDomain(async ({ _id: domainId }) => {
|
||||
const cursor = document.getMultiStatus(domainId, document.TYPE_PROBLEM, filter);
|
||||
|
||||
@@ -831,6 +831,7 @@ const scripts: UpgradeScript[] = [
|
||||
return true;
|
||||
},
|
||||
async function _67_78() {
|
||||
const _FRESH_INSTALL_IGNORE = 1;
|
||||
const rdocs = RecordModel.coll.find({ code: /^@@hydro_submission_file@@/ });
|
||||
let rdoc;
|
||||
while (rdoc = await rdocs.next()) { // eslint-disable-line
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hydrooj/ui-default",
|
||||
"version": "4.40.5",
|
||||
"version": "4.40.6",
|
||||
"author": "undefined <i@undefined.moe>",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "hydro.js",
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<tr>
|
||||
{%- for column in rows[0] -%}
|
||||
<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 }}">
|
||||
{{ column.value }}<br />{{ pdict[column.raw].nAccept }}/{{ pdict[column.raw].nSubmit }}
|
||||
</a>
|
||||
|
||||
Reference in new issue
Block a user