core: contest.getRawStatus API

This commit is contained in:
undefined committed 2020-12-09 23:38:29 +08:00
1 parent 2cc9c6a06a
commit 68286607e9
5 files changed
+46 -27

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "hydrooj",
"version": "2.14.4",
"version": "2.14.5",
"bin": "bin/hydrooj.js",
"main": "dist/loader.js",
"typings": "dist/loader.d.ts",
+8
View File
@@ -143,6 +143,13 @@ class ContestScoreboardHandler extends ContestHandler {
}
}
class ContestScoreboardRawHandler extends ContestHandler {
@param('tid', Types.ObjectID)
async get(domainId: string, tid: ObjectID) {
this.response.body = await this.getRawStatus(domainId, tid);
}
}
class ContestScoreboardDownloadHandler extends ContestHandler {
@param('tid', Types.ObjectID)
@param('ext', Types.String)
@@ -414,6 +421,7 @@ export async function apply() {
Route('contest_boardcast', '/contest/:tid/boardcast', ContestBoardcastHandler);
Route('contest_edit', '/contest/:tid/edit', ContestEditHandler, PERM.PERM_VIEW_CONTEST);
Route('contest_scoreboard', '/contest/:tid/scoreboard', ContestScoreboardHandler, PERM.PERM_VIEW_CONTEST);
Route('contest_scoreboard_raw', '/contest/:tid/scoreboard/raw', ContestScoreboardRawHandler, PERM.PERM_VIEW_CONTEST);
Route('contest_scoreboard_download', '/contest/:tid/export/:ext', ContestScoreboardDownloadHandler, PERM.PERM_VIEW_CONTEST);
Route('contest_detail_problem', '/contest/:tid/p/:pid', ContestProblemHandler, PERM.PERM_VIEW_CONTEST);
Route('contest_detail_problem_submit', '/contest/:tid/p/:pid/submit', ContestProblemSubmitHandler, PERM.PERM_VIEW_CONTEST);
+4 -3
View File
@@ -282,6 +282,7 @@ export interface ScoreboardNode {
value: string,
raw?: any,
}
export type ScoreboardRow = ScoreboardNode[] & { raw?: any };
export type PenaltyRules = Dictionary<number>;
@@ -423,9 +424,9 @@ export interface ContestRule {
stat: (tdoc: Tdoc, journal: any[]) => ContestStat,
scoreboard: (
isExport: boolean, _: (s: string) => string,
tdoc: Tdoc, rankedTsdocs: any[] | Generator<any>, udict: Udict, pdict: Pdict
) => ScoreboardNode[][],
rank: (tsdocs: any[]) => any[] | Generator<any>,
tdoc: Tdoc, rankedTsdocs: any[], udict: Udict, pdict: Pdict
) => ScoreboardRow[],
rank: (tsdocs: any[]) => any[],
}
export type ContestRules = Dictionary<ContestRule>;
+4 -2
View File
@@ -1,13 +1,15 @@
function* ranked<T>(diter: T[], equ = (a: T, b: T) => a === b): Generator<[number, T], void> {
function ranked<T>(diter: T[], equ = (a: T, b: T) => a === b): [number, T][] {
let last = null;
let r = 0;
let count = 0;
const results: [number, T][] = [];
for (const doc of diter) {
count++;
if (count === 1 || !equ(last, doc)) r = count;
last = doc;
yield [r, doc];
results.push([r, doc]);
}
return results;
}
global.Hydro.lib.rank = ranked;
+29 -21
View File
@@ -1,4 +1,5 @@
import { ObjectID, FilterQuery } from 'mongodb';
import { filter, sortBy } from 'lodash';
import * as user from './user';
import * as problem from './problem';
import * as document from './document';
@@ -8,7 +9,7 @@ import {
ContestNotAttendedError, ContestScoreboardHiddenError,
} from '../error';
import {
ContestRule, ContestRules, ScoreboardNode, Tdoc, Udict,
ContestRule, ContestRules, ScoreboardNode, ScoreboardRow, Tdoc, Udict,
} from '../interface';
import * as misc from '../lib/misc';
import ranked from '../lib/rank';
@@ -34,25 +35,24 @@ const acm: ContestRule = {
if (!j.accept) naccept[j.pid] = (naccept[j.pid] || 0) + 1;
}
}
function _time(jdoc) {
const real = jdoc.rid.generationTime - Math.floor(tdoc.beginAt.getTime() / 1000);
const penalty = 20 * 60 * naccept[jdoc.pid];
return real + penalty;
}
for (const key in effective) {
const j = effective[key];
const real = j.rid.generationTime - Math.floor(tdoc.beginAt.getTime() / 1000);
const penalty = 20 * 60 * naccept[j.pid];
detail.push({
...j, naccept: naccept[j.pid], time: _time(j),
...j, naccept: naccept[j.pid], time: real + penalty, real, penalty,
});
}
for (const d of detail) {
accept += d.accept;
if (d.accept) time += d.time;
if (d.accept) {
accept += d.accept;
time += d.time;
}
}
return { accept, time, detail };
},
scoreboard(isExport, _, tdoc, rankedTsdocs, udict, pdict) {
const columns: ScoreboardNode[] = [
const columns: ScoreboardRow = [
{ type: 'rank', value: _('Rank') },
{ type: 'user', value: _('User') },
{ type: 'solved_problems', value: _('Solved Problems') },
@@ -88,13 +88,11 @@ const acm: ContestRule = {
});
}
}
const rows = [columns];
const rows: ScoreboardRow[] = [columns];
for (const [rank, tsdoc] of rankedTsdocs) {
const tsddict = {};
if (tsdoc.detail) {
for (const item of tsdoc.detail) tsddict[item.pid] = item;
}
const row = [];
for (const item of tsdoc.detail || []) tsddict[item.pid] = item;
const row: ScoreboardRow = [];
row.push(
{ type: 'string', value: rank },
{ type: 'user', value: udict[tsdoc.uid].uname, raw: tsdoc.uid },
@@ -134,6 +132,7 @@ const acm: ContestRule = {
});
}
}
row.raw = tsdoc;
rows.push(row);
}
return rows;
@@ -233,7 +232,7 @@ const homework: ContestRule = {
if (exceedSeconds < 0) return score;
let coefficient = 1;
const keys = Object.keys(tdoc.penaltyRules)
.map((k) => parseFloat(k)).sort((a, b) => a - b);
.map(parseFloat).sort((a, b) => a - b);
for (const i of keys) {
if (i * 3600 <= exceedSeconds) coefficient = tdoc.penaltyRules[i];
else break;
@@ -498,6 +497,17 @@ export const ContestHandlerMixin = (c) => class extends c {
domainId: string, tid: ObjectID,
isExport = false, docType: 30 | 60 = document.TYPE_CONTEST,
): Promise<[Tdoc, ScoreboardNode[][], Udict]> {
const {
tdoc, udict, pdict, rankedTsdocs,
} = await this.getRawStatus(domainId, tid, docType);
const rows = RULES[tdoc.rule].scoreboard(
isExport, this.translate.bind(this),
tdoc, rankedTsdocs, udict, pdict,
);
return [tdoc, rows, udict];
}
async getRawStatus(domainId: string, tid: ObjectID, docType: 30 | 60 = document.TYPE_CONTEST) {
const tdoc = await get(domainId, tid, docType);
if (!this.canShowScoreboard(tdoc)) throw new ContestScoreboardHiddenError(tid);
const tsdocs = await getMultiStatus(domainId, { docId: tid }, docType).sort(RULES[tdoc.rule].statusSort).toArray();
@@ -507,11 +517,9 @@ export const ContestHandlerMixin = (c) => class extends c {
problem.getList(domainId, tdoc.pids, true),
]);
const rankedTsdocs = RULES[tdoc.rule].rank(tsdocs);
const rows = RULES[tdoc.rule].scoreboard(
isExport, this.translate.bind(this),
tdoc, rankedTsdocs, udict, pdict,
);
return [tdoc, rows, udict];
return {
tdoc, tsdocs, udict, pdict, rankedTsdocs,
};
}
// eslint-disable-next-line class-methods-use-this