mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
core: contest_scoreboard: disable pagination
This commit is contained in:
1 parent
757b5e6bc1
commit
9461499d7c
6 files changed
+32
-35
No files matched your search
@@ -193,10 +193,9 @@ export class ContestBroadcastHandler extends ContestDetailBaseHandler {
|
|||||||
|
|
||||||
export class ContestScoreboardHandler extends ContestDetailBaseHandler {
|
export class ContestScoreboardHandler extends ContestDetailBaseHandler {
|
||||||
@param('tid', Types.ObjectID)
|
@param('tid', Types.ObjectID)
|
||||||
@param('page', Types.PositiveInt, true)
|
|
||||||
@param('ext', Types.Range(['csv', 'html']), true)
|
@param('ext', Types.Range(['csv', 'html']), true)
|
||||||
@param('ignoreLock', Types.Boolean, true)
|
@param('ignoreLock', Types.Boolean, true)
|
||||||
async get(domainId: string, tid: ObjectID, page = 1, ext = '', ignoreLock = false) {
|
async get(domainId: string, tid: ObjectID, ext = '', ignoreLock = false) {
|
||||||
if (ignoreLock && !this.user.own(this.tdoc)) {
|
if (ignoreLock && !this.user.own(this.tdoc)) {
|
||||||
this.checkPerm(this.tdoc.rule === 'homework'
|
this.checkPerm(this.tdoc.rule === 'homework'
|
||||||
? PERM.PERM_VIEW_HOMEWORK_HIDDEN_SCOREBOARD
|
? PERM.PERM_VIEW_HOMEWORK_HIDDEN_SCOREBOARD
|
||||||
@@ -210,14 +209,14 @@ export class ContestScoreboardHandler extends ContestDetailBaseHandler {
|
|||||||
// Problem statistics display is allowed as we can view submission info in scoreboard.
|
// Problem statistics display is allowed as we can view submission info in scoreboard.
|
||||||
...problem.PROJECTION_CONTEST_LIST, 'nSubmit', 'nAccept',
|
...problem.PROJECTION_CONTEST_LIST, 'nSubmit', 'nAccept',
|
||||||
]);
|
]);
|
||||||
const [, rows, udict, , nPages] = await contest.getScoreboard.call(this, domainId, tid, false, page, ignoreLock);
|
const [, rows, udict] = await contest.getScoreboard.call(this, domainId, tid, false, ignoreLock);
|
||||||
this.response.template = 'contest_scoreboard.html';
|
this.response.template = 'contest_scoreboard.html';
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
const page_name = this.tdoc.rule === 'homework'
|
const page_name = this.tdoc.rule === 'homework'
|
||||||
? 'homework_scoreboard'
|
? 'homework_scoreboard'
|
||||||
: 'contest_scoreboard';
|
: 'contest_scoreboard';
|
||||||
this.response.body = {
|
this.response.body = {
|
||||||
tdoc: this.tdoc, rows, udict, nPages, page, pdict, page_name,
|
tdoc: this.tdoc, rows, udict, pdict, page_name,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +226,7 @@ export class ContestScoreboardHandler extends ContestDetailBaseHandler {
|
|||||||
csv: async (rows) => `\uFEFF${rows.map((c) => (c.map((i) => i.value?.toString().replace(/\n/g, ' ')).join(','))).join('\n')}`,
|
csv: async (rows) => `\uFEFF${rows.map((c) => (c.map((i) => i.value?.toString().replace(/\n/g, ' ')).join(','))).join('\n')}`,
|
||||||
html: (rows, tdoc) => this.renderHTML('contest_scoreboard_download_html.html', { rows, tdoc }),
|
html: (rows, tdoc) => this.renderHTML('contest_scoreboard_download_html.html', { rows, tdoc }),
|
||||||
};
|
};
|
||||||
const [, rows] = await contest.getScoreboard.call(this, domainId, tid, true, 0, ignoreLock);
|
const [, rows] = await contest.getScoreboard.call(this, domainId, tid, true, ignoreLock);
|
||||||
this.binary(await getContent[ext](rows, this.tdoc), `${this.tdoc.title}.${ext}`);
|
this.binary(await getContent[ext](rows, this.tdoc), `${this.tdoc.title}.${ext}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -514,9 +514,9 @@ export interface ContestRule<T = any> {
|
|||||||
) => Promise<ScoreboardRow>;
|
) => 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>,
|
||||||
) => Promise<[board: ScoreboardRow[], udict: Udict, nPages: number]>;
|
) => Promise<[board: ScoreboardRow[], udict: Udict]>;
|
||||||
ranked: (tdoc: Tdoc<30>, cursor: Cursor<ContestStat & T>) => Promise<[Array<[number, ContestStat & T]>, number]>;
|
ranked: (tdoc: Tdoc<30>, cursor: Cursor<ContestStat & T>) => Promise<[number, ContestStat & T][]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ContestRules = Dictionary<ContestRule>;
|
export type ContestRules = Dictionary<ContestRule>;
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
import { Cursor } from 'mongodb';
|
import { Cursor } from 'mongodb';
|
||||||
import paginate from './paginate';
|
|
||||||
|
|
||||||
type ranked = <T>(cursor: Cursor<T>, equ: ((a: T, b: T) => boolean), page?: number) => Promise<[[number, T][], number]>;
|
type ranked = <T>(cursor: Cursor<T>, equ: ((a: T, b: T) => boolean)) => Promise<[number, T][]>;
|
||||||
|
|
||||||
const ranked: ranked = async (cursor, equ, page) => {
|
const ranked: ranked = async (cursor, equ) => {
|
||||||
let last = null;
|
let last = null;
|
||||||
let r = page ? (page - 1) * 100 : 0;
|
let r = 0;
|
||||||
let count = page ? r : 0;
|
let count = 0;
|
||||||
const results = [];
|
const results = [];
|
||||||
const [docs, nPages] = page ? await paginate(cursor, page, 100) : [await cursor.toArray(), 1];
|
const docs = await cursor.toArray();
|
||||||
for (const doc of docs) {
|
for (const doc of docs) {
|
||||||
count++;
|
count++;
|
||||||
if (!last || !equ(last, doc)) r = count;
|
if (!last || !equ(last, doc)) r = count;
|
||||||
last = doc;
|
last = doc;
|
||||||
results.push([r, doc]);
|
results.push([r, doc]);
|
||||||
}
|
}
|
||||||
return [results, nPages];
|
return results;
|
||||||
};
|
};
|
||||||
|
|
||||||
global.Hydro.lib.rank = ranked;
|
global.Hydro.lib.rank = ranked;
|
||||||
|
|||||||
@@ -154,8 +154,8 @@ const acm = buildContestRule({
|
|||||||
}
|
}
|
||||||
return row;
|
return row;
|
||||||
},
|
},
|
||||||
async scoreboard(isExport, _, tdoc, pdict, cursor, page) {
|
async scoreboard(isExport, _, tdoc, pdict, cursor) {
|
||||||
const [rankedTsdocs, nPages] = await ranked(cursor, (a, b) => a.score === b.score && a.time === b.time, page);
|
const rankedTsdocs = await ranked(cursor, (a, b) => a.score === b.score && a.time === b.time);
|
||||||
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
|
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
|
||||||
const udict = await user.getList(tdoc.domainId, uids);
|
const udict = await user.getList(tdoc.domainId, uids);
|
||||||
// Find first accept
|
// Find first accept
|
||||||
@@ -186,7 +186,7 @@ const acm = buildContestRule({
|
|||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
];
|
];
|
||||||
return [rows, udict, nPages];
|
return [rows, udict];
|
||||||
},
|
},
|
||||||
async ranked(tdoc, cursor) {
|
async ranked(tdoc, cursor) {
|
||||||
return await ranked(cursor, (a, b) => a.accept === b.accept && a.time === b.time);
|
return await ranked(cursor, (a, b) => a.accept === b.accept && a.time === b.time);
|
||||||
@@ -272,8 +272,8 @@ const oi = buildContestRule({
|
|||||||
}
|
}
|
||||||
return row;
|
return row;
|
||||||
},
|
},
|
||||||
async scoreboard(isExport, _, tdoc, pdict, cursor, page) {
|
async scoreboard(isExport, _, tdoc, pdict, cursor) {
|
||||||
const [rankedTsdocs, nPages] = await ranked(cursor, (a, b) => a.score === b.score, page);
|
const rankedTsdocs = await ranked(cursor, (a, b) => a.score === b.score);
|
||||||
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
|
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
|
||||||
const udict = await user.getList(tdoc.domainId, uids);
|
const udict = await user.getList(tdoc.domainId, uids);
|
||||||
const psdict = {};
|
const psdict = {};
|
||||||
@@ -307,7 +307,7 @@ const oi = buildContestRule({
|
|||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
];
|
];
|
||||||
return [rows, udict, nPages];
|
return [rows, udict];
|
||||||
},
|
},
|
||||||
async ranked(tdoc, cursor) {
|
async ranked(tdoc, cursor) {
|
||||||
return await ranked(cursor, (a, b) => a.score === b.score);
|
return await ranked(cursor, (a, b) => a.score === b.score);
|
||||||
@@ -453,8 +453,8 @@ const homework = buildContestRule({
|
|||||||
}
|
}
|
||||||
return row;
|
return row;
|
||||||
},
|
},
|
||||||
async scoreboard(isExport, _, tdoc, pdict, cursor, page) {
|
async scoreboard(isExport, _, tdoc, pdict, cursor) {
|
||||||
const [rankedTsdocs, nPages] = await ranked(cursor, (a, b) => a.score === b.score, page);
|
const rankedTsdocs = await ranked(cursor, (a, b) => a.score === b.score);
|
||||||
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
|
const uids = rankedTsdocs.map(([, tsdoc]) => tsdoc.uid);
|
||||||
const udict = await user.getList(tdoc.domainId, uids);
|
const udict = await user.getList(tdoc.domainId, uids);
|
||||||
const columns = await this.scoreboardHeader(isExport, _, tdoc, pdict);
|
const columns = await this.scoreboardHeader(isExport, _, tdoc, pdict);
|
||||||
@@ -464,7 +464,7 @@ const homework = buildContestRule({
|
|||||||
([rank, tsdoc]) => this.scoreboardRow(isExport, _, tdoc, pdict, udict[tsdoc.uid], rank, tsdoc),
|
([rank, tsdoc]) => this.scoreboardRow(isExport, _, tdoc, pdict, udict[tsdoc.uid], rank, tsdoc),
|
||||||
)),
|
)),
|
||||||
];
|
];
|
||||||
return [rows, udict, nPages];
|
return [rows, udict];
|
||||||
},
|
},
|
||||||
async ranked(tdoc, cursor) {
|
async ranked(tdoc, cursor) {
|
||||||
return await ranked(cursor, (a, b) => a.score === b.score);
|
return await ranked(cursor, (a, b) => a.score === b.score);
|
||||||
@@ -679,18 +679,18 @@ export function canShowScoreboard(tdoc: Tdoc<30>, allowPermOverride = true) {
|
|||||||
|
|
||||||
export async function getScoreboard(
|
export async function getScoreboard(
|
||||||
this: Handler, domainId: string, tid: ObjectID,
|
this: Handler, domainId: string, tid: ObjectID,
|
||||||
isExport = false, page: number, ignoreLock = false,
|
isExport = false, ignoreLock = false,
|
||||||
): Promise<[Tdoc<30 | 60>, ScoreboardRow[], Udict, ProblemDict, number]> {
|
): Promise<[Tdoc<30 | 60>, ScoreboardRow[], Udict, ProblemDict]> {
|
||||||
const tdoc = await get(domainId, tid);
|
const tdoc = await get(domainId, tid);
|
||||||
if (!canShowScoreboard.call(this, tdoc)) throw new ContestScoreboardHiddenError(tid);
|
if (!canShowScoreboard.call(this, tdoc)) throw new ContestScoreboardHiddenError(tid);
|
||||||
if (ignoreLock) delete tdoc.lockAt;
|
if (ignoreLock) delete tdoc.lockAt;
|
||||||
const tsdocsCursor = getMultiStatus(domainId, { docId: tid }).sort(RULES[tdoc.rule].statusSort);
|
const tsdocsCursor = getMultiStatus(domainId, { docId: tid }).sort(RULES[tdoc.rule].statusSort);
|
||||||
const pdict = await problem.getList(domainId, tdoc.pids, true);
|
const pdict = await problem.getList(domainId, tdoc.pids, true);
|
||||||
const [rows, udict, nPages] = await RULES[tdoc.rule].scoreboard(
|
const [rows, udict] = await RULES[tdoc.rule].scoreboard(
|
||||||
isExport, this.translate.bind(this),
|
isExport, this.translate.bind(this),
|
||||||
tdoc, pdict, tsdocsCursor, page,
|
tdoc, pdict, tsdocsCursor,
|
||||||
);
|
);
|
||||||
return [tdoc, rows, udict, pdict, nPages];
|
return [tdoc, rows, udict, pdict];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const statusText = (tdoc: Tdoc, tsdoc?: any) => (
|
export const statusText = (tdoc: Tdoc, tsdoc?: any) => (
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { escapeRegExp, pick } from 'lodash';
|
import { escapeRegExp, pick, uniq } from 'lodash';
|
||||||
import LRU from 'lru-cache';
|
import LRU from 'lru-cache';
|
||||||
import { Collection, ObjectID } from 'mongodb';
|
import { Collection, ObjectID } from 'mongodb';
|
||||||
import { LoginError, UserAlreadyExistError, UserNotFoundError } from '../error';
|
import { LoginError, UserAlreadyExistError, UserNotFoundError } from '../error';
|
||||||
@@ -21,7 +21,7 @@ export const coll: Collection<Udoc> = db.collection('user');
|
|||||||
// Virtual user, only for display in contest.
|
// Virtual user, only for display in contest.
|
||||||
export const collV: Collection<VUdoc> = db.collection('vuser');
|
export const collV: Collection<VUdoc> = db.collection('vuser');
|
||||||
export const collGroup: Collection<GDoc> = db.collection('user.group');
|
export const collGroup: Collection<GDoc> = db.collection('user.group');
|
||||||
const cache = new LRU<string, User>({ max: 500, ttl: 300 * 1000 });
|
const cache = new LRU<string, User>({ max: 10000, ttl: 300 * 1000 });
|
||||||
|
|
||||||
export function deleteUserCache(udoc: User | Udoc | string | undefined | null, receiver = false) {
|
export function deleteUserCache(udoc: User | Udoc | string | undefined | null, receiver = false) {
|
||||||
if (!udoc) return;
|
if (!udoc) return;
|
||||||
@@ -192,10 +192,10 @@ class UserModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async getList(domainId: string, uids: number[]): Promise<Udict> {
|
static async getList(domainId: string, uids: number[]): Promise<Udict> {
|
||||||
const _uids = new Set(uids);
|
|
||||||
const r: Udict = {};
|
const r: Udict = {};
|
||||||
// eslint-disable-next-line no-await-in-loop
|
await Promise.all(uniq(uids).map(async (uid) => {
|
||||||
for (const uid of _uids) r[uid] = (await UserModel.getById(domainId, uid)) || new User(UserModel.defaultUser, {});
|
r[uid] = (await UserModel.getById(domainId, uid)) || new User(UserModel.defaultUser, {});
|
||||||
|
}));
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,6 @@
|
|||||||
{% endif %}{%- endfor -%}
|
{% endif %}{%- endfor -%}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{{ paginator.render(page, nPages) }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div></div>
|
</div></div>
|
||||||
|
|||||||
Reference in new issue
Block a user