mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 09:46:28 +08:00
judge: add translation api, add zh_CN.yaml
This commit is contained in:
1 parent
eec28d788a
commit
c16a1248a0
15 files changed
+88
-52
No files matched your search
@@ -23,6 +23,7 @@ module.exports = {
|
|||||||
'import/extensions': 'off',
|
'import/extensions': 'off',
|
||||||
'import/no-cycle': 'off',
|
'import/no-cycle': 'off',
|
||||||
'import/no-extraneous-dependencies': 'off',
|
'import/no-extraneous-dependencies': 'off',
|
||||||
|
'import/no-named-as-default': 'off',
|
||||||
'import/no-named-as-default-member': 'off',
|
'import/no-named-as-default-member': 'off',
|
||||||
'import/prefer-default-export': 'off',
|
'import/prefer-default-export': 'off',
|
||||||
indent: ['warn', 4],
|
indent: ['warn', 4],
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
'Error parsing memory: {0}': 无法解析内存:{0}
|
||||||
|
'Error parsing time: {0}': 无法解析时间:{0}
|
||||||
|
Cannot connect to sandbox service.: 无法连接到沙箱服务。
|
||||||
|
Cannot find checker {0}.: 找不到比较器 {0}。
|
||||||
|
Cannot find input file {0}.: 找不到输入文件 {0}。
|
||||||
|
Cannot find judge extra file {0}.: 找不到评测额外文件 {0}。
|
||||||
|
Cannot find output file {0}.: 找不到输出文件 {0}。
|
||||||
|
Cannot find user extra file {0}.: 找不到用户额外文件 {0}。
|
||||||
|
Cannot parse testdata.: 无法解析测试数据。
|
||||||
|
Checker returned {0}.: 比较器返回了{0}。
|
||||||
|
Executable file not found.: 没有找到可执行文件。
|
||||||
|
Found {0} testcases.: 识别到 {0} 个测试点。
|
||||||
|
Invalid judge_extra_files config.: 无效的 judge_extra_files 配置。
|
||||||
|
Invalid user_extra_files config.: 无效的 user_extra_files 配置。
|
||||||
|
Problem data not found.: 没有找到测试数据。
|
||||||
|
Read {0}, expect {1}.: 读取到 {0},应为 {1}。
|
||||||
|
Standard answer longer than user output.: 标准输出比选手输出长。
|
||||||
|
Testdata configuration incorrect.: 题目配置错误。
|
||||||
|
Too many files.: 文件过多。
|
||||||
|
Too many testcases. Cancelled.: 测试数据过多。评测取消。
|
||||||
|
Total time limit longer than 60s. Cancelled.: 总时限超过60s。评测取消。
|
||||||
|
Unknown checker type {0}.: 未知的比较器类型 {0}。
|
||||||
|
Unknown language type.: 未知的语言类型。
|
||||||
|
Unsupported language {0}.: 不支持的语言 {0}。
|
||||||
|
User output longer than standard answer.: 选手输出比标准输出长。
|
||||||
|
Your program returned {0}.: 您的程序返回了{0}。
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@hydrooj/hydrojudge",
|
"name": "@hydrooj/hydrojudge",
|
||||||
"bin": "bin/hydrojudge.js",
|
"bin": "bin/hydrojudge.js",
|
||||||
"version": "2.1.25",
|
"version": "2.1.26",
|
||||||
"main": "package.json",
|
"main": "package.json",
|
||||||
"author": "masnn",
|
"author": "masnn",
|
||||||
"repository": "https://github.com/hydro-dev/Hydro.git",
|
"repository": "https://github.com/hydro-dev/Hydro.git",
|
||||||
|
|||||||
@@ -186,9 +186,9 @@ async function readAutoCases(folder, { next }) {
|
|||||||
let result = await read0(folder, files, checkFile);
|
let result = await read0(folder, files, checkFile);
|
||||||
if (!result.count) result = await read1(folder, files, checkFile);
|
if (!result.count) result = await read1(folder, files, checkFile);
|
||||||
Object.assign(config, result);
|
Object.assign(config, result);
|
||||||
next({ message: `识别到${config.count}个测试点` });
|
next({ message: { message: 'Found {0} testcases.', params: config.count } });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new SystemError('在自动识别测试点的过程中出现了错误。', [e]);
|
throw new SystemError('Cannot parse testdata.', [e]);
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
@@ -204,24 +204,24 @@ export async function readYamlCases(folder: string, cfg: Dictionary<any> = {}, a
|
|||||||
const next = args.next;
|
const next = args.next;
|
||||||
const checkFile = ensureFile(folder);
|
const checkFile = ensureFile(folder);
|
||||||
config.checker_type = cfg.checker_type || 'default';
|
config.checker_type = cfg.checker_type || 'default';
|
||||||
if (cfg.checker) config.checker = checkFile(cfg.checker, '找不到比较器 ');
|
if (cfg.checker) config.checker = checkFile(cfg.checker, 'Cannot find checker {0}.');
|
||||||
if (cfg.judge_extra_files) {
|
if (cfg.judge_extra_files) {
|
||||||
if (typeof cfg.judge_extra_files === 'string') {
|
if (typeof cfg.judge_extra_files === 'string') {
|
||||||
config.judge_extra_files = [checkFile(cfg.judge_extra_files, '找不到评测额外文件 ')];
|
config.judge_extra_files = [checkFile(cfg.judge_extra_files, 'Cannot find judge extra file {0}.')];
|
||||||
} else if (cfg.judge_extra_files instanceof Array) {
|
} else if (cfg.judge_extra_files instanceof Array) {
|
||||||
for (const file of cfg.judge_extra_files) {
|
for (const file of cfg.judge_extra_files) {
|
||||||
config.judge_extra_files.push(checkFile(file, '找不到评测额外文件 '));
|
config.judge_extra_files.push(checkFile(file, 'Cannot find judge extra file {0}.'));
|
||||||
}
|
}
|
||||||
} else throw new FormatError('无效的 judge_extra_files 配置项');
|
} else throw new FormatError('Invalid judge_extra_files config.');
|
||||||
}
|
}
|
||||||
if (cfg.user_extra_files) {
|
if (cfg.user_extra_files) {
|
||||||
if (typeof cfg.user_extra_files === 'string') {
|
if (typeof cfg.user_extra_files === 'string') {
|
||||||
config.user_extra_files = [checkFile(cfg.user_extra_files, '找不到用户额外文件 ')];
|
config.user_extra_files = [checkFile(cfg.user_extra_files, 'Cannot find user extra file {0}.')];
|
||||||
} else if (cfg.user_extra_files instanceof Array) {
|
} else if (cfg.user_extra_files instanceof Array) {
|
||||||
for (const file of cfg.user_extra_files) {
|
for (const file of cfg.user_extra_files) {
|
||||||
config.user_extra_files.push(checkFile(file, '找不到用户额外文件 '));
|
config.user_extra_files.push(checkFile(file, 'Cannot find user extra file {0}.'));
|
||||||
}
|
}
|
||||||
} else throw new FormatError('无效的 user_extra_files 配置项');
|
} else throw new FormatError('Invalid user_extra_files config.');
|
||||||
}
|
}
|
||||||
if (cfg.cases) {
|
if (cfg.cases) {
|
||||||
config.subtasks = [{
|
config.subtasks = [{
|
||||||
@@ -234,8 +234,8 @@ export async function readYamlCases(folder: string, cfg: Dictionary<any> = {}, a
|
|||||||
for (const c of cfg.cases) {
|
for (const c of cfg.cases) {
|
||||||
config.count++;
|
config.count++;
|
||||||
config.subtasks[0].cases.push({
|
config.subtasks[0].cases.push({
|
||||||
input: checkFile(c.input, '找不到输入文件 '),
|
input: checkFile(c.input, 'Cannot find input file {0}.'),
|
||||||
output: checkFile(c.output, '找不到输出文件 '),
|
output: checkFile(c.output, 'Cannot find output file {0}.'),
|
||||||
id: config.count,
|
id: config.count,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -245,8 +245,8 @@ export async function readYamlCases(folder: string, cfg: Dictionary<any> = {}, a
|
|||||||
for (const c of subtask.cases) {
|
for (const c of subtask.cases) {
|
||||||
config.count++;
|
config.count++;
|
||||||
cases.push({
|
cases.push({
|
||||||
input: checkFile(c.input, '找不到输入文件 '),
|
input: checkFile(c.input, 'Cannot find input file {0}.'),
|
||||||
output: checkFile(c.output, '找不到输出文件 '),
|
output: checkFile(c.output, 'Cannot find output file {0}.'),
|
||||||
id: config.count,
|
id: config.count,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -284,12 +284,12 @@ function convertIniConfig(ini: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isValidConfig(config) {
|
function isValidConfig(config) {
|
||||||
if (config.count > 100) throw new FormatError('测试数据组数过多,拒绝评测');
|
if (config.count > 100) throw new FormatError('Too many testcases. Cancelled.');
|
||||||
let total_time = 0;
|
let total_time = 0;
|
||||||
for (const subtask of config.subtasks) {
|
for (const subtask of config.subtasks) {
|
||||||
total_time += subtask.time_limit_ms * subtask.cases.length;
|
total_time += subtask.time_limit_ms * subtask.cases.length;
|
||||||
}
|
}
|
||||||
if (total_time > 60 * 1000) throw new FormatError('总时限超过限制60s,拒绝评测');
|
if (total_time > 60 * 1000) throw new FormatError('Total time limit longer than 60s. Cancelled.');
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function readCases(folder: string, cfg: Record<string, any> = {}, args) {
|
export default async function readCases(folder: string, cfg: Record<string, any> = {}, args) {
|
||||||
|
|||||||
@@ -17,12 +17,12 @@ export async function check(config): Promise<[number, number, string]> {
|
|||||||
copyIn: config.copyIn || {},
|
copyIn: config.copyIn || {},
|
||||||
detail: config.detail,
|
detail: config.detail,
|
||||||
});
|
});
|
||||||
if (code) throw new SystemError(`比较器返回了非零值:${code}`);
|
if (code) throw new SystemError('Checker returned {0}.', [code]);
|
||||||
return [status, score, message];
|
return [status, score, message];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function compileChecker(checkerType: string, checker: string, copyIn: any) {
|
export async function compileChecker(checkerType: string, checker: string, copyIn: any) {
|
||||||
if (!checkers[checkerType]) throw new SystemError(`未知比较器类型:${checkerType}`);
|
if (!checkers[checkerType]) throw new SystemError('Unknown checker type {0}.', [checkerType]);
|
||||||
const file = await fs.readFile(checker);
|
const file = await fs.readFile(checker);
|
||||||
// TODO cache compiled checker
|
// TODO cache compiled checker
|
||||||
return await compile(parseFilename(checker).split('.')[1], file.toString(), 'checker', copyIn);
|
return await compile(parseFilename(checker).split('.')[1], file.toString(), 'checker', copyIn);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const checkers: Record<string, Checker> = {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
let status;
|
let status;
|
||||||
let message = '';
|
let message: any = '';
|
||||||
if (stdout) {
|
if (stdout) {
|
||||||
status = STATUS.STATUS_WRONG_ANSWER;
|
status = STATUS.STATUS_WRONG_ANSWER;
|
||||||
if (config.detail) {
|
if (config.detail) {
|
||||||
@@ -33,8 +33,8 @@ const checkers: Record<string, Checker> = {
|
|||||||
let usr = u.substr(2, u.length - 2).trim().split(' ');
|
let usr = u.substr(2, u.length - 2).trim().split(' ');
|
||||||
const t = pt[1].split('\n')[1];
|
const t = pt[1].split('\n')[1];
|
||||||
let std = t.substr(2, t.length - 2).trim().split(' ');
|
let std = t.substr(2, t.length - 2).trim().split(' ');
|
||||||
if (usr.length < std.length) message = '标准输出比选手输出长。';
|
if (usr.length < std.length) message = 'Standard answer longer than user output.';
|
||||||
else if (usr.length > std.length) message = '选手输出比标准输出长。';
|
else if (usr.length > std.length) message = 'User output longer than standard answer.';
|
||||||
else {
|
else {
|
||||||
for (const i in usr) {
|
for (const i in usr) {
|
||||||
if (usr[i] !== std[i]) {
|
if (usr[i] !== std[i]) {
|
||||||
@@ -45,7 +45,7 @@ const checkers: Record<string, Checker> = {
|
|||||||
}
|
}
|
||||||
if (usr.length > 20) usr = `${usr.substring(0, 16)}...`;
|
if (usr.length > 20) usr = `${usr.substring(0, 16)}...`;
|
||||||
if (std.length > 20) std = `${std.substring(0, 16)}...`;
|
if (std.length > 20) std = `${std.substring(0, 16)}...`;
|
||||||
message = `读取到 ${usr} ,应为 ${std}`;
|
message = { message: 'Read {0}, expect {1}.', params: [usr, std] };
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
message = stdout.substring(0, stdout.length - 1 <= 30 ? stdout.length - 1 : 30);
|
message = stdout.substring(0, stdout.length - 1 <= 30 ? stdout.length - 1 : 30);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export = async function compile(
|
|||||||
const LANGS = global.Hydro
|
const LANGS = global.Hydro
|
||||||
? yaml.safeLoad(await global.Hydro.model.system.get('hydrojudge.langs'))
|
? yaml.safeLoad(await global.Hydro.model.system.get('hydrojudge.langs'))
|
||||||
: require('./config').LANGS;
|
: require('./config').LANGS;
|
||||||
if (!LANGS[lang]) throw new SystemError(`不支持的语言:${lang}`);
|
if (!LANGS[lang]) throw new SystemError('Unsupported language {0}.', [lang]);
|
||||||
const info = LANGS[lang];
|
const info = LANGS[lang];
|
||||||
target = info.target || target;
|
target = info.target || target;
|
||||||
const f = {};
|
const f = {};
|
||||||
@@ -24,7 +24,7 @@ export = async function compile(
|
|||||||
{ copyIn, copyOutCached: [target] },
|
{ copyIn, copyOutCached: [target] },
|
||||||
);
|
);
|
||||||
if (status !== STATUS.STATUS_ACCEPTED) throw new CompileError({ status, stdout, stderr });
|
if (status !== STATUS.STATUS_ACCEPTED) throw new CompileError({ status, stdout, stderr });
|
||||||
if (!fileIds[target]) throw new CompileError({ stderr: '没有找到可执行文件' });
|
if (!fileIds[target]) throw new CompileError({ stderr: 'Executable file not found.' });
|
||||||
if (next) next({ compiler_text: compilerText(stdout, stderr) });
|
if (next) next({ compiler_text: compilerText(stdout, stderr) });
|
||||||
f[target] = { fileId: fileIds[target] };
|
f[target] = { fileId: fileIds[target] };
|
||||||
return {
|
return {
|
||||||
@@ -36,5 +36,5 @@ export = async function compile(
|
|||||||
execute: info.execute, copyIn: f, clean: () => Promise.resolve(), time: info.time || 1,
|
execute: info.execute, copyIn: f, clean: () => Promise.resolve(), time: info.time || 1,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
throw new SystemError('Unknown language type');
|
throw new SystemError('Unknown language type.');
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ import axios from 'axios';
|
|||||||
import AdmZip from 'adm-zip';
|
import AdmZip from 'adm-zip';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import WebSocket from 'ws';
|
import WebSocket from 'ws';
|
||||||
|
import { argv } from 'yargs';
|
||||||
import { noop } from 'lodash';
|
import { noop } from 'lodash';
|
||||||
import { ObjectID } from 'bson';
|
import { ObjectID } from 'bson';
|
||||||
import * as tmpfs from '../tmpfs';
|
import * as tmpfs from '../tmpfs';
|
||||||
@@ -89,14 +90,14 @@ class JudgeTask {
|
|||||||
status: STATUS_COMPILE_ERROR, score: 0, time_ms: 0, memory_kb: 0,
|
status: STATUS_COMPILE_ERROR, score: 0, time_ms: 0, memory_kb: 0,
|
||||||
});
|
});
|
||||||
} else if (e instanceof FormatError) {
|
} else if (e instanceof FormatError) {
|
||||||
this.next({ judge_text: '题目配置出现错误。请联系题目上传者。' });
|
this.next({ judge_text: 'Testdata configuration incorrect.' });
|
||||||
this.next({ judge_text: `${e.message}\n${JSON.stringify(e.params)}` });
|
this.next({ judge_text: { message: e.message, params: e.params } });
|
||||||
this.end({
|
this.end({
|
||||||
status: STATUS_SYSTEM_ERROR, score: 0, time_ms: 0, memory_kb: 0,
|
status: STATUS_SYSTEM_ERROR, score: 0, time_ms: 0, memory_kb: 0,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
log.error(e);
|
log.error(e);
|
||||||
this.next({ judge_text: `${e.message}\n${e.stack}\n${JSON.stringify(e.params)}` });
|
this.next({ message: { message: e.message, params: e.params, ...argv.debug ? { stack: e.stack } : {} } });
|
||||||
this.end({
|
this.end({
|
||||||
status: STATUS_SYSTEM_ERROR, score: 0, time_ms: 0, memory_kb: 0,
|
status: STATUS_SYSTEM_ERROR, score: 0, time_ms: 0, memory_kb: 0,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -134,10 +134,16 @@ class JudgeTask {
|
|||||||
data.judge_text = data.message;
|
data.judge_text = data.message;
|
||||||
delete data.message;
|
delete data.message;
|
||||||
}
|
}
|
||||||
|
if (data.judge_text && typeof data.judge_text !== 'string') {
|
||||||
|
data.judge_text = `${data.judge_text.message} ${JSON.stringify(data.judge_text.params || {})}`;
|
||||||
|
}
|
||||||
if (data.case?.message !== undefined) {
|
if (data.case?.message !== undefined) {
|
||||||
data.case.judge_text = data.case.message;
|
data.case.judge_text = data.case.message;
|
||||||
delete data.case.message;
|
delete data.case.message;
|
||||||
}
|
}
|
||||||
|
if (data.case?.judge_text && typeof data.case?.judge_text !== 'string') {
|
||||||
|
data.case.judge_text = `${data.case.judge_text.message} ${JSON.stringify(data.case.judge_text.params || {})}`;
|
||||||
|
}
|
||||||
data.key = 'next';
|
data.key = 'next';
|
||||||
data.tag = this.tag;
|
data.tag = this.tag;
|
||||||
if (id) {
|
if (id) {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ function judgeCase(c) {
|
|||||||
if (res.files[`${filename}.out`] || !fs.existsSync(stdout)) {
|
if (res.files[`${filename}.out`] || !fs.existsSync(stdout)) {
|
||||||
fs.writeFileSync(stdout, res.files[`${filename}.out`] || '');
|
fs.writeFileSync(stdout, res.files[`${filename}.out`] || '');
|
||||||
}
|
}
|
||||||
let message = '';
|
let message: any = '';
|
||||||
let score = 0;
|
let score = 0;
|
||||||
if (status === STATUS.STATUS_ACCEPTED) {
|
if (status === STATUS.STATUS_ACCEPTED) {
|
||||||
if (time_usage_ms > ctxSubtask.subtask.time_limit_ms * ctx.execute.time) {
|
if (time_usage_ms > ctxSubtask.subtask.time_limit_ms * ctx.execute.time) {
|
||||||
@@ -64,7 +64,7 @@ function judgeCase(c) {
|
|||||||
}
|
}
|
||||||
} else if (status === STATUS.STATUS_RUNTIME_ERROR && code) {
|
} else if (status === STATUS.STATUS_RUNTIME_ERROR && code) {
|
||||||
if (code < 32) message = signals[code];
|
if (code < 32) message = signals[code];
|
||||||
else message = `您的程序返回了 ${code}.`;
|
else message = { message: 'Your program returned {0}.', params: [code] };
|
||||||
}
|
}
|
||||||
ctxSubtask.score = Score[ctxSubtask.subtask.type](ctxSubtask.score, score);
|
ctxSubtask.score = Score[ctxSubtask.subtask.type](ctxSubtask.score, score);
|
||||||
ctxSubtask.status = Math.max(ctxSubtask.status, status);
|
ctxSubtask.status = Math.max(ctxSubtask.status, status);
|
||||||
@@ -105,7 +105,7 @@ function judgeSubtask(subtask) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const judge = async (ctx) => {
|
export const judge = async (ctx) => {
|
||||||
if (!ctx.config.subtasks.length) throw new SystemError('没有找到测试数据');
|
if (!ctx.config.subtasks.length) throw new SystemError('Problem data not found.');
|
||||||
if (ctx.config.template) {
|
if (ctx.config.template) {
|
||||||
if (ctx.config.template[ctx.lang]) {
|
if (ctx.config.template[ctx.lang]) {
|
||||||
const tpl = ctx.config.template[ctx.lang];
|
const tpl = ctx.config.template[ctx.lang];
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ function judgeCase(c) {
|
|||||||
]);
|
]);
|
||||||
let status;
|
let status;
|
||||||
let score = 0;
|
let score = 0;
|
||||||
let message = '';
|
let message: any = '';
|
||||||
if (time_usage_ms > ctxSubtask.subtask.time_limit_ms) {
|
if (time_usage_ms > ctxSubtask.subtask.time_limit_ms) {
|
||||||
status = STATUS.STATUS_TIME_LIMIT_EXCEEDED;
|
status = STATUS.STATUS_TIME_LIMIT_EXCEEDED;
|
||||||
} else if (memory_usage_kb > ctxSubtask.subtask.memory_limit_mb * 1024) {
|
} else if (memory_usage_kb > ctxSubtask.subtask.memory_limit_mb * 1024) {
|
||||||
@@ -38,7 +38,7 @@ function judgeCase(c) {
|
|||||||
} else if (code) {
|
} else if (code) {
|
||||||
status = STATUS.STATUS_RUNTIME_ERROR;
|
status = STATUS.STATUS_RUNTIME_ERROR;
|
||||||
if (code < 32) message = signals[code];
|
if (code < 32) message = signals[code];
|
||||||
else message = `您的程序返回了 ${code}.`;
|
else message = { message: 'Your program returned {0}.', params: [code] };
|
||||||
} else [status, score, message] = resInteractor.files.stderr.split('\n');
|
} else [status, score, message] = resInteractor.files.stderr.split('\n');
|
||||||
ctxSubtask.score = Score[ctxSubtask.subtask.type](ctxSubtask.score, score);
|
ctxSubtask.score = Score[ctxSubtask.subtask.type](ctxSubtask.score, score);
|
||||||
ctxSubtask.status = Math.max(ctxSubtask.status, status);
|
ctxSubtask.status = Math.max(ctxSubtask.status, status);
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export const judge = async (ctx) => {
|
|||||||
const { code, time_usage_ms, memory_usage_kb } = res;
|
const { code, time_usage_ms, memory_usage_kb } = res;
|
||||||
let { status } = res;
|
let { status } = res;
|
||||||
if (!fs.existsSync(stdout)) fs.writeFileSync(stdout, '');
|
if (!fs.existsSync(stdout)) fs.writeFileSync(stdout, '');
|
||||||
let message = fs.readFileSync(stdout).toString() + fs.readFileSync(stderr).toString();
|
let message: any = fs.readFileSync(stdout).toString() + fs.readFileSync(stderr).toString();
|
||||||
if (status === STATUS.STATUS_ACCEPTED) {
|
if (status === STATUS.STATUS_ACCEPTED) {
|
||||||
if (time_usage_ms > ctx.config.time) {
|
if (time_usage_ms > ctx.config.time) {
|
||||||
status = STATUS.STATUS_TIME_LIMIT_EXCEEDED;
|
status = STATUS.STATUS_TIME_LIMIT_EXCEEDED;
|
||||||
@@ -41,7 +41,7 @@ export const judge = async (ctx) => {
|
|||||||
} else if (code) {
|
} else if (code) {
|
||||||
status = STATUS.STATUS_RUNTIME_ERROR;
|
status = STATUS.STATUS_RUNTIME_ERROR;
|
||||||
if (code < 32) message += signals[code];
|
if (code < 32) message += signals[code];
|
||||||
else message += `您的程序返回了 ${code}.`;
|
else message = { message: 'Your program returned {0}.', params: [code] };
|
||||||
}
|
}
|
||||||
ctx.next({
|
ctx.next({
|
||||||
status,
|
status,
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export async function runMultiple(execute) {
|
|||||||
body.cmd[1].files[1] = null;
|
body.cmd[1].files[1] = null;
|
||||||
res = await axios.post('/run', body);
|
res = await axios.post('/run', body);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new SystemError('Cannot connect to sandbox service');
|
throw new SystemError('Cannot connect to sandbox service.');
|
||||||
}
|
}
|
||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ export async function run(execute, params?) {
|
|||||||
[result] = res.data;
|
[result] = res.data;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// FIXME request body larger than maxBodyLength limit
|
// FIXME request body larger than maxBodyLength limit
|
||||||
throw new SystemError('Cannot connect to sandbox service ', e.message);
|
throw new SystemError('Cannot connect to sandbox service.', e.message);
|
||||||
}
|
}
|
||||||
// FIXME: Signalled?
|
// FIXME: Signalled?
|
||||||
const ret: any = {
|
const ret: any = {
|
||||||
|
|||||||
@@ -2,11 +2,19 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
import 'hydrooj';
|
import 'hydrooj';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import { argv } from 'yargs';
|
||||||
import { ObjectID } from 'bson';
|
import { ObjectID } from 'bson';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import { homedir, tmpdir } from 'os';
|
import { homedir, tmpdir } from 'os';
|
||||||
import AdmZip from 'adm-zip';
|
import AdmZip from 'adm-zip';
|
||||||
import { noop } from 'lodash';
|
import { noop } from 'lodash';
|
||||||
|
import { Logger } from 'hydrooj/dist/logger';
|
||||||
|
import * as monitor from 'hydrooj/dist/service/monitor';
|
||||||
|
import * as tmpfs from './tmpfs';
|
||||||
|
import { FormatError, CompileError, SystemError } from './error';
|
||||||
|
import { STATUS_COMPILE_ERROR, STATUS_SYSTEM_ERROR } from './status';
|
||||||
|
import { compilerText } from './utils';
|
||||||
|
import readYamlCases from './cases';
|
||||||
|
|
||||||
declare module 'hydrooj/dist/interface' {
|
declare module 'hydrooj/dist/interface' {
|
||||||
interface SystemKeys {
|
interface SystemKeys {
|
||||||
@@ -14,22 +22,16 @@ declare module 'hydrooj/dist/interface' {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const logger = new Logger('judge');
|
||||||
|
|
||||||
async function postInit() {
|
async function postInit() {
|
||||||
// Only start a single daemon
|
// Only start a single daemon
|
||||||
if (!global.Hydro.isFirstWorker) return;
|
if (!global.Hydro.isFirstWorker) return;
|
||||||
const { compilerText } = require('./utils');
|
|
||||||
const tmpfs = require('./tmpfs');
|
|
||||||
const { FormatError, CompileError, SystemError } = require('./error');
|
|
||||||
const { STATUS_COMPILE_ERROR, STATUS_SYSTEM_ERROR } = global.Hydro.model.builtin.STATUS;
|
|
||||||
const { default: readYamlCases } = require('./cases');
|
|
||||||
const judge = require('./judge');
|
const judge = require('./judge');
|
||||||
const sysinfo = require('./sysinfo');
|
const sysinfo = require('./sysinfo');
|
||||||
|
|
||||||
const { problem, file, task } = global.Hydro.model;
|
const { problem, file, task } = global.Hydro.model;
|
||||||
const _judge = global.Hydro.handler.judge as any;
|
const _judge = global.Hydro.handler.judge as any;
|
||||||
const { monitor } = global.Hydro.service;
|
|
||||||
const { Logger } = global.Hydro.Logger;
|
|
||||||
const logger = new Logger('judge');
|
|
||||||
|
|
||||||
const info = await sysinfo.get();
|
const info = await sysinfo.get();
|
||||||
monitor.updateJudger(info);
|
monitor.updateJudger(info);
|
||||||
@@ -76,7 +78,7 @@ async function postInit() {
|
|||||||
fs.ensureDirSync(path.dirname(savePath));
|
fs.ensureDirSync(path.dirname(savePath));
|
||||||
const zip = new AdmZip(tmpFilePath);
|
const zip = new AdmZip(tmpFilePath);
|
||||||
const entries = zip.getEntries();
|
const entries = zip.getEntries();
|
||||||
if (entries.length > 256) throw new FormatError('Too many files');
|
if (entries.length > 256) throw new FormatError('Too many files.');
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
zip.extractAllToAsync(savePath, true, (e) => {
|
zip.extractAllToAsync(savePath, true, (e) => {
|
||||||
if (e) reject(e);
|
if (e) reject(e);
|
||||||
@@ -227,13 +229,13 @@ async function postInit() {
|
|||||||
status: STATUS_COMPILE_ERROR, score: 0, time_ms: 0, memory_kb: 0,
|
status: STATUS_COMPILE_ERROR, score: 0, time_ms: 0, memory_kb: 0,
|
||||||
});
|
});
|
||||||
} else if (e instanceof FormatError) {
|
} else if (e instanceof FormatError) {
|
||||||
this.next({ message: `${e.message}\n${JSON.stringify(e.params)}` });
|
this.next({ message: { message: e.message, params: e.params } });
|
||||||
this.end({
|
this.end({
|
||||||
status: STATUS_SYSTEM_ERROR, score: 0, time_ms: 0, memory_kb: 0,
|
status: STATUS_SYSTEM_ERROR, score: 0, time_ms: 0, memory_kb: 0,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
logger.error(e);
|
logger.error(e);
|
||||||
this.next({ message: `${e.message}\n${e.stack}\n${JSON.stringify(e.params)}` });
|
this.next({ message: { message: e.message, params: e.params, ...argv.debug ? { stack: e.stack } : {} } });
|
||||||
this.end({
|
this.end({
|
||||||
status: STATUS_SYSTEM_ERROR, score: 0, time_ms: 0, memory_kb: 0,
|
status: STATUS_SYSTEM_ERROR, score: 0, time_ms: 0, memory_kb: 0,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ export function noop() { }
|
|||||||
|
|
||||||
export function parseTimeMS(str: string) {
|
export function parseTimeMS(str: string) {
|
||||||
const match = TIME_RE.exec(str);
|
const match = TIME_RE.exec(str);
|
||||||
if (!match) throw new FormatError(str, ['error parsing time']);
|
if (!match) throw new FormatError('Error parsing time: {0}', [str]);
|
||||||
return Math.floor(parseFloat(match[1]) * TIME_UNITS[match[2]]);
|
return Math.floor(parseFloat(match[1]) * TIME_UNITS[match[2]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseMemoryMB(str: string) {
|
export function parseMemoryMB(str: string) {
|
||||||
const match = MEMORY_RE.exec(str);
|
const match = MEMORY_RE.exec(str);
|
||||||
if (!match) throw new FormatError(str, ['error parsing memory']);
|
if (!match) throw new FormatError('Error parsing memory: {0}', [str]);
|
||||||
return Math.floor(parseFloat(match[1]) * MEMORY_UNITS[match[2]]);
|
return Math.floor(parseFloat(match[1]) * MEMORY_UNITS[match[2]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,9 +110,9 @@ export function restrictFile(p: string) {
|
|||||||
export function ensureFile(folder: string) {
|
export function ensureFile(folder: string) {
|
||||||
return (file: string, message: string) => {
|
return (file: string, message: string) => {
|
||||||
const f = path.join(folder, restrictFile(file));
|
const f = path.join(folder, restrictFile(file));
|
||||||
if (!fs.existsSync(f)) throw new FormatError(message + file);
|
if (!fs.existsSync(f)) throw new FormatError(message, [file]);
|
||||||
const stat = fs.statSync(f);
|
const stat = fs.statSync(f);
|
||||||
if (!stat.isFile()) throw new FormatError(message + file);
|
if (!stat.isFile()) throw new FormatError(message, [file]);
|
||||||
return f;
|
return f;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user