import { ObjectID, Collection, UpdateQuery, PushOperator, MatchKeysAndValues, OnlyFieldsOfType, } from 'mongodb'; import { Dictionary } from 'lodash'; import { STATUS } from './builtin'; import * as task from './task'; import * as problem from './problem'; import { Rdoc, TestCase, RunConfig, ContestInfo, ProblemConfig, } from '../interface'; import db from '../service/db'; import storage from '../service/storage'; export const coll: Collection = db.collection('record'); export interface RdocBase { _id?: ObjectID, domainId?: string, pid: number uid: number, lang: string, code: string, score?: number, memory?: number, time?: number, judgeTexts?: string[], compilerTexts?: string[], testCases?: TestCase[], rejudged?: boolean, judger?: string, judgeAt?: Date, status?: number, type?: string, hidden?: boolean, input?: string, tid?: ObjectID, ttype?: number, } export interface JudgeTask { _id: ObjectID, rid: ObjectID, domainId: string, pid: number lang: string, code: string, data?: ObjectID, config: string, type?: string, } export async function get(domainId: string, _id: ObjectID): Promise { const res = await coll.findOne({ _id }); if (res && res.domainId === domainId) return res; return null; } export async function judge(domainId: string, rid: ObjectID, priority = 1) { const rdoc = await get(domainId, rid); let config: RunConfig | ProblemConfig = rdoc.config; let data = []; if (rdoc.pid) { const pdoc = await problem.get(domainId, rdoc.pid); data = await storage.list(`problem/${domainId}/${rdoc.pid}/testdata/`); if (!config) config = pdoc?.config || {}; } delete rdoc._id; await task.add({ ...rdoc, priority, type: 'judge', rid, domainId, config, data, }); } export async function add( domainId: string, pid: number, uid: number, lang: string, code: string, addTask: boolean, contestOrConfig?: ContestInfo | RunConfig, ) { const data: Rdoc = { status: STATUS.STATUS_WAITING, _id: new ObjectID(), uid, code, lang, pid, domainId, score: 0, time: 0, memory: 0, hidden: false, judgeTexts: [], compilerTexts: [], testCases: [], judger: null, judgeAt: null, rejudged: false, }; if (contestOrConfig) { if ((contestOrConfig as ContestInfo).type) { data.contest = contestOrConfig as ContestInfo; } else { data.config = contestOrConfig as RunConfig; data.hidden = true; } } const res = await coll.insertOne(data); if (addTask) await judge(domainId, res.insertedId); return res.insertedId; } export function getMulti(domainId: string, query: any) { return coll.find({ ...query, domainId }); } export async function update( domainId: string, _id: ObjectID, $set?: MatchKeysAndValues, $push?: PushOperator, $unset?: OnlyFieldsOfType, ): Promise { const $update: UpdateQuery = {}; if ($set && Object.keys($set).length) $update.$set = $set; if ($push && Object.keys($push).length) $update.$push = $push; if ($unset && Object.keys($unset).length) $update.$unset = $unset; if (Object.keys($update).length) { const res = await coll.findOneAndUpdate( { _id, domainId }, $update, { returnOriginal: false }, ); return res.value; } return await get(domainId, _id); } export function reset(domainId: string, rid: ObjectID, isRejudge: boolean) { const upd: any = { score: 0, status: STATUS.STATUS_WAITING, time: 0, memory: 0, testCases: [], judgeTexts: [], compilerTexts: [], judgeAt: null, judger: null, }; if (isRejudge) upd.rejudged = true; return update(domainId, rid, upd); } export function count(domainId: string, query: any) { return coll.find({ domainId, ...query }).count(); } export async function getList( domainId: string, rids: ObjectID[], showHidden: boolean, ): Promise> { const r = {}; rids = Array.from(new Set(rids)); const rdocs = await coll.find({ domainId, _id: { $in: rids } }).toArray(); for (const rdoc of rdocs) { if (rdoc.hidden && !showHidden) r[rdoc._id.toHexString()] = null; else r[rdoc._id.toHexString()] = rdoc; } return r; } export function getUserInProblemMulti( domainId: string, uid: number, pid: number, getHidden = false, ) { if (!getHidden) { return coll.find({ domainId, uid, pid, hidden: false, }); } return coll.find({ domainId, uid, pid }); } export function getByUid(domainId: string, uid: number, limit: number): Promise { return coll.find({ domainId, uid }).limit(limit).toArray(); } global.Hydro.model.record = { coll, add, get, getMulti, update, count, reset, getList, getUserInProblemMulti, getByUid, judge, };