You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Hydro/hydro/handler/judge.ts

186 lines
5.9 KiB
TypeScript

import yaml from 'js-yaml';
import { ObjectID } from 'mongodb';
import { parseTimeMS, parseMemoryMB } from '../utils';
import { JudgeResultBody, Rdoc } from '../interface';
import * as record from '../model/record';
import * as problem from '../model/problem';
import * as builtin from '../model/builtin';
import * as contest from '../model/contest';
import * as domain from '../model/domain';
import * as task from '../model/task';
import * as bus from '../service/bus';
import {
Route, Handler, Connection, ConnectionHandler, Types, param,
} from '../service/server';
async function _postJudge(rdoc: Rdoc) {
const accept = rdoc.status === builtin.STATUS.STATUS_ACCEPTED;
5 years ago
const tasks = [];
if (rdoc.tid) {
5 years ago
tasks.push(
contest.updateStatus(
rdoc.domainId, rdoc.tid, rdoc.uid,
rdoc._id, rdoc.pid, accept, rdoc.score, rdoc.ttype,
),
5 years ago
);
}
if (await problem.updateStatus(rdoc.domainId, rdoc.pid, rdoc.uid, rdoc._id, rdoc.status)) {
if (accept && !rdoc.rejudged) {
tasks.push(
problem.inc(rdoc.domainId, rdoc.pid, 'nAccept', 1),
domain.incUserInDomain(rdoc.domainId, rdoc.uid, 'nAccept', 1),
);
5 years ago
}
}
await Promise.all(tasks);
}
export async function next(body: JudgeResultBody) {
if (body.rid) body.rid = new ObjectID(body.rid);
let rdoc = await record.get(body.domainId, body.rid);
const $set: any = {};
const $push: any = {};
if (body.case) {
const c: any = {};
c.memory = body.case.memory;
c.time = body.case.time;
c.message = body.case.message;
c.status = body.case.status;
rdoc.testCases.push(c);
$push.testCases = c;
}
if (body.message) {
rdoc.judgeTexts.push(body.message);
$push.judgeTexts = body.message;
}
if (body.compilerText) {
rdoc.compilerTexts.push(body.compilerText);
$push.compilerTexts = body.compilerText;
}
if (body.status) $set.status = body.status;
if (body.score) $set.score = body.score;
if (body.time) $set.time = body.time;
if (body.memory) $set.memory = body.memory;
if (body.progress) $set.progress = body.progress;
rdoc = await record.update(body.domainId, body.rid, $set, $push);
bus.publish('record_change', { rdoc, $set, $push });
}
export async function end(body: JudgeResultBody) {
if (body.rid) body.rid = new ObjectID(body.rid);
let rdoc = await record.get(body.domainId, body.rid);
const $set: any = {};
const $push: any = {};
const $unset = { progress: '' };
if (body.message) {
rdoc.judgeTexts.push(body.message);
$push.judgeTexts = body.message;
}
if (body.compilerText) {
rdoc.compilerTexts.push(body.compilerText);
$push.compilerTexts = body.compilerText;
}
if (body.status) $set.status = body.status;
if (body.score) $set.score = body.score;
4 years ago
if (body.stdout) $set.stdout = body.stdout;
if (body.stderr) $set.stderr = body.stderr;
if (body.time) $set.time = body.time;
if (body.memory) $set.memory = body.memory;
$set.judgeAt = new Date();
$set.judger = body.judger;
rdoc = await record.update(body.domainId, body.rid, $set, $push, $unset);
5 years ago
await _postJudge(rdoc);
bus.publish('record_change', { rdoc }); // trigger a full update
}
4 years ago
class PretestHandler extends Handler {
@param('pid', Types.UnsignedInt, true)
@param('code', Types.String)
@param('lang', Types.String)
@param('time', Types.String, true)
@param('memory', Types.String, true)
@param('input', Types.String, true)
async post(
domainId: string, pid = 0,
code: string, lang: string,
time = '1s', memory = '256m', input = '',
) {
4 years ago
if (pid) {
const pdoc = await problem.get(domainId, pid);
if (pdoc.config) {
const config: any = yaml.safeLoad(pdoc.config);
4 years ago
if (config.time) time = config.time;
if (config.memory) memory = config.memory;
}
}
const rid = await record.add(domainId, {
pid,
4 years ago
uid: this.user._id,
type: 'run',
time: parseTimeMS(time),
memory: parseMemoryMB(memory),
input,
lang,
code,
hidden: true,
}, true);
4 years ago
this.response.body = { rid };
this.response.redirect = this.url('record_detail', { rid });
}
}
class JudgeHandler extends Handler {
async get({ check = false }) {
if (check) return;
5 years ago
const tasks = [];
let t = await task.getFirst({ type: 'judge' });
while (t) {
tasks.push(t);
t = await task.getFirst({ type: 'judge' }); // eslint-disable-line no-await-in-loop
}
this.response.body = { tasks };
}
5 years ago
async postNext() {
await next(this.request.body);
}
5 years ago
async postEnd() {
5 years ago
this.request.body.judger = this.user._id;
await end(this.request.body);
}
}
class JudgeConnectionHandler extends ConnectionHandler {
processing: any;
async message(msg) {
5 years ago
if (msg.key === 'next') await next(msg);
else if (msg.key === 'end') {
await end({ judger: this.user._id, ...msg });
this.processing = null;
const t = await task.getFirst({ type: 'judge' });
this.send({ task: t });
this.processing = t;
}
}
5 years ago
async cleanup() {
if (this.processing) {
await record.reset(this.processing.domainId, this.processing.rid, false);
task.add(this.processing);
}
}
}
export async function apply() {
4 years ago
Route('pretest', '/pretest', PretestHandler);
Route('judge', '/judge', JudgeHandler, builtin.PRIV.PRIV_JUDGE);
Connection('judge_conn', '/judge/conn', JudgeConnectionHandler, builtin.PRIV.PRIV_JUDGE);
5 years ago
}
4 years ago
apply.next = next;
apply.end = end;
global.Hydro.handler.judge = apply;