mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
update interface: ProblemConfig
This commit is contained in:
1 parent
92335d8529
commit
57881792f3
15 files changed
+226
-145
No files matched your search
@@ -14,6 +14,7 @@ module.exports = {
|
|||||||
parser: '@typescript-eslint/parser',
|
parser: '@typescript-eslint/parser',
|
||||||
plugins: ['@typescript-eslint'],
|
plugins: ['@typescript-eslint'],
|
||||||
rules: {
|
rules: {
|
||||||
|
camelcase: 'off',
|
||||||
'@typescript-eslint/no-unused-vars': 'error',
|
'@typescript-eslint/no-unused-vars': 'error',
|
||||||
'global-require': 'off',
|
'global-require': 'off',
|
||||||
'guard-for-in': 'off',
|
'guard-for-in': 'off',
|
||||||
|
|||||||
@@ -304,14 +304,10 @@ class ContestProblemSubmitHandler extends ContestProblemHandler {
|
|||||||
@param('code', Types.String)
|
@param('code', Types.String)
|
||||||
async post(domainId: string, tid: ObjectID, lang: string, code: string) {
|
async post(domainId: string, tid: ObjectID, lang: string, code: string) {
|
||||||
await this.limitRate('add_record', 60, 100);
|
await this.limitRate('add_record', 60, 100);
|
||||||
const rid = await record.add(domainId, {
|
const rid = await record.add(domainId, this.pdoc.docId, this.user._id, lang, code, true, {
|
||||||
pid: this.pdoc.docId,
|
type: document.TYPE_CONTEST,
|
||||||
uid: this.user._id,
|
tid,
|
||||||
tid: this.tdoc.docId,
|
});
|
||||||
ttype: document.TYPE_CONTEST,
|
|
||||||
lang,
|
|
||||||
code,
|
|
||||||
}, true);
|
|
||||||
const [rdoc] = await Promise.all([
|
const [rdoc] = await Promise.all([
|
||||||
record.get(domainId, rid),
|
record.get(domainId, rid),
|
||||||
contest.updateStatus(domainId, this.tdoc.docId, this.user._id, rid, this.pdoc.docId),
|
contest.updateStatus(domainId, this.tdoc.docId, this.user._id, rid, this.pdoc.docId),
|
||||||
|
|||||||
@@ -181,9 +181,10 @@ class HomeworkDetailProblemSubmitHandler extends HomeworkDetailProblemHandler {
|
|||||||
if (!tsdoc.attend) throw new HomeworkNotAttendedError(tid);
|
if (!tsdoc.attend) throw new HomeworkNotAttendedError(tid);
|
||||||
if (!contest.isOngoing(this.tdoc)) throw new HomeworkNotLiveError(tid);
|
if (!contest.isOngoing(this.tdoc)) throw new HomeworkNotLiveError(tid);
|
||||||
if (!this.tdoc.pids.includes(pid)) throw new ProblemNotFoundError(domainId, pid);
|
if (!this.tdoc.pids.includes(pid)) throw new ProblemNotFoundError(domainId, pid);
|
||||||
const rid = await record.add(domainId, {
|
const rid = await record.add(domainId, pid, this.user._id, lang, code, true, {
|
||||||
pid, lang, code, uid: this.user._id, tid, hidden: true, ttype: document.TYPE_HOMEWORK,
|
type: document.TYPE_HOMEWORK,
|
||||||
}, true);
|
tid,
|
||||||
|
});
|
||||||
const [rdoc] = await Promise.all([
|
const [rdoc] = await Promise.all([
|
||||||
record.get(domainId, rid),
|
record.get(domainId, rid),
|
||||||
contest.updateStatus(domainId, tid, this.user._id,
|
contest.updateStatus(domainId, tid, this.user._id,
|
||||||
|
|||||||
+3
-40
@@ -16,11 +16,11 @@ import {
|
|||||||
async function _postJudge(rdoc: Rdoc) {
|
async function _postJudge(rdoc: Rdoc) {
|
||||||
const accept = rdoc.status === builtin.STATUS.STATUS_ACCEPTED;
|
const accept = rdoc.status === builtin.STATUS.STATUS_ACCEPTED;
|
||||||
const tasks = [];
|
const tasks = [];
|
||||||
if (rdoc.tid) {
|
if (rdoc.contest) {
|
||||||
tasks.push(
|
tasks.push(
|
||||||
contest.updateStatus(
|
contest.updateStatus(
|
||||||
rdoc.domainId, rdoc.tid, rdoc.uid,
|
rdoc.domainId, rdoc.contest.tid, rdoc.uid,
|
||||||
rdoc._id, rdoc.pid, accept, rdoc.score, rdoc.ttype,
|
rdoc._id, rdoc.pid, accept, rdoc.score, rdoc.contest.type,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -93,42 +93,6 @@ export async function end(body: JudgeResultBody) {
|
|||||||
bus.publish('record_change', { rdoc }); // trigger a full update
|
bus.publish('record_change', { rdoc }); // trigger a full update
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = '',
|
|
||||||
) {
|
|
||||||
if (pid) {
|
|
||||||
const pdoc = await problem.get(domainId, pid);
|
|
||||||
if (pdoc.config) {
|
|
||||||
const config: any = yaml.safeLoad(pdoc.config);
|
|
||||||
if (config.time) time = config.time;
|
|
||||||
if (config.memory) memory = config.memory;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const rid = await record.add(domainId, {
|
|
||||||
pid,
|
|
||||||
uid: this.user._id,
|
|
||||||
type: 'run',
|
|
||||||
time: parseTimeMS(time),
|
|
||||||
memory: parseMemoryMB(memory),
|
|
||||||
input,
|
|
||||||
lang,
|
|
||||||
code,
|
|
||||||
hidden: true,
|
|
||||||
}, true);
|
|
||||||
this.response.body = { rid };
|
|
||||||
this.response.redirect = this.url('record_detail', { rid });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class JudgeHandler extends Handler {
|
class JudgeHandler extends Handler {
|
||||||
async get({ check = false }) {
|
async get({ check = false }) {
|
||||||
if (check) return;
|
if (check) return;
|
||||||
@@ -174,7 +138,6 @@ class JudgeConnectionHandler extends ConnectionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function apply() {
|
export async function apply() {
|
||||||
Route('pretest', '/pretest', PretestHandler);
|
|
||||||
Route('judge', '/judge', JudgeHandler, builtin.PRIV.PRIV_JUDGE);
|
Route('judge', '/judge', JudgeHandler, builtin.PRIV.PRIV_JUDGE);
|
||||||
Connection('judge_conn', '/judge/conn', JudgeConnectionHandler, builtin.PRIV.PRIV_JUDGE);
|
Connection('judge_conn', '/judge/conn', JudgeConnectionHandler, builtin.PRIV.PRIV_JUDGE);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-10
@@ -89,18 +89,11 @@ class SystemScriptHandler extends SystemHandler {
|
|||||||
|
|
||||||
@param('id', Types.String)
|
@param('id', Types.String)
|
||||||
@param('args', Types.String, true)
|
@param('args', Types.String, true)
|
||||||
async post(domainId: string, id: string, args = '{}') {
|
async post(domainId: string, id: string, raw = '{}') {
|
||||||
if (!global.Hydro.script[id]) throw new ValidationError('id');
|
if (!global.Hydro.script[id]) throw new ValidationError('id');
|
||||||
args = JSON.parse(args);
|
const args = JSON.parse(raw);
|
||||||
validate(global.Hydro.script[id].validate, args);
|
validate(global.Hydro.script[id].validate, args);
|
||||||
const rid = await record.add(domainId, {
|
const rid = await record.add(domainId, -1, this.user._id, '-', id, false, { input: 'raw' });
|
||||||
pid: -1,
|
|
||||||
uid: this.user._id,
|
|
||||||
lang: '-',
|
|
||||||
code: id,
|
|
||||||
status: STATUS.STATUS_JUDGING,
|
|
||||||
hidden: true,
|
|
||||||
}, false);
|
|
||||||
judge.next({ domainId, rid, message: `Running script: ${id}` });
|
judge.next({ domainId, rid, message: `Running script: ${id}` });
|
||||||
async function report(data) {
|
async function report(data) {
|
||||||
judge.next({ domainId, rid, ...data });
|
judge.next({ domainId, rid, ...data });
|
||||||
|
|||||||
+22
-15
@@ -24,7 +24,9 @@ import {
|
|||||||
ValidationError,
|
ValidationError,
|
||||||
PermissionError,
|
PermissionError,
|
||||||
} from '../error';
|
} from '../error';
|
||||||
import { Pdoc, User, Rdoc } from '../interface';
|
import {
|
||||||
|
Pdoc, User, Rdoc, ProblemConfig,
|
||||||
|
} from '../interface';
|
||||||
|
|
||||||
const parseCategory = (value: string) => {
|
const parseCategory = (value: string) => {
|
||||||
if (!value) return [];
|
if (!value) return [];
|
||||||
@@ -179,12 +181,9 @@ class ProblemDetailHandler extends ProblemHandler {
|
|||||||
if (this.pdoc.hidden && this.pdoc.owner !== this.user._id) {
|
if (this.pdoc.hidden && this.pdoc.owner !== this.user._id) {
|
||||||
this.checkPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN);
|
this.checkPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN);
|
||||||
}
|
}
|
||||||
const config: any = this.pdoc.config ? yaml.safeLoad(this.pdoc.config) : {};
|
|
||||||
this.udoc = await user.getById(domainId, this.pdoc.owner);
|
this.udoc = await user.getById(domainId, this.pdoc.owner);
|
||||||
this.response.body = {
|
this.response.body = {
|
||||||
pdoc: this.pdoc,
|
pdoc: this.pdoc,
|
||||||
time: config.time,
|
|
||||||
memory: config.memory,
|
|
||||||
udoc: this.udoc,
|
udoc: this.udoc,
|
||||||
title: this.pdoc.title,
|
title: this.pdoc.title,
|
||||||
path: [
|
path: [
|
||||||
@@ -255,14 +254,12 @@ class ProblemSubmitHandler extends ProblemDetailHandler {
|
|||||||
@param('lang', Types.String)
|
@param('lang', Types.String)
|
||||||
@param('code', Types.String)
|
@param('code', Types.String)
|
||||||
async post(domainId: string, lang: string, code: string) {
|
async post(domainId: string, lang: string, code: string) {
|
||||||
const rid = await record.add(domainId, {
|
const rid = await record.add(domainId, this.pdoc.docId, this.user._id, lang, code, true);
|
||||||
uid: this.user._id, lang, code, pid: this.pdoc.docId,
|
|
||||||
}, true);
|
|
||||||
const [rdoc] = await Promise.all([
|
const [rdoc] = await Promise.all([
|
||||||
record.get(domainId, rid),
|
record.get(domainId, rid),
|
||||||
domain.incUserInDomain(domainId, this.user._id, 'nSubmit'),
|
domain.incUserInDomain(domainId, this.user._id, 'nSubmit'),
|
||||||
]);
|
]);
|
||||||
bus.publish('record_change', rdoc);
|
bus.publish('record_change', { rdoc });
|
||||||
this.response.body = { rid };
|
this.response.body = { rid };
|
||||||
this.response.redirect = this.url('record_detail', { rid });
|
this.response.redirect = this.url('record_detail', { rid });
|
||||||
}
|
}
|
||||||
@@ -274,11 +271,18 @@ class ProblemPretestHandler extends ProblemDetailHandler {
|
|||||||
@param('input', Types.String, true)
|
@param('input', Types.String, true)
|
||||||
async post(domainId: string, lang: string, code: string, input: string = '') {
|
async post(domainId: string, lang: string, code: string, input: string = '') {
|
||||||
this.limitRate('add_record', 3600, 100);
|
this.limitRate('add_record', 3600, 100);
|
||||||
const rid = await record.add(domainId, {
|
// TODO parseConfig
|
||||||
uid: this.user._id, lang, code, pid: this.pdoc.docId, input,
|
const rid = await record.add(
|
||||||
}, true);
|
domainId, this.pdoc.docId, this.user._id,
|
||||||
|
lang, code, true,
|
||||||
|
{
|
||||||
|
input,
|
||||||
|
time: 1000,
|
||||||
|
memory: 256,
|
||||||
|
},
|
||||||
|
);
|
||||||
const rdoc = await record.get(domainId, rid);
|
const rdoc = await record.get(domainId, rid);
|
||||||
bus.publish('record_change', rdoc);
|
bus.publish('record_change', { rdoc });
|
||||||
this.response.body = { rid };
|
this.response.body = { rid };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,7 +315,8 @@ class ProblemPretestConnectionHandler extends ConnectionHandler {
|
|||||||
rdoc.testCases = rdoc.testCases.map((c) => ({
|
rdoc.testCases = rdoc.testCases.map((c) => ({
|
||||||
status: c.status,
|
status: c.status,
|
||||||
}));
|
}));
|
||||||
if (rdoc.tid) return;
|
// TODO handle update
|
||||||
|
if (rdoc.contest) return;
|
||||||
this.send({ rdoc });
|
this.send({ rdoc });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,9 +359,11 @@ class ProblemSettingsHandler extends ProblemManageHandler {
|
|||||||
|
|
||||||
@param('pid', Types.String, null, parsePid)
|
@param('pid', Types.String, null, parsePid)
|
||||||
@param('yaml', Types.String)
|
@param('yaml', Types.String)
|
||||||
async postConfig(domainId: string, pid: string | number, yaml: string) {
|
async postConfig(domainId: string, pid: string | number, cfg: string) {
|
||||||
const pdoc = await problem.get(domainId, pid);
|
const pdoc = await problem.get(domainId, pid);
|
||||||
await problem.edit(domainId, pdoc.docId, { config: yaml });
|
// TODO validate
|
||||||
|
const config = yaml.safeLoad(cfg);
|
||||||
|
await problem.edit(domainId, pdoc.docId, { config });
|
||||||
this.back();
|
this.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,14 +63,13 @@ class RecordDetailHandler extends RecordHandler {
|
|||||||
async get(domainId: string, rid: ObjectID) {
|
async get(domainId: string, rid: ObjectID) {
|
||||||
this.response.template = 'record_detail.html';
|
this.response.template = 'record_detail.html';
|
||||||
const rdoc = await record.get(domainId, rid);
|
const rdoc = await record.get(domainId, rid);
|
||||||
if (rdoc.tid) {
|
if (rdoc.contest) {
|
||||||
const tdoc = await contest.get(domainId, rdoc.tid, rdoc.ttype);
|
const tdoc = await contest.get(domainId, rdoc.contest.tid, rdoc.contest.type);
|
||||||
if (!this.canShowRecord(tdoc, true)) throw new PermissionError(rid);
|
if (!this.canShowRecord(tdoc, true)) throw new PermissionError(rid);
|
||||||
}
|
}
|
||||||
if (rdoc.uid !== this.user.uid && !this.user.hasPerm(PERM.PERM_READ_RECORD_CODE)) {
|
if (rdoc.uid !== this.user.uid && !this.user.hasPerm(PERM.PERM_READ_RECORD_CODE)) {
|
||||||
rdoc.code = null;
|
rdoc.code = null;
|
||||||
}
|
}
|
||||||
if (rdoc.type !== 'run') rdoc.stdout = rdoc.stderr = '';
|
|
||||||
const [pdoc, udoc] = await Promise.all([
|
const [pdoc, udoc] = await Promise.all([
|
||||||
problem.get(domainId, rdoc.pid, null),
|
problem.get(domainId, rdoc.pid, null),
|
||||||
user.getById(domainId, rdoc.uid),
|
user.getById(domainId, rdoc.uid),
|
||||||
@@ -83,8 +82,6 @@ class RecordDetailHandler extends RecordHandler {
|
|||||||
udoc,
|
udoc,
|
||||||
rdoc,
|
rdoc,
|
||||||
pdoc,
|
pdoc,
|
||||||
// TODO protect
|
|
||||||
showStatus: true,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +131,7 @@ class RecordMainConnectionHandler extends RecordConnectionHandler {
|
|||||||
|
|
||||||
async onRecordChange(data) {
|
async onRecordChange(data) {
|
||||||
const rdoc: Rdoc = data.value.rdoc;
|
const rdoc: Rdoc = data.value.rdoc;
|
||||||
if (rdoc.tid && rdoc.tid.toString() !== this.tid) return;
|
if (rdoc.contest && rdoc.contest.tid.toString() !== this.tid) return;
|
||||||
// eslint-disable-next-line prefer-const
|
// eslint-disable-next-line prefer-const
|
||||||
let [udoc, pdoc] = await Promise.all([
|
let [udoc, pdoc] = await Promise.all([
|
||||||
user.getById(this.domainId, rdoc.uid),
|
user.getById(this.domainId, rdoc.uid),
|
||||||
@@ -149,8 +146,8 @@ class RecordDetailConnectionHandler extends contest.ContestHandlerMixin(Connecti
|
|||||||
@param('rid', Types.ObjectID)
|
@param('rid', Types.ObjectID)
|
||||||
async prepare(domainId: string, rid: ObjectID) {
|
async prepare(domainId: string, rid: ObjectID) {
|
||||||
const rdoc = await record.get(domainId, rid);
|
const rdoc = await record.get(domainId, rid);
|
||||||
if (rdoc.tid) {
|
if (rdoc.contest) {
|
||||||
const tdoc = await contest.get(domainId, rdoc.tid, -1);
|
const tdoc = await contest.get(domainId, rdoc.contest.tid, -1);
|
||||||
if (!this.canShowRecord(tdoc)) {
|
if (!this.canShowRecord(tdoc)) {
|
||||||
this.close();
|
this.close();
|
||||||
return;
|
return;
|
||||||
|
|||||||
+75
-8
@@ -62,6 +62,66 @@ export interface ProblemDataSource {
|
|||||||
// ObjectID for built-in files, ProblemDataSource for other source (RemoteJudge, etc.)
|
// ObjectID for built-in files, ProblemDataSource for other source (RemoteJudge, etc.)
|
||||||
export type ProblemData = ObjectID | ProblemDataSource;
|
export type ProblemData = ObjectID | ProblemDataSource;
|
||||||
|
|
||||||
|
export interface TestCaseConfig {
|
||||||
|
input: string,
|
||||||
|
output: string,
|
||||||
|
time?: number,
|
||||||
|
memory?: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum LocalProblemType {
|
||||||
|
Default = 'default',
|
||||||
|
SubmitAnswer = 'submit_answer',
|
||||||
|
Interactive = 'interactive',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum RemoteProblemType {
|
||||||
|
RemoteJudge = 'remotejudge',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum RemoteServerType {
|
||||||
|
vj4 = 'vj4',
|
||||||
|
syzoj = 'syzoj',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SubtaskType {
|
||||||
|
min = 'min',
|
||||||
|
max = 'max',
|
||||||
|
sum = 'sum',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SubtaskConfig {
|
||||||
|
time?: number,
|
||||||
|
memory?: number,
|
||||||
|
score?: number,
|
||||||
|
type?: SubtaskType,
|
||||||
|
cases?: TestCaseConfig[],
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LocalProblemConfig {
|
||||||
|
type?: LocalProblemType,
|
||||||
|
score?: number,
|
||||||
|
time?: number,
|
||||||
|
memory?: number,
|
||||||
|
checker_type?: string,
|
||||||
|
checker?: string,
|
||||||
|
interactor?: string,
|
||||||
|
user_extra_files?: string[],
|
||||||
|
judge_extra_files?: string[],
|
||||||
|
cases?: TestCaseConfig[],
|
||||||
|
subtasks?: SubtaskConfig[],
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RemoteProblemConfig {
|
||||||
|
type?: RemoteProblemType,
|
||||||
|
server_type?: RemoteServerType,
|
||||||
|
url?: string,
|
||||||
|
// TODO handle username&password storage
|
||||||
|
pid?: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProblemConfig = LocalProblemConfig | RemoteProblemConfig;
|
||||||
|
|
||||||
export interface Pdoc {
|
export interface Pdoc {
|
||||||
_id: ObjectID,
|
_id: ObjectID,
|
||||||
domainId: string,
|
domainId: string,
|
||||||
@@ -77,7 +137,7 @@ export interface Pdoc {
|
|||||||
category: string[],
|
category: string[],
|
||||||
data?: ProblemData,
|
data?: ProblemData,
|
||||||
hidden: boolean,
|
hidden: boolean,
|
||||||
config: string,
|
config: ProblemConfig,
|
||||||
acMsg?: string,
|
acMsg?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,6 +150,17 @@ export interface TestCase {
|
|||||||
message: string,
|
message: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PretestConfig {
|
||||||
|
time?: number,
|
||||||
|
memory?: number,
|
||||||
|
input: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContestInfo {
|
||||||
|
type: number,
|
||||||
|
tid: ObjectID,
|
||||||
|
}
|
||||||
|
|
||||||
export interface Rdoc {
|
export interface Rdoc {
|
||||||
_id: ObjectID,
|
_id: ObjectID,
|
||||||
domainId: string,
|
domainId: string,
|
||||||
@@ -107,13 +178,9 @@ export interface Rdoc {
|
|||||||
judger: string,
|
judger: string,
|
||||||
judgeAt: Date,
|
judgeAt: Date,
|
||||||
status: number,
|
status: number,
|
||||||
type: string,
|
|
||||||
hidden: boolean,
|
hidden: boolean,
|
||||||
input?: string,
|
config?: PretestConfig,
|
||||||
stdout?: string,
|
contest?: ContestInfo,
|
||||||
stderr?: string,
|
|
||||||
tid?: ObjectID,
|
|
||||||
ttype?: number,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ScoreboardNode {
|
export interface ScoreboardNode {
|
||||||
@@ -279,7 +346,7 @@ declare global {
|
|||||||
paginate: typeof import('./lib/paginate').default,
|
paginate: typeof import('./lib/paginate').default,
|
||||||
rank: typeof import('./lib/rank').default,
|
rank: typeof import('./lib/rank').default,
|
||||||
rating: typeof import('./lib/rating').default,
|
rating: typeof import('./lib/rating').default,
|
||||||
readConfig: typeof import('./lib/readConfig').default,
|
testdataConfig: typeof import('./lib/testdataConfig'),
|
||||||
sha1: typeof import('./lib/sha1').default,
|
sha1: typeof import('./lib/sha1').default,
|
||||||
sysinfo: typeof import('./lib/sysinfo'),
|
sysinfo: typeof import('./lib/sysinfo'),
|
||||||
template: typeof import('./lib/template'),
|
template: typeof import('./lib/template'),
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
import Zip from 'adm-zip';
|
|
||||||
|
|
||||||
async function readConfig(filePath: string) {
|
|
||||||
const data = new Zip(filePath);
|
|
||||||
const entries = data.getEntries();
|
|
||||||
for (const entry of entries) {
|
|
||||||
if (entry.name.toLowerCase() === 'config.yaml') {
|
|
||||||
return entry.getData().toString();
|
|
||||||
}
|
|
||||||
if (entry.name.toLowerCase() === 'config.ini') {
|
|
||||||
const ini = entry.getData().toString();
|
|
||||||
const conv = global.Hydro.lib['testdata.convert.ini'];
|
|
||||||
return conv(ini);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
global.Hydro.lib.readConfig = readConfig;
|
|
||||||
|
|
||||||
export default readConfig;
|
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import Zip from 'adm-zip';
|
||||||
|
import { safeLoad } from 'js-yaml';
|
||||||
|
import { LocalProblemConfig } from '../interface';
|
||||||
|
|
||||||
|
export async function readConfig(filePath: string) {
|
||||||
|
const data = new Zip(filePath);
|
||||||
|
const entries = data.getEntries();
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (entry.name.toLowerCase() === 'config.yaml') {
|
||||||
|
return entry.getData().toString();
|
||||||
|
}
|
||||||
|
if (entry.name.toLowerCase() === 'config.ini') {
|
||||||
|
const ini = entry.getData().toString();
|
||||||
|
const conv = global.Hydro.lib['testdata.convert.ini'];
|
||||||
|
return conv(ini);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ParseResult {
|
||||||
|
count: number,
|
||||||
|
memoryMax: number,
|
||||||
|
memoryMin: number,
|
||||||
|
timeMax: number,
|
||||||
|
timeMin: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function parseConfig(config: string | LocalProblemConfig) {
|
||||||
|
const result: ParseResult = {
|
||||||
|
count: 0,
|
||||||
|
memoryMin: Number.MAX_SAFE_INTEGER,
|
||||||
|
memoryMax: 0,
|
||||||
|
timeMin: Number.MAX_SAFE_INTEGER,
|
||||||
|
timeMax: 0,
|
||||||
|
};
|
||||||
|
let cfg: LocalProblemConfig = {};
|
||||||
|
if (typeof config === 'string') {
|
||||||
|
// TODO should validate here?
|
||||||
|
cfg = safeLoad(config) as LocalProblemConfig;
|
||||||
|
} else cfg = config;
|
||||||
|
if (cfg.cases) {
|
||||||
|
for (const c of cfg.cases) {
|
||||||
|
result.memoryMax = Math.max(result.memoryMax, c.memory);
|
||||||
|
result.memoryMin = Math.min(result.memoryMin, c.memory);
|
||||||
|
result.timeMax = Math.max(result.timeMax, c.time);
|
||||||
|
result.timeMin = Math.min(result.timeMin, c.time);
|
||||||
|
result.count++;
|
||||||
|
}
|
||||||
|
} else if (cfg.subtasks) {
|
||||||
|
for (const subtask of cfg.subtasks) {
|
||||||
|
result.memoryMax = Math.max(result.memoryMax, subtask.memory);
|
||||||
|
result.memoryMin = Math.min(result.memoryMin, subtask.memory);
|
||||||
|
result.timeMax = Math.max(result.timeMax, subtask.time);
|
||||||
|
result.timeMin = Math.min(result.timeMin, subtask.time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
global.Hydro.lib.testdataConfig = { readConfig, parseConfig };
|
||||||
+6
-1
@@ -10,6 +10,8 @@ import fs from 'fs-extra';
|
|||||||
import { argv } from 'yargs';
|
import { argv } from 'yargs';
|
||||||
import AdmZip from 'adm-zip';
|
import AdmZip from 'adm-zip';
|
||||||
|
|
||||||
|
export * from './interface';
|
||||||
|
|
||||||
global.Hydro = {
|
global.Hydro = {
|
||||||
stat: { reqCount: 0 },
|
stat: { reqCount: 0 },
|
||||||
handler: {},
|
handler: {},
|
||||||
@@ -65,7 +67,10 @@ async function entry(config: EntryConfig) {
|
|||||||
const sargv = [`--entry=${config.entry}`];
|
const sargv = [`--entry=${config.entry}`];
|
||||||
const p = await fork(sargv);
|
const p = await fork(sargv);
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
p.on('exit', resolve);
|
p.on('exit', (code, signal) => {
|
||||||
|
if (code === 0) resolve();
|
||||||
|
else reject(signal);
|
||||||
|
});
|
||||||
p.on('error', (err: Error) => {
|
p.on('error', (err: Error) => {
|
||||||
p.kill();
|
p.kill();
|
||||||
reject(err);
|
reject(err);
|
||||||
|
|||||||
+6
-14
@@ -1,17 +1,15 @@
|
|||||||
import { ObjectID } from 'mongodb';
|
import { ObjectID } from 'mongodb';
|
||||||
import * as user from './user';
|
import * as user from './user';
|
||||||
import * as problem from './problem';
|
import * as problem from './problem';
|
||||||
|
import * as document from './document';
|
||||||
|
import { PERM } from './builtin';
|
||||||
import {
|
import {
|
||||||
ValidationError, ContestNotFoundError, ContestAlreadyAttendedError,
|
ValidationError, ContestNotFoundError, ContestAlreadyAttendedError,
|
||||||
ContestNotAttendedError, ContestScoreboardHiddenError,
|
ContestNotAttendedError, ContestScoreboardHiddenError,
|
||||||
} from '../error';
|
} from '../error';
|
||||||
import * as document from './document';
|
|
||||||
import { PERM } from './builtin';
|
|
||||||
import {
|
import {
|
||||||
Tdoc, ContestRule, ContestRules,
|
ContestRule, ContestRules, ScoreboardNode, Tdoc, Udict,
|
||||||
Udict, ScoreboardNode,
|
|
||||||
} from '../interface';
|
} from '../interface';
|
||||||
import * as validator from '../lib/validator';
|
|
||||||
import * as misc from '../lib/misc';
|
import * as misc from '../lib/misc';
|
||||||
import ranked from '../lib/rank';
|
import ranked from '../lib/rank';
|
||||||
|
|
||||||
@@ -21,7 +19,7 @@ const acm: ContestRule = {
|
|||||||
statusSort: { accept: -1, time: 1 },
|
statusSort: { accept: -1, time: 1 },
|
||||||
showScoreboard: () => true,
|
showScoreboard: () => true,
|
||||||
showRecord: (tdoc, now) => now > tdoc.endAt,
|
showRecord: (tdoc, now) => now > tdoc.endAt,
|
||||||
stat: (tdoc, journal: any[]) => {
|
stat: (tdoc, journal) => {
|
||||||
const naccept = {};
|
const naccept = {};
|
||||||
const effective = {};
|
const effective = {};
|
||||||
const detail = [];
|
const detail = [];
|
||||||
@@ -156,12 +154,8 @@ const oi: ContestRule = {
|
|||||||
}
|
}
|
||||||
return { score, detail };
|
return { score, detail };
|
||||||
},
|
},
|
||||||
showScoreboard(tdoc, now) {
|
showScoreboard: (tdoc, now) => now > tdoc.endAt,
|
||||||
return now > tdoc.endAt;
|
showRecord: (tdoc, now) => now > tdoc.endAt,
|
||||||
},
|
|
||||||
showRecord(tdoc, now) {
|
|
||||||
return now > tdoc.endAt;
|
|
||||||
},
|
|
||||||
scoreboard(isExport, _, tdoc, rankedTsdocs, udict, pdict) {
|
scoreboard(isExport, _, tdoc, rankedTsdocs, udict, pdict) {
|
||||||
const columns: ScoreboardNode[] = [
|
const columns: ScoreboardNode[] = [
|
||||||
{ type: 'rank', value: _('Rank') },
|
{ type: 'rank', value: _('Rank') },
|
||||||
@@ -364,8 +358,6 @@ export function add(
|
|||||||
rule: string, beginAt = new Date(), endAt = new Date(), pids = [],
|
rule: string, beginAt = new Date(), endAt = new Date(), pids = [],
|
||||||
rated = false, data = {}, type = document.TYPE_CONTEST,
|
rated = false, data = {}, type = document.TYPE_CONTEST,
|
||||||
) {
|
) {
|
||||||
validator.checkTitle(title);
|
|
||||||
validator.checkContent(content);
|
|
||||||
if (!this.RULES[rule]) throw new ValidationError('rule');
|
if (!this.RULES[rule]) throw new ValidationError('rule');
|
||||||
if (beginAt >= endAt) throw new ValidationError('beginAt', 'endAt');
|
if (beginAt >= endAt) throw new ValidationError('beginAt', 'endAt');
|
||||||
Object.assign(data, {
|
Object.assign(data, {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import * as document from './document';
|
|||||||
import * as domain from './domain';
|
import * as domain from './domain';
|
||||||
import { Pdoc, Pdict, ProblemDataSource } from '../interface';
|
import { Pdoc, Pdict, ProblemDataSource } from '../interface';
|
||||||
import { ProblemNotFoundError } from '../error';
|
import { ProblemNotFoundError } from '../error';
|
||||||
import readConfig from '../lib/readConfig';
|
import * as testdataConfig from '../lib/testdataConfig';
|
||||||
|
|
||||||
export const pdocHidden: Pdoc = {
|
export const pdocHidden: Pdoc = {
|
||||||
_id: new ObjectID(),
|
_id: new ObjectID(),
|
||||||
@@ -22,7 +22,7 @@ export const pdocHidden: Pdoc = {
|
|||||||
category: [],
|
category: [],
|
||||||
data: null,
|
data: null,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
config: '',
|
config: {},
|
||||||
acMsg: '',
|
acMsg: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ export function setStar(domainId: string, pid: number, uid: number, star: boolea
|
|||||||
|
|
||||||
export async function setTestdata(domainId: string, _id: number, filePath: string) {
|
export async function setTestdata(domainId: string, _id: number, filePath: string) {
|
||||||
const pdoc = await get(domainId, _id);
|
const pdoc = await get(domainId, _id);
|
||||||
const config = await readConfig(filePath);
|
const config = await testdataConfig.readConfig(filePath);
|
||||||
const id = await file.add(filePath, 'data.zip', 1);
|
const id = await file.add(filePath, 'data.zip', 1);
|
||||||
if (pdoc.data instanceof ObjectID) file.del(pdoc.data);
|
if (pdoc.data instanceof ObjectID) file.del(pdoc.data);
|
||||||
return await edit(domainId, _id, { data: id, config });
|
return await edit(domainId, _id, { data: id, config });
|
||||||
|
|||||||
+33
-13
@@ -4,7 +4,9 @@ import { ObjectID } from 'mongodb';
|
|||||||
import { STATUS } from './builtin';
|
import { STATUS } from './builtin';
|
||||||
import * as task from './task';
|
import * as task from './task';
|
||||||
import * as problem from './problem';
|
import * as problem from './problem';
|
||||||
import { Rdoc, TestCase } from '../interface';
|
import {
|
||||||
|
Rdoc, TestCase, PretestConfig, ContestInfo,
|
||||||
|
} from '../interface';
|
||||||
import { RecordNotFoundError } from '../error';
|
import { RecordNotFoundError } from '../error';
|
||||||
import * as db from '../service/db';
|
import * as db from '../service/db';
|
||||||
|
|
||||||
@@ -46,28 +48,50 @@ export interface JudgeTask {
|
|||||||
type?: string,
|
type?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function add(domainId: string, data: RdocBase, addTask: boolean): Promise<ObjectID> {
|
export async function add(
|
||||||
_.defaults(data, {
|
domainId: string, pid: number, uid: number,
|
||||||
|
lang: string, code: string, addTask: boolean, contest?: ContestInfo,
|
||||||
|
): Promise<ObjectID>
|
||||||
|
export async function add(
|
||||||
|
domainId: string, pid: number, uid: number,
|
||||||
|
lang: string, code: string, addTask: boolean, config: PretestConfig,
|
||||||
|
): Promise<ObjectID>
|
||||||
|
export async function add(
|
||||||
|
domainId: string, pid: number, uid: number,
|
||||||
|
lang: string, code: string, addTask: boolean, contestOrConfig?: ContestInfo | PretestConfig,
|
||||||
|
) {
|
||||||
|
const data: Rdoc = {
|
||||||
status: STATUS.STATUS_WAITING,
|
status: STATUS.STATUS_WAITING,
|
||||||
type: 'judge',
|
_id: new ObjectID(),
|
||||||
|
uid,
|
||||||
|
code,
|
||||||
|
lang,
|
||||||
|
pid,
|
||||||
domainId,
|
domainId,
|
||||||
score: 0,
|
score: 0,
|
||||||
time: 0,
|
time: 0,
|
||||||
memory: 0,
|
memory: 0,
|
||||||
hidden: false,
|
hidden: !!contestOrConfig,
|
||||||
judgeTexts: [],
|
judgeTexts: [],
|
||||||
compilerTexts: [],
|
compilerTexts: [],
|
||||||
testCases: [],
|
testCases: [],
|
||||||
judger: null,
|
judger: null,
|
||||||
judgeAt: null,
|
judgeAt: null,
|
||||||
});
|
rejudged: false,
|
||||||
|
};
|
||||||
|
if ((contestOrConfig as ContestInfo).type) {
|
||||||
|
// is contest
|
||||||
|
data.contest = contestOrConfig as ContestInfo;
|
||||||
|
} else {
|
||||||
|
data.config = contestOrConfig as PretestConfig;
|
||||||
|
}
|
||||||
const [pdoc, res] = await Promise.all([
|
const [pdoc, res] = await Promise.all([
|
||||||
problem.get(domainId, data.pid, null),
|
problem.get(domainId, pid, null),
|
||||||
coll.insertOne(data),
|
coll.insertOne(data),
|
||||||
]);
|
]);
|
||||||
if (addTask) {
|
if (addTask) {
|
||||||
const t: any = {
|
const t: any = {
|
||||||
type: data.type || 'judge',
|
type: 'judge',
|
||||||
rid: res.insertedId,
|
rid: res.insertedId,
|
||||||
domainId,
|
domainId,
|
||||||
pid: data.pid,
|
pid: data.pid,
|
||||||
@@ -78,11 +102,7 @@ export async function add(domainId: string, data: RdocBase, addTask: boolean): P
|
|||||||
t.data = pdoc.data;
|
t.data = pdoc.data;
|
||||||
t.config = pdoc.config;
|
t.config = pdoc.config;
|
||||||
} else {
|
} else {
|
||||||
t.config = yaml.safeDump({
|
t.config = yaml.safeDump(contestOrConfig);
|
||||||
time: data.time,
|
|
||||||
memory: data.memory,
|
|
||||||
input: data.input,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
await task.add(t);
|
await task.add(t);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hydrooj",
|
"name": "hydrooj",
|
||||||
"version": "2.9.13",
|
"version": "2.10.0",
|
||||||
"main": "dist/loader.js",
|
"main": "dist/loader.js",
|
||||||
"bin": "bin/hydrooj.js",
|
"bin": "bin/hydrooj.js",
|
||||||
"repository": "https://github.com/hydro-dev/Hydro.git",
|
"repository": "https://github.com/hydro-dev/Hydro.git",
|
||||||
|
|||||||
Reference in new issue
Block a user