core: update tdoc type

master
undefined 10 months ago
parent ba4853602e
commit 039d0c2d4d
No known key found for this signature in database

@ -112,7 +112,7 @@ export class ContestListHandler extends Handler {
} }
export class ContestDetailBaseHandler extends Handler { export class ContestDetailBaseHandler extends Handler {
tdoc?: Tdoc<30>; tdoc?: Tdoc;
tsdoc?: any; tsdoc?: any;
@param('tid', Types.ObjectId, true) @param('tid', Types.ObjectId, true)
@ -172,7 +172,7 @@ export class ContestDetailBaseHandler extends Handler {
} }
export class ContestDetailHandler extends Handler { export class ContestDetailHandler extends Handler {
tdoc?: Tdoc<30>; tdoc?: Tdoc;
tsdoc?: any; tsdoc?: any;
@param('tid', Types.ObjectId) @param('tid', Types.ObjectId)

@ -25,7 +25,7 @@ import { ContestDetailBaseHandler } from './contest';
import { postJudge } from './judge'; import { postJudge } from './judge';
class RecordListHandler extends ContestDetailBaseHandler { class RecordListHandler extends ContestDetailBaseHandler {
tdoc?: Tdoc<30>; tdoc?: Tdoc;
@param('page', Types.PositiveInt, true) @param('page', Types.PositiveInt, true)
@param('pid', Types.ProblemId, true) @param('pid', Types.ProblemId, true)
@ -122,7 +122,7 @@ class RecordListHandler extends ContestDetailBaseHandler {
class RecordDetailHandler extends ContestDetailBaseHandler { class RecordDetailHandler extends ContestDetailBaseHandler {
rdoc: RecordDoc; rdoc: RecordDoc;
tdoc?: Tdoc<30>; tdoc?: Tdoc;
@param('rid', Types.ObjectId) @param('rid', Types.ObjectId)
async prepare(domainId: string, rid: ObjectId) { async prepare(domainId: string, rid: ObjectId) {
@ -242,7 +242,7 @@ class RecordMainConnectionHandler extends ConnectionHandler {
pid: number; pid: number;
status: number; status: number;
pretest = false; pretest = false;
tdoc: Tdoc<30>; tdoc: Tdoc;
applyProjection = false; applyProjection = false;
@param('tid', Types.ObjectId, true) @param('tid', Types.ObjectId, true)
@ -334,7 +334,7 @@ class RecordMainConnectionHandler extends ConnectionHandler {
class RecordDetailConnectionHandler extends ConnectionHandler { class RecordDetailConnectionHandler extends ConnectionHandler {
pdoc: ProblemDoc; pdoc: ProblemDoc;
tdoc?: Tdoc<30>; tdoc?: Tdoc;
rid: string = ''; rid: string = '';
disconnectTimeout: NodeJS.Timeout; disconnectTimeout: NodeJS.Timeout;
throttleSend: any; throttleSend: any;

@ -373,9 +373,9 @@ export interface TrainingNode {
pids: number[], pids: number[],
} }
export interface Tdoc<docType = document['TYPE_CONTEST'] | document['TYPE_TRAINING']> extends Document { export interface Tdoc extends Document {
docId: ObjectId; docId: ObjectId;
docType: docType & number; docType: document['TYPE_CONTEST'];
beginAt: Date; beginAt: Date;
endAt: Date; endAt: Date;
attend: number; attend: number;
@ -410,7 +410,8 @@ export interface Tdoc<docType = document['TYPE_CONTEST'] | document['TYPE_TRAINI
dag?: TrainingNode[]; dag?: TrainingNode[];
} }
export interface TrainingDoc extends Tdoc { export interface TrainingDoc extends Omit<Tdoc, 'docType'> {
docType: document['TYPE_TRAINING'],
description: string; description: string;
pin?: number; pin?: number;
dag: TrainingNode[]; dag: TrainingNode[];
@ -536,24 +537,25 @@ export interface ContestRule<T = any> {
check: (args: any) => any; check: (args: any) => any;
statusSort: Record<string, 1 | -1>; statusSort: Record<string, 1 | -1>;
submitAfterAccept: boolean; submitAfterAccept: boolean;
showScoreboard: (tdoc: Tdoc<30>, now: Date) => boolean; showScoreboard: (tdoc: Tdoc, now: Date) => boolean;
showSelfRecord: (tdoc: Tdoc<30>, now: Date) => boolean; showSelfRecord: (tdoc: Tdoc, now: Date) => boolean;
showRecord: (tdoc: Tdoc<30>, now: Date) => boolean; showRecord: (tdoc: Tdoc, now: Date) => boolean;
stat: (this: ContestRule<T>, tdoc: Tdoc<30>, journal: any[]) => ContestStat & T; stat: (this: ContestRule<T>, tdoc: Tdoc, journal: any[]) => ContestStat & T;
scoreboardHeader: ( scoreboardHeader: (
this: ContestRule<T>, config: ScoreboardConfig, _: (s: string) => string, this: ContestRule<T>, config: ScoreboardConfig, _: (s: string) => string,
tdoc: Tdoc<30>, pdict: ProblemDict, tdoc: Tdoc, pdict: ProblemDict,
) => Promise<ScoreboardRow>; ) => Promise<ScoreboardRow>;
scoreboardRow: ( scoreboardRow: (
this: ContestRule<T>, config: ScoreboardConfig, _: (s: string) => string, this: ContestRule<T>, config: ScoreboardConfig, _: (s: string) => string,
tdoc: Tdoc<30>, pdict: ProblemDict, udoc: BaseUser, rank: number, tsdoc: ContestStat & T, tdoc: Tdoc, pdict: ProblemDict, udoc: BaseUser, rank: number, tsdoc: ContestStat & T,
meta?: any, meta?: any,
) => Promise<ScoreboardRow>; ) => Promise<ScoreboardRow>;
scoreboard: ( scoreboard: (
this: ContestRule<T>, config: ScoreboardConfig, _: (s: string) => string, this: ContestRule<T>, config: ScoreboardConfig, _: (s: string) => string,
tdoc: Tdoc<30>, pdict: ProblemDict, cursor: FindCursor<ContestStat & T>, tdoc: Tdoc, pdict: ProblemDict, cursor: FindCursor<ContestStat & T>,
) => Promise<[board: ScoreboardRow[], udict: BaseUserDict]>; ) => Promise<[board: ScoreboardRow[], udict: BaseUserDict]>;
ranked: (tdoc: Tdoc<30>, cursor: FindCursor<ContestStat & T>) => Promise<[number, ContestStat & T][]>; ranked: (tdoc: Tdoc, cursor: FindCursor<ContestStat & T>) => Promise<[number, ContestStat & T][]>;
applyProjection: (tdoc: Tdoc, rdoc: RecordDoc, user: User) => RecordDoc;
} }
export type ContestRules = Dictionary<ContestRule>; export type ContestRules = Dictionary<ContestRule>;

@ -13,6 +13,7 @@ import ranked from '../lib/rank';
import * as bus from '../service/bus'; import * as bus from '../service/bus';
import db from '../service/db'; import db from '../service/db';
import type { Handler } from '../service/server'; import type { Handler } from '../service/server';
import { Optional } from '../typeutils';
import { PERM, STATUS, STATUS_SHORT_TEXTS } from './builtin'; import { PERM, STATUS, STATUS_SHORT_TEXTS } from './builtin';
import * as document from './document'; import * as document from './document';
import problem from './problem'; import problem from './problem';
@ -69,11 +70,11 @@ export function isExtended(tdoc: Tdoc) {
return tdoc.penaltySince.getTime() <= now && now < tdoc.endAt.getTime(); return tdoc.penaltySince.getTime() <= now && now < tdoc.endAt.getTime();
} }
function buildContestRule<T>(def: ContestRule<T>): ContestRule<T>; function buildContestRule<T>(def: Optional<ContestRule<T>, 'applyProjection'>): ContestRule<T>;
function buildContestRule<T>(def: Partial<ContestRule<T>>, baseRule: ContestRule<T>): ContestRule<T>; function buildContestRule<T>(def: Partial<ContestRule<T>>, baseRule: ContestRule<T>): ContestRule<T>;
function buildContestRule<T>(def: Partial<ContestRule<T>>, baseRule: ContestRule<T> = {} as any) { function buildContestRule<T>(def: Partial<ContestRule<T>>, baseRule: ContestRule<T> = {} as any) {
const base = baseRule._originalRule || {}; const base = baseRule._originalRule || {};
const funcs = ['scoreboard', 'scoreboardRow', 'scoreboardHeader', 'stat']; const funcs = ['scoreboard', 'scoreboardRow', 'scoreboardHeader', 'stat', 'applyProjection'];
const f = {}; const f = {};
const rule = { ...baseRule, ...def }; const rule = { ...baseRule, ...def };
for (const key of funcs) { for (const key of funcs) {
@ -81,6 +82,7 @@ function buildContestRule<T>(def: Partial<ContestRule<T>>, baseRule: ContestRule
rule[key] = f[key].bind(rule); rule[key] = f[key].bind(rule);
} }
rule._originalRule = f; rule._originalRule = f;
rule.applyProjection ||= (_, rdoc) => rdoc;
return rule; return rule;
} }
@ -715,9 +717,6 @@ const homework = buildContestRule({
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);
}, },
applyProjection(_, rdoc) {
return rdoc;
},
}); });
export const RULES: ContestRules = { export const RULES: ContestRules = {
@ -733,7 +732,7 @@ function _getStatusJournal(tsdoc) {
export async function add( export async function add(
domainId: string, title: string, content: string, owner: number, domainId: string, title: string, content: string, owner: number,
rule: string, beginAt = new Date(), endAt = new Date(), pids: number[] = [], rule: string, beginAt = new Date(), endAt = new Date(), pids: number[] = [],
rated = false, data: Partial<Tdoc<30>> = {}, rated = false, data: Partial<Tdoc> = {},
) { ) {
if (!RULES[rule]) throw new ValidationError('rule'); if (!RULES[rule]) throw new ValidationError('rule');
if (beginAt >= endAt) throw new ValidationError('beginAt', 'endAt'); if (beginAt >= endAt) throw new ValidationError('beginAt', 'endAt');
@ -765,7 +764,7 @@ export async function del(domainId: string, tid: ObjectId) {
]); ]);
} }
export async function get(domainId: string, tid: ObjectId): Promise<Tdoc<30>> { export async function get(domainId: string, tid: ObjectId): Promise<Tdoc> {
const tdoc = await document.get(domainId, document.TYPE_CONTEST, tid); const tdoc = await document.get(domainId, document.TYPE_CONTEST, tid);
if (!tdoc) throw new ContestNotFoundError(tid); if (!tdoc) throw new ContestNotFoundError(tid);
return tdoc; return tdoc;
@ -807,7 +806,7 @@ export async function getStatus(domainId: string, tid: ObjectId, uid: number) {
} }
async function _updateStatus( async function _updateStatus(
tdoc: Tdoc<30>, uid: number, rid: ObjectId, pid: number, status: STATUS, score: number, tdoc: Tdoc, uid: number, rid: ObjectId, pid: number, status: STATUS, score: number,
subtasks: Record<number, SubtaskResult>, subtasks: Record<number, SubtaskResult>,
) { ) {
const tsdoc = await document.revPushStatus(tdoc.domainId, document.TYPE_CONTEST, tdoc.docId, uid, 'journal', { const tsdoc = await document.revPushStatus(tdoc.domainId, document.TYPE_CONTEST, tdoc.docId, uid, 'journal', {
@ -902,25 +901,25 @@ export async function unlockScoreboard(domainId: string, tid: ObjectId) {
await recalcStatus(domainId, tid); await recalcStatus(domainId, tid);
} }
export function canViewHiddenScoreboard(this: { user: User }, tdoc: Tdoc<30>) { export function canViewHiddenScoreboard(this: { user: User }, tdoc: Tdoc) {
if (this.user.own(tdoc)) return true; if (this.user.own(tdoc)) return true;
if (tdoc.rule === 'homework') return this.user.hasPerm(PERM.PERM_VIEW_HOMEWORK_HIDDEN_SCOREBOARD); if (tdoc.rule === 'homework') return this.user.hasPerm(PERM.PERM_VIEW_HOMEWORK_HIDDEN_SCOREBOARD);
return this.user.hasPerm(PERM.PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD); return this.user.hasPerm(PERM.PERM_VIEW_CONTEST_HIDDEN_SCOREBOARD);
} }
export function canShowRecord(this: { user: User }, tdoc: Tdoc<30>, allowPermOverride = true) { export function canShowRecord(this: { user: User }, tdoc: Tdoc, allowPermOverride = true) {
if (RULES[tdoc.rule].showRecord(tdoc, new Date())) return true; if (RULES[tdoc.rule].showRecord(tdoc, new Date())) return true;
if (allowPermOverride && canViewHiddenScoreboard.call(this, tdoc)) return true; if (allowPermOverride && canViewHiddenScoreboard.call(this, tdoc)) return true;
return false; return false;
} }
export function canShowSelfRecord(this: { user: User }, tdoc: Tdoc<30>, allowPermOverride = true) { export function canShowSelfRecord(this: { user: User }, tdoc: Tdoc, allowPermOverride = true) {
if (RULES[tdoc.rule].showSelfRecord(tdoc, new Date())) return true; if (RULES[tdoc.rule].showSelfRecord(tdoc, new Date())) return true;
if (allowPermOverride && canViewHiddenScoreboard.call(this, tdoc)) return true; if (allowPermOverride && canViewHiddenScoreboard.call(this, tdoc)) return true;
return false; return false;
} }
export function canShowScoreboard(this: { user: User }, tdoc: Tdoc<30>, allowPermOverride = true) { export function canShowScoreboard(this: { user: User }, tdoc: Tdoc, allowPermOverride = true) {
if (RULES[tdoc.rule].showScoreboard(tdoc, new Date())) return true; if (RULES[tdoc.rule].showScoreboard(tdoc, new Date())) return true;
if (allowPermOverride && canViewHiddenScoreboard.call(this, tdoc)) return true; if (allowPermOverride && canViewHiddenScoreboard.call(this, tdoc)) return true;
return false; return false;
@ -928,7 +927,7 @@ export function canShowScoreboard(this: { user: User }, tdoc: Tdoc<30>, allowPer
export async function getScoreboard( export async function getScoreboard(
this: Handler, domainId: string, tid: ObjectId, config: ScoreboardConfig, this: Handler, domainId: string, tid: ObjectId, config: ScoreboardConfig,
): Promise<[Tdoc<30>, ScoreboardRow[], BaseUserDict, ProblemDict]> { ): Promise<[Tdoc, ScoreboardRow[], BaseUserDict, 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);
const tsdocsCursor = getMultiStatus(domainId, { docId: tid }).sort(RULES[tdoc.rule].statusSort); const tsdocsCursor = getMultiStatus(domainId, { docId: tid }).sort(RULES[tdoc.rule].statusSort);
@ -972,7 +971,7 @@ export function getMultiClarification(domainId: string, tid: ObjectId, owner = 0
).sort('_id', -1).toArray(); ).sort('_id', -1).toArray();
} }
export function applyProjection(tdoc: Tdoc<30>, rdoc: RecordDoc, udoc: User) { export function applyProjection(tdoc: Tdoc, rdoc: RecordDoc, udoc: User) {
if (!RULES[tdoc.rule]) return rdoc; if (!RULES[tdoc.rule]) return rdoc;
return RULES[tdoc.rule].applyProjection(tdoc, rdoc, udoc); return RULES[tdoc.rule].applyProjection(tdoc, rdoc, udoc);
} }

@ -55,7 +55,7 @@ export const RpTypes: Record<string, RpDef> = {
}, },
contest: { contest: {
async run(domainIds, udict, report) { async run(domainIds, udict, report) {
const contests: Tdoc<30>[] = await contest.getMulti('', { domainId: { $in: domainIds }, rated: true }) const contests: Tdoc[] = await contest.getMulti('', { domainId: { $in: domainIds }, rated: true })
.limit(10).toArray() as any; .limit(10).toArray() as any;
if (contests.length) await report({ message: `Found ${contests.length} contests in ${domainIds[0]}` }); if (contests.length) await report({ message: `Found ${contests.length} contests in ${domainIds[0]}` });
for (const tdoc of contests.reverse()) { for (const tdoc of contests.reverse()) {

@ -91,9 +91,9 @@ export interface EventMap extends LifecycleEvents, HandlerEvents {
'problem/renameAdditionalFile': (domainId: string, docId: number, name: string, newName: string) => VoidReturn 'problem/renameAdditionalFile': (domainId: string, docId: number, name: string, newName: string) => VoidReturn
'problem/delAdditionalFile': (domainId: string, docId: number, name: string[]) => VoidReturn 'problem/delAdditionalFile': (domainId: string, docId: number, name: string[]) => VoidReturn
'contest/before-add': (payload: Partial<Tdoc<30>>) => VoidReturn 'contest/before-add': (payload: Partial<Tdoc>) => VoidReturn
'contest/add': (payload: Partial<Tdoc<30>>, id: ObjectId) => VoidReturn 'contest/add': (payload: Partial<Tdoc>, id: ObjectId) => VoidReturn
'contest/scoreboard': (tdoc: Tdoc<30>, rows: ScoreboardRow[], udict: BaseUserDict, pdict: ProblemDict) => VoidReturn 'contest/scoreboard': (tdoc: Tdoc, rows: ScoreboardRow[], udict: BaseUserDict, pdict: ProblemDict) => VoidReturn
'contest/balloon': (domainId: string, tid: ObjectId, bdoc: ContestBalloonDoc) => VoidReturn 'contest/balloon': (domainId: string, tid: ObjectId, bdoc: ContestBalloonDoc) => VoidReturn
'oplog/log': (type: string, handler: Handler, args: any, data: any) => VoidReturn; 'oplog/log': (type: string, handler: Handler, args: any, data: any) => VoidReturn;

@ -14,3 +14,4 @@ export type Projection<O> = readonly (string & keyof O)[];
export type Omit<T, K> = Pick<T, Exclude<keyof T, K>>; export type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
export type MaybeArray<T> = T | T[]; export type MaybeArray<T> = T | T[];
export type UnionToIntersection<U> = (U extends any ? (arg: U) => void : never) extends ((arg: infer I) => void) ? I : never; export type UnionToIntersection<U> = (U extends any ? (arg: U) => void : never) extends ((arg: infer I) => void) ? I : never;
export type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;

Loading…
Cancel
Save