mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-19 17:38:19 +08:00
core: typing for tsdoc (#1164)
This commit is contained in:
1 parent
fbf27379b3
commit
a6cdbb9914
11 files changed
+108
-44
No files matched your search
@@ -14,7 +14,7 @@ import {
|
|||||||
ContestScoreboardHiddenError, FileLimitExceededError, FileUploadError,
|
ContestScoreboardHiddenError, FileLimitExceededError, FileUploadError,
|
||||||
InvalidTokenError, MethodNotAllowedError, NotAssignedError, NotFoundError, PermissionError, ValidationError,
|
InvalidTokenError, MethodNotAllowedError, NotAssignedError, NotFoundError, PermissionError, ValidationError,
|
||||||
} from '../error';
|
} from '../error';
|
||||||
import { FileInfo, ScoreboardConfig, Tdoc } from '../interface';
|
import { ContestStatusDoc, FileInfo, ScoreboardConfig, Tdoc } from '../interface';
|
||||||
import { PERM, PRIV, STATUS } from '../model/builtin';
|
import { PERM, PRIV, STATUS } from '../model/builtin';
|
||||||
import * as contest from '../model/contest';
|
import * as contest from '../model/contest';
|
||||||
import * as discussion from '../model/discussion';
|
import * as discussion from '../model/discussion';
|
||||||
@@ -77,7 +77,7 @@ export class ContestListHandler extends Handler {
|
|||||||
|
|
||||||
export class ContestDetailBaseHandler extends Handler {
|
export class ContestDetailBaseHandler extends Handler {
|
||||||
tdoc?: Tdoc;
|
tdoc?: Tdoc;
|
||||||
tsdoc?: any;
|
tsdoc?: ContestStatusDoc;
|
||||||
|
|
||||||
@param('tid', Types.ObjectId, true)
|
@param('tid', Types.ObjectId, true)
|
||||||
async __prepare(domainId: string, tid: ObjectId) {
|
async __prepare(domainId: string, tid: ObjectId) {
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ class HomeworkDetailHandler extends Handler {
|
|||||||
const valid = (tsdoc.journal || []).filter((p) => this.tdoc.pids.includes(p.pid));
|
const valid = (tsdoc.journal || []).filter((p) => this.tdoc.pids.includes(p.pid));
|
||||||
for (const pdetail of valid) {
|
for (const pdetail of valid) {
|
||||||
psdict[pdetail.pid] = pdetail;
|
psdict[pdetail.pid] = pdetail;
|
||||||
rdict[pdetail.rid] = { _id: pdetail.rid };
|
rdict[pdetail.rid.toHexString()] = { _id: pdetail.rid };
|
||||||
}
|
}
|
||||||
if (contest.canShowSelfRecord.call(this, this.tdoc) && valid.length) {
|
if (contest.canShowSelfRecord.call(this, this.tdoc) && valid.length) {
|
||||||
rdict = await record.getList(domainId, valid.map((pdetail) => pdetail.rid));
|
rdict = await record.getList(domainId, valid.map((pdetail) => pdetail.rid));
|
||||||
|
|||||||
@@ -368,7 +368,7 @@ export class ProblemDetailHandler extends ContestDetailBaseHandler {
|
|||||||
if (this.tdoc && this.tsdoc) {
|
if (this.tdoc && this.tsdoc) {
|
||||||
const fields = ['attend', 'startAt', 'endAt'];
|
const fields = ['attend', 'startAt', 'endAt'];
|
||||||
if (contest.canShowSelfRecord.call(this, this.tdoc, true)) fields.push('detail');
|
if (contest.canShowSelfRecord.call(this, this.tdoc, true)) fields.push('detail');
|
||||||
this.tsdoc = pick(this.tsdoc, fields);
|
this.tsdoc = pick(this.tsdoc, fields) as typeof this.tsdoc;
|
||||||
this.response.body.tsdoc = this.tsdoc;
|
this.response.body.tsdoc = this.tsdoc;
|
||||||
}
|
}
|
||||||
this.response.template = 'problem_detail.html';
|
this.response.template = 'problem_detail.html';
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ export class RecordDetailHandler extends ContestDetailBaseHandler {
|
|||||||
this.tsdoc = await contest.getStatus(domainId, this.tdoc.docId, this.user._id);
|
this.tsdoc = await contest.getStatus(domainId, this.tdoc.docId, this.user._id);
|
||||||
canViewCode ||= this.user.own(this.tdoc);
|
canViewCode ||= this.user.own(this.tdoc);
|
||||||
if (this.tdoc.allowViewCode && contest.isDone(this.tdoc)) {
|
if (this.tdoc.allowViewCode && contest.isDone(this.tdoc)) {
|
||||||
canViewCode ||= this.tsdoc?.attend;
|
canViewCode ||= !!this.tsdoc?.attend;
|
||||||
}
|
}
|
||||||
if (!this.tsdoc?.attend && pdoc && !problem.canViewBy(pdoc, this.user)) throw new PermissionError(PERM.PERM_VIEW_PROBLEM_HIDDEN);
|
if (!this.tsdoc?.attend && pdoc && !problem.canViewBy(pdoc, this.user)) throw new PermissionError(PERM.PERM_VIEW_PROBLEM_HIDDEN);
|
||||||
} else if (pdoc && !problem.canViewBy(pdoc, this.user)) throw new PermissionError(PERM.PERM_VIEW_PROBLEM_HIDDEN);
|
} else if (pdoc && !problem.canViewBy(pdoc, this.user)) throw new PermissionError(PERM.PERM_VIEW_PROBLEM_HIDDEN);
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ class TrainingMainHandler extends Handler {
|
|||||||
$or: [{ docId: { $in: Array.from(tids) } }, { enroll: 1 }],
|
$or: [{ docId: { $in: Array.from(tids) } }, { enroll: 1 }],
|
||||||
}).toArray();
|
}).toArray();
|
||||||
for (const tsdoc of tsdocs) {
|
for (const tsdoc of tsdocs) {
|
||||||
tsdict[tsdoc.docId] = tsdoc;
|
tsdict[tsdoc.docId.toHexString()] = tsdoc;
|
||||||
enrolledTids.add(tsdoc.docId);
|
enrolledTids.add(tsdoc.docId);
|
||||||
}
|
}
|
||||||
for (const tid of tids) enrolledTids.delete(tid);
|
for (const tid of tids) enrolledTids.delete(tid);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type fs from 'fs';
|
|||||||
import type { Dictionary, NumericDictionary } from 'lodash';
|
import type { Dictionary, NumericDictionary } from 'lodash';
|
||||||
import type { Binary, FindCursor, ObjectId } from 'mongodb';
|
import type { Binary, FindCursor, ObjectId } from 'mongodb';
|
||||||
import type {
|
import type {
|
||||||
FileInfo, RecordJudgeInfo, RecordPayload,
|
FileInfo, RecordJudgeInfo, RecordPayload, SubtaskResult,
|
||||||
} from '@hydrooj/common/types';
|
} from '@hydrooj/common/types';
|
||||||
import type { Context } from './context';
|
import type { Context } from './context';
|
||||||
import type { PrintTaskStatus } from './model/contest';
|
import type { PrintTaskStatus } from './model/contest';
|
||||||
@@ -412,11 +412,70 @@ export interface OplogDoc extends Record<string, any> {
|
|||||||
type: string;
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ContestJournalEntry extends Record<string, any> {
|
||||||
|
rid: ObjectId;
|
||||||
|
pid: number;
|
||||||
|
score: number;
|
||||||
|
status: number;
|
||||||
|
time?: number;
|
||||||
|
lang?: string;
|
||||||
|
subtasks?: Record<number, SubtaskResult>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContestDetailEntry extends Record<string, any> {
|
||||||
|
rid?: ObjectId;
|
||||||
|
pid?: number;
|
||||||
|
score?: number;
|
||||||
|
status?: number;
|
||||||
|
/** ACM: penalty + elapsed time (in seconds). */
|
||||||
|
time?: number;
|
||||||
|
/** ACM: elapsed time since contest begin (in seconds). */
|
||||||
|
real?: number;
|
||||||
|
/** ACM: accumulated penalty (in seconds). */
|
||||||
|
penalty?: number;
|
||||||
|
/** ACM: number of non-accepted submissions on this problem. */
|
||||||
|
naccept?: number;
|
||||||
|
/** Number of submissions hidden after the scoreboard is locked. */
|
||||||
|
npending?: number;
|
||||||
|
/** Ledo: number of effective attempts. */
|
||||||
|
ntry?: number;
|
||||||
|
/** Ledo / homework: score after rule-specific bonus or penalty. */
|
||||||
|
penaltyScore?: number;
|
||||||
|
subtasks?: Record<number, SubtaskResult>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ContestStat extends Record<string, any> {
|
export interface ContestStat extends Record<string, any> {
|
||||||
detail: Record<number, Record<string, any>>;
|
detail: Record<number, ContestDetailEntry>;
|
||||||
|
display?: Record<number, ContestDetailEntry>;
|
||||||
|
score?: number;
|
||||||
|
originalScore?: number;
|
||||||
|
accept?: number;
|
||||||
|
time?: number;
|
||||||
|
penaltyScore?: number;
|
||||||
unrank?: boolean;
|
unrank?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ContestStatusDoc extends StatusDocBase, ContestStat {
|
||||||
|
docId: ObjectId;
|
||||||
|
docType: document['TYPE_CONTEST'];
|
||||||
|
attend?: number;
|
||||||
|
subscribe?: number;
|
||||||
|
journal?: ContestJournalEntry[];
|
||||||
|
startAt?: Date;
|
||||||
|
endAt?: Date; // 灵活时间模式的结束时间,或者是提前结束比赛的时间
|
||||||
|
rev?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TrainingStatusDoc extends StatusDocBase, Record<string, any> {
|
||||||
|
docId: ObjectId;
|
||||||
|
docType: document['TYPE_TRAINING'];
|
||||||
|
enroll?: number;
|
||||||
|
attend?: number;
|
||||||
|
doneNids?: number[];
|
||||||
|
donePids?: number[];
|
||||||
|
done?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ScoreboardConfig {
|
export interface ScoreboardConfig {
|
||||||
isExport: boolean;
|
isExport: boolean;
|
||||||
showDisplayName: boolean;
|
showDisplayName: boolean;
|
||||||
@@ -436,21 +495,21 @@ export interface ContestRule<T = any> {
|
|||||||
showScoreboard: (tdoc: Tdoc, now: Date) => boolean;
|
showScoreboard: (tdoc: Tdoc, now: Date) => boolean;
|
||||||
showSelfRecord: (tdoc: Tdoc, now: Date) => boolean;
|
showSelfRecord: (tdoc: Tdoc, now: Date) => boolean;
|
||||||
showRecord: (tdoc: Tdoc, now: Date) => boolean;
|
showRecord: (tdoc: Tdoc, now: Date) => boolean;
|
||||||
stat: (this: ContestRule<T>, tdoc: Tdoc, journal: any[]) => ContestStat & T;
|
stat: (this: ContestRule<T>, tdoc: Tdoc, journal: ContestJournalEntry[]) => ContestStat & T;
|
||||||
scoreboardHeader: (
|
scoreboardHeader: (
|
||||||
this: ContestRule<T>, config: ScoreboardConfig, _: (s: string) => string,
|
this: ContestRule<T>, config: ScoreboardConfig, _: (s: string) => string,
|
||||||
tdoc: Tdoc, 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, pdict: ProblemDict, udoc: BaseUser, rank: number, tsdoc: ContestStat & T,
|
tdoc: Tdoc, pdict: ProblemDict, udoc: BaseUser, rank: number, tsdoc: ContestStatusDoc & 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, pdict: ProblemDict, cursor: FindCursor<ContestStat & T>,
|
tdoc: Tdoc, pdict: ProblemDict, cursor: FindCursor<ContestStatusDoc & T>,
|
||||||
) => Promise<[board: ScoreboardRow[], udict: BaseUserDict]>;
|
) => Promise<[board: ScoreboardRow[], udict: BaseUserDict]>;
|
||||||
ranked: (tdoc: Tdoc, cursor: FindCursor<ContestStat & T>) => Promise<[number, ContestStat & T][]>;
|
ranked: (tdoc: Tdoc, cursor: FindCursor<ContestStatusDoc & T>) => Promise<[number, ContestStatusDoc & T][]>;
|
||||||
applyProjection: (tdoc: Tdoc, rdoc: RecordDoc, user: User) => RecordDoc;
|
applyProjection: (tdoc: Tdoc, rdoc: RecordDoc, user: User) => RecordDoc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ import {
|
|||||||
ContestScoreboardHiddenError, ValidationError,
|
ContestScoreboardHiddenError, ValidationError,
|
||||||
} from '../error';
|
} from '../error';
|
||||||
import {
|
import {
|
||||||
BaseUserDict, ContestPrintDoc, ContestRule, ContestRules, ProblemDict, RecordDoc,
|
BaseUserDict, ContestJournalEntry, ContestPrintDoc, ContestRule, ContestRules,
|
||||||
ScoreboardConfig, ScoreboardNode, ScoreboardRow, SubtaskResult, Tdoc,
|
ContestStatusDoc, ProblemDict, RecordDoc, ScoreboardConfig, ScoreboardNode,
|
||||||
|
ScoreboardRow, SubtaskResult, Tdoc,
|
||||||
} from '../interface';
|
} from '../interface';
|
||||||
import avatar from '../lib/avatar';
|
import avatar from '../lib/avatar';
|
||||||
import bus from '../service/bus';
|
import bus from '../service/bus';
|
||||||
@@ -60,14 +61,14 @@ export function isNotStarted(tdoc: Tdoc) {
|
|||||||
return (new Date()) < tdoc.beginAt;
|
return (new Date()) < tdoc.beginAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isOngoing(tdoc: Tdoc, tsdoc?: any) {
|
export function isOngoing(tdoc: Tdoc, tsdoc?: ContestStatusDoc): boolean {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
if (tsdoc?.endAt && tsdoc.endAt <= now) return false;
|
if (tsdoc?.endAt && tsdoc.endAt <= now) return false;
|
||||||
if (tsdoc && tdoc.duration && tsdoc.startAt <= new Date(Date.now() - Math.floor(tdoc.duration * Time.hour))) return false;
|
if (tsdoc && tdoc.duration && tsdoc.startAt <= new Date(Date.now() - Math.floor(tdoc.duration * Time.hour))) return false;
|
||||||
return (tdoc.beginAt <= now && now < tdoc.endAt);
|
return (tdoc.beginAt <= now && now < tdoc.endAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isDone(tdoc: Tdoc, tsdoc?: any) {
|
export function isDone(tdoc: Tdoc, tsdoc?: ContestStatusDoc): boolean {
|
||||||
if (tdoc.endAt <= new Date()) return true;
|
if (tdoc.endAt <= new Date()) return true;
|
||||||
if (tsdoc?.endAt && tsdoc.endAt <= new Date()) return true;
|
if (tsdoc?.endAt && tsdoc.endAt <= new Date()) return true;
|
||||||
if (tsdoc && tdoc.duration && tsdoc.startAt <= new Date(Date.now() - Math.floor(tdoc.duration * Time.hour))) return true;
|
if (tsdoc && tdoc.duration && tsdoc.startAt <= new Date(Date.now() - Math.floor(tdoc.duration * Time.hour))) return true;
|
||||||
@@ -362,7 +363,7 @@ const oi = buildContestRule({
|
|||||||
row.push({ type: 'string', value: udoc.displayName || '' });
|
row.push({ type: 'string', value: udoc.displayName || '' });
|
||||||
row.push({ type: 'string', value: udoc.studentId || '' });
|
row.push({ type: 'string', value: udoc.studentId || '' });
|
||||||
}
|
}
|
||||||
row.push({ type: 'total_score', value: tsdoc.score || 0 });
|
row.push({ type: 'total_score', value: (tsdoc.score || 0).toString() });
|
||||||
const accepted = {};
|
const accepted = {};
|
||||||
for (const s of tsdoc.journal || []) {
|
for (const s of tsdoc.journal || []) {
|
||||||
if (!pdict[s.pid]) continue;
|
if (!pdict[s.pid]) continue;
|
||||||
@@ -510,7 +511,7 @@ const strictioi = buildContestRule({
|
|||||||
row.push({ type: 'string', value: udoc.displayName || '' });
|
row.push({ type: 'string', value: udoc.displayName || '' });
|
||||||
row.push({ type: 'string', value: udoc.studentId || '' });
|
row.push({ type: 'string', value: udoc.studentId || '' });
|
||||||
}
|
}
|
||||||
row.push({ type: 'total_score', value: tsdoc.score || 0 });
|
row.push({ type: 'total_score', value: (tsdoc.score || 0).toString() });
|
||||||
const accepted = {};
|
const accepted = {};
|
||||||
for (const s of tsdoc.journal || []) {
|
for (const s of tsdoc.journal || []) {
|
||||||
if (!pdict[s.pid]) continue;
|
if (!pdict[s.pid]) continue;
|
||||||
@@ -603,7 +604,7 @@ const ledo = buildContestRule({
|
|||||||
}
|
}
|
||||||
row.push({
|
row.push({
|
||||||
type: 'total_score',
|
type: 'total_score',
|
||||||
value: tsdoc.score || 0,
|
value: (tsdoc.score || 0).toString(),
|
||||||
hover: tsdoc.score !== tsdoc.originalScore ? _('Original score: {0}').format(tsdoc.originalScore) : '',
|
hover: tsdoc.score !== tsdoc.originalScore ? _('Original score: {0}').format(tsdoc.originalScore) : '',
|
||||||
});
|
});
|
||||||
const accepted = {};
|
const accepted = {};
|
||||||
@@ -742,9 +743,9 @@ const homework = buildContestRule({
|
|||||||
row.push({ type: 'string', value: udoc.displayName || '' });
|
row.push({ type: 'string', value: udoc.displayName || '' });
|
||||||
row.push({ type: 'string', value: udoc.studentId || '' });
|
row.push({ type: 'string', value: udoc.studentId || '' });
|
||||||
}
|
}
|
||||||
row.push({ type: 'string', value: tsdoc.penaltyScore || 0 });
|
row.push({ type: 'string', value: (tsdoc.penaltyScore || 0).toString() });
|
||||||
if (config.isExport) {
|
if (config.isExport) {
|
||||||
row.push({ type: 'string', value: tsdoc.score || 0 });
|
row.push({ type: 'string', value: (tsdoc.score || 0).toString() });
|
||||||
}
|
}
|
||||||
row.push({ type: 'time', value: formatSeconds(tsdoc.time || 0, false), raw: tsdoc.time });
|
row.push({ type: 'time', value: formatSeconds(tsdoc.time || 0, false), raw: tsdoc.time });
|
||||||
const accepted = {};
|
const accepted = {};
|
||||||
@@ -758,9 +759,9 @@ const homework = buildContestRule({
|
|||||||
}
|
}
|
||||||
for (const pid of tdoc.pids) {
|
for (const pid of tdoc.pids) {
|
||||||
const rid = tsddict[pid]?.rid;
|
const rid = tsddict[pid]?.rid;
|
||||||
const colScore = tsddict[pid]?.penaltyScore ?? '';
|
const colScore = (tsddict[pid]?.penaltyScore ?? '').toString();
|
||||||
const colOriginalScore = tsddict[pid]?.score ?? '';
|
const colOriginalScore = (tsddict[pid]?.score ?? '').toString();
|
||||||
const colTime = tsddict[pid]?.time || '';
|
const colTime = (tsddict[pid]?.time || '').toString();
|
||||||
const colTimeStr = colTime ? formatSeconds(colTime, false) : '';
|
const colTimeStr = colTime ? formatSeconds(colTime, false) : '';
|
||||||
if (config.isExport) {
|
if (config.isExport) {
|
||||||
row.push(
|
row.push(
|
||||||
@@ -805,8 +806,8 @@ export const RULES: ContestRules = {
|
|||||||
|
|
||||||
const collBalloon = db.collection('contest.balloon');
|
const collBalloon = db.collection('contest.balloon');
|
||||||
|
|
||||||
function _getStatusJournal(tsdoc) {
|
function _getStatusJournal(tsdoc: ContestStatusDoc): ContestJournalEntry[] {
|
||||||
return tsdoc.journal.sort((a, b) => (a.rid.getTimestamp() - b.rid.getTimestamp()));
|
return tsdoc.journal!.sort((a, b) => (a.rid.getTimestamp().getTime() - b.rid.getTimestamp().getTime()));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function add(
|
export async function add(
|
||||||
@@ -1071,7 +1072,7 @@ export function applyProjection(tdoc: Tdoc, rdoc: RecordDoc, udoc: User) {
|
|||||||
return RULES[tdoc.rule].applyProjection(tdoc, rdoc, udoc);
|
return RULES[tdoc.rule].applyProjection(tdoc, rdoc, udoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const statusText = (tdoc: Tdoc, tsdoc?: any) => (
|
export const statusText = (tdoc: Tdoc, tsdoc?: ContestStatusDoc): string => (
|
||||||
isNew(tdoc)
|
isNew(tdoc)
|
||||||
? 'New'
|
? 'New'
|
||||||
: isUpcoming(tdoc)
|
: isUpcoming(tdoc)
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import {
|
|||||||
} from 'mongodb';
|
} from 'mongodb';
|
||||||
import { Context } from '../context';
|
import { Context } from '../context';
|
||||||
import {
|
import {
|
||||||
Content, ContestClarificationDoc, ContestPrintDoc, DiscussionDoc,
|
Content, ContestClarificationDoc, ContestPrintDoc, ContestStatusDoc,
|
||||||
DiscussionReplyDoc, ProblemDoc, ProblemStatusDoc,
|
DiscussionDoc, DiscussionReplyDoc, ProblemDoc, ProblemStatusDoc,
|
||||||
Tdoc, TrainingDoc,
|
Tdoc, TrainingDoc, TrainingStatusDoc,
|
||||||
} from '../interface';
|
} from '../interface';
|
||||||
import bus from '../service/bus';
|
import bus from '../service/bus';
|
||||||
import db from '../service/db';
|
import db from '../service/db';
|
||||||
@@ -45,6 +45,8 @@ export interface DocType {
|
|||||||
|
|
||||||
export interface DocStatusType {
|
export interface DocStatusType {
|
||||||
[TYPE_PROBLEM]: ProblemStatusDoc;
|
[TYPE_PROBLEM]: ProblemStatusDoc;
|
||||||
|
[TYPE_CONTEST]: ContestStatusDoc;
|
||||||
|
[TYPE_TRAINING]: TrainingStatusDoc;
|
||||||
// FIXME: this need to be typed
|
// FIXME: this need to be typed
|
||||||
[key: number]: any;
|
[key: number]: any;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { flatten } from 'lodash';
|
import { flatten } from 'lodash';
|
||||||
import { Filter, ObjectId } from 'mongodb';
|
import { Filter, ObjectId } from 'mongodb';
|
||||||
import { TrainingAlreadyEnrollError, TrainingNotFoundError } from '../error';
|
import { TrainingAlreadyEnrollError, TrainingNotFoundError } from '../error';
|
||||||
import { TrainingDoc, TrainingNode } from '../interface';
|
import { TrainingDoc, TrainingNode, TrainingStatusDoc } from '../interface';
|
||||||
import * as document from './document';
|
import * as document from './document';
|
||||||
|
|
||||||
export function getStatus(domainId: string, tid: ObjectId, uid: number) {
|
export function getStatus(domainId: string, tid: ObjectId, uid: number) {
|
||||||
return document.getStatus(domainId, document.TYPE_TRAINING, tid, uid);
|
return document.getStatus(domainId, document.TYPE_TRAINING, tid, uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMultiStatus(domainId: string, query: Filter<TrainingDoc>) {
|
export function getMultiStatus(domainId: string, query: Filter<TrainingStatusDoc>) {
|
||||||
return document.getMultiStatus(domainId, document.TYPE_TRAINING, query);
|
return document.getMultiStatus(domainId, document.TYPE_TRAINING, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,8 +16,8 @@ export async function getListStatus(domainId: string, uid: number, tids: ObjectI
|
|||||||
const tsdocs = await getMultiStatus(
|
const tsdocs = await getMultiStatus(
|
||||||
domainId, { uid, docId: { $in: Array.from(new Set(tids)) } },
|
domainId, { uid, docId: { $in: Array.from(new Set(tids)) } },
|
||||||
).toArray();
|
).toArray();
|
||||||
const r = {};
|
const r: Record<string, TrainingStatusDoc> = {};
|
||||||
for (const tsdoc of tsdocs) r[tsdoc.docId] = tsdoc;
|
for (const tsdoc of tsdocs) r[tsdoc.docId.toHexString()] = tsdoc;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ export async function enroll(domainId: string, tid: ObjectId, uid: number) {
|
|||||||
return await document.inc(domainId, document.TYPE_TRAINING, tid, 'attend', 1);
|
return await document.inc(domainId, document.TYPE_TRAINING, tid, 'attend', 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setStatus(domainId: string, tid: ObjectId, uid: number, $set: any) {
|
export function setStatus(domainId: string, tid: ObjectId, uid: number, $set: Partial<TrainingStatusDoc>) {
|
||||||
return document.setStatus(domainId, document.TYPE_TRAINING, tid, uid, $set);
|
return document.setStatus(domainId, document.TYPE_TRAINING, tid, uid, $set);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -102,10 +102,10 @@ export const coreScripts: MigrationScript[] = [
|
|||||||
doc.domainId, document.TYPE_CONTEST, { docId: doc.docId },
|
doc.domainId, document.TYPE_CONTEST, { docId: doc.docId },
|
||||||
).toArray();
|
).toArray();
|
||||||
for (const ctdoc of ctdocs) {
|
for (const ctdoc of ctdocs) {
|
||||||
if (!ctdoc.journal?.filter((i) => isStringPid(i.pid)).length) continue;
|
if (!ctdoc.journal?.filter((i) => isStringPid(i.pid as any)).length) continue;
|
||||||
const journal = [];
|
const journal = [];
|
||||||
for (const i of ctdoc.journal) {
|
for (const i of ctdoc.journal) {
|
||||||
const pdoc = await getProblem(doc.domainId, i.pid);
|
const pdoc = await getProblem(doc.domainId, i.pid as any);
|
||||||
if (pdoc) i.pid = pdoc.docId;
|
if (pdoc) i.pid = pdoc.docId;
|
||||||
journal.push(i);
|
journal.push(i);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { LRUCache } from 'lru-cache';
|
import { LRUCache } from 'lru-cache';
|
||||||
import {
|
import {
|
||||||
avatar, ContestModel, Context, fs, getAlphabeticId, Logger, ObjectId,
|
avatar, ContestJournalEntry, ContestModel, Context, fs, getAlphabeticId, Logger, ObjectId,
|
||||||
PERM, RecordDoc, Schema, STATUS, superagent, Tdoc, Types, UserModel,
|
PERM, RecordDoc, Schema, STATUS, superagent, Tdoc, Types, UserModel,
|
||||||
} from 'hydrooj';
|
} from 'hydrooj';
|
||||||
|
|
||||||
@@ -43,15 +43,17 @@ const statusPrivate = {
|
|||||||
[STATUS.STATUS_CANCELED]: 'CANCELED',
|
[STATUS.STATUS_CANCELED]: 'CANCELED',
|
||||||
};
|
};
|
||||||
|
|
||||||
function submissionBase(tdoc: Tdoc, rdoc: RecordDoc, uid?: number) {
|
function submissionBase(tdoc: Tdoc, rdoc: RecordDoc | ContestJournalEntry, uid?: number) {
|
||||||
// NOTE: rdoc can be either record, or a tsdoc detail entry
|
// NOTE: rdoc can be either record, or a tsdoc detail entry
|
||||||
const submit = new ObjectId(rdoc._id || (rdoc as any).rid).getTimestamp().getTime();
|
const id: ObjectId = '_id' in rdoc ? rdoc._id : rdoc.rid;
|
||||||
|
const submit = new ObjectId(id).getTimestamp().getTime();
|
||||||
|
const teamId = (uid || (rdoc as RecordDoc).uid).toString();
|
||||||
return {
|
return {
|
||||||
problem_id: tdoc.pids.indexOf(rdoc.pid),
|
problem_id: tdoc.pids.indexOf(rdoc.pid),
|
||||||
team_id: `${uid || rdoc.uid}`,
|
team_id: teamId,
|
||||||
timestamp: Math.floor(submit - tdoc.beginAt.getTime()),
|
timestamp: Math.floor(submit - tdoc.beginAt.getTime()),
|
||||||
language: rdoc.lang || '',
|
language: ('lang' in rdoc && rdoc.lang) || '',
|
||||||
submission_id: rdoc._id,
|
submission_id: id.toHexString(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +78,7 @@ async function loadContestState(tdoc: Tdoc, realtime: boolean) {
|
|||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
submissions: tsdocs.flatMap((i) => (i.journal || []).map((j) => {
|
submissions: tsdocs.flatMap((i) => (i.journal || []).map((j) => {
|
||||||
const submit = new ObjectId(j.rid as string).getTimestamp().getTime();
|
const submit = new ObjectId(j.rid).getTimestamp().getTime();
|
||||||
const curStatus = (ended ? status : statusPrivate)[j.status] || 'SYSTEM_ERROR';
|
const curStatus = (ended ? status : statusPrivate)[j.status] || 'SYSTEM_ERROR';
|
||||||
return {
|
return {
|
||||||
...submissionBase(tdoc, j, i.uid),
|
...submissionBase(tdoc, j, i.uid),
|
||||||
|
|||||||
Reference in new issue
Block a user