core&ui: contest: tsdoc projection for contest

This commit is contained in:
undefined committed 2022-09-12 18:41:06 +08:00
1 parent 8f7d81ddca
commit dbe369d0c6
9 files changed
+57 -28

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "hydrooj",
"version": "3.17.5",
"version": "3.17.6",
"bin": "bin/hydrooj.js",
"main": "src/loader",
"module": "src/loader",
+1 -1
View File
@@ -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
+7 -5
View File
@@ -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'),
]);
}
}
+5 -1
View File
@@ -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,
+29 -17
View File
@@ -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,
+11 -1
View File
@@ -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);
+1
View File
@@ -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 -1
View File
@@ -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>