Browse Source

Handle finished judge reports at web side

pull/6/head
hewenyang 7 years ago
parent
commit
f149f5656b
  1. 78
      libs/judgeResult.js
  2. 51
      libs/judger.js
  3. 53
      libs/judger_interfaces.js
  4. 49
      modules/api_v2.js
  5. 23
      modules/winston.js

78
libs/judgeResult.js

@ -0,0 +1,78 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const _ = require("lodash");
const winston = require("winston");
const interfaces_1 = require("./judger_interfaces");
const compileError = "Compile Error", systemError = "System Error", testdataError = "No Testdata";
exports.statusToString = {};
exports.statusToString[interfaces_1.TestcaseResultType.Accepted] = "Accepted";
exports.statusToString[interfaces_1.TestcaseResultType.WrongAnswer] = "Wrong Answer";
exports.statusToString[interfaces_1.TestcaseResultType.PartiallyCorrect] = "Partially Correct";
exports.statusToString[interfaces_1.TestcaseResultType.MemoryLimitExceeded] = "Memory Limit Exceeded";
exports.statusToString[interfaces_1.TestcaseResultType.TimeLimitExceeded] = "Time Limit Exceeded";
exports.statusToString[interfaces_1.TestcaseResultType.OutputLimitExceeded] = "Output Limit Exceeded";
exports.statusToString[interfaces_1.TestcaseResultType.RuntimeError] = "Runtime Error";
exports.statusToString[interfaces_1.TestcaseResultType.FileError] = "File Error";
exports.statusToString[interfaces_1.TestcaseResultType.JudgementFailed] = "Judgement Failed";
exports.statusToString[interfaces_1.TestcaseResultType.InvalidInteraction] = "Invalid Interaction";
function firstNonAC(t) {
if (t.every(v => v === interfaces_1.TestcaseResultType.Accepted)) {
return interfaces_1.TestcaseResultType.Accepted;
}
else {
return t.find(r => r !== interfaces_1.TestcaseResultType.Accepted);
}
}
exports.firstNonAC = firstNonAC;
function convertResult(taskId, source) {
winston.debug(`Converting result for ${taskId}`, source);
let time = null, memory = null, score = null, done = true, statusString = null;
if (source.compile && source.compile.status === interfaces_1.TaskStatus.Failed) {
statusString = compileError;
}
else if (source.error != null) {
done = false;
if (source.error === interfaces_1.ErrorType.TestDataError) {
statusString = testdataError;
}
else {
statusString = systemError;
}
}
else if (source.judge != null && source.judge.subtasks != null) {
const forEveryTestcase = function (map, reduce) {
const list = source.judge.subtasks.map(s => reduce(s.cases.filter(c => c.result != null).map(c => map(c.result))));
if (list.every(x => x == null))
return null;
else
return reduce(list);
};
time = forEveryTestcase(c => c.time, _.sum);
memory = forEveryTestcase(c => c.memory, _.max);
if (source.judge.subtasks.some(s => s.cases.some(c => c.status === interfaces_1.TaskStatus.Failed))) {
winston.debug(`Some subtasks failed, returning system error`);
statusString = systemError;
}
else {
score = _.sum(source.judge.subtasks.map(s => s.score));
const finalResult = forEveryTestcase(c => c.type, firstNonAC);
statusString = exports.statusToString[finalResult];
}
}
else {
statusString = systemError;
}
const result = {
taskId: taskId,
time: time,
memory: memory,
score: score,
statusNumber: done ? interfaces_1.TaskStatus.Done : interfaces_1.TaskStatus.Failed,
statusString: statusString,
result: source
};
winston.debug(`Result for ${taskId}`, result);
return result;
}
exports.convertResult = convertResult;
//# sourceMappingURL=judgeResult.js.map

51
libs/judger.js

@ -6,29 +6,68 @@ const amqp = require('amqplib');
const util = require('util'); const util = require('util');
const winston = require('winston'); const winston = require('winston');
const msgPack = require('msgpack-lite'); const msgPack = require('msgpack-lite');
const interface = require('./judger_interfaces');
const judgeResult = require('./judgeResult');
let amqpConnection; let amqpConnection;
let publicChannel; let amqpSendChannel;
let amqpConsumeChannel;
async function connect () { async function connect () {
amqpConnection = await amqp.connect(syzoj.config.rabbitMQ); amqpConnection = await amqp.connect(syzoj.config.rabbitMQ);
publicChannel = await amqpConnection.createChannel(); amqpSendChannel = await amqpConnection.createChannel();
await publicChannel.assertQueue('judge', { await amqpSendChannel.assertQueue('judge', {
maxPriority: 5, maxPriority: 5,
durable: true durable: true
}); });
await publicChannel.assertQueue('result', { await amqpSendChannel.assertQueue('result', {
durable: true durable: true
}); });
await publicChannel.assertExchange('progress', 'fanout', { await amqpSendChannel.assertExchange('progress', 'fanout', {
durable: false durable: false
}); });
amqpConsumeChannel = await amqpConnection.createChannel();
amqpConsumeChannel.prefetch(1);
winston.debug('Winston test');
amqpConsumeChannel.consume('result', async (msg) => {
(async(msg) => {
const data = msgPack.decode(msg.content);
winston.verbose('Received report for task ' + data.taskId);
let JudgeState = syzoj.model('judge_state');
let judge_state = await JudgeState.findOne({ where: { task_id: data.taskId } });
if(data.type === interface.ProgressReportType.Finished) {
const convertedResult = judgeResult.convertResult(data.taskId, data.progress);
winston.verbose('Reporting report finished: ' + data.taskId);
const payload = msgPack.encode({ type: interface.ProgressReportType.Reported, taskId: data.taskId });
amqpSendChannel.publish('progress', '', payload);
if(!judge_state) return;
judge_state.score = convertedResult.score;
judge_state.pending = false;
judge_state.status = convertedResult.statusString;
judge_state.total_time = convertedResult.time;
judge_state.max_memory = convertedResult.memory;
judge_state.result = convertedResult.result;
await judge_state.save();
await judge_state.updateRelatedInfo();
} else {
if(!judge_state) return;
judge_state.compilation = data.progress;
await judge_state.save();
}
})(msg).then(async() => {
amqpConsumeChannel.ack(msg)
}, async(err) => {
winston.error('Error handling report', err);
amqpConsumeChannel.nack(msg, false, false);
});
});
amqpConnection.on('error', (err) => { amqpConnection.on('error', (err) => {
winston.error('RabbitMQ connection failure: ${err.toString()}'); winston.error('RabbitMQ connection failure: ${err.toString()}');
amqpConnection.close(); amqpConnection.close();
process.exit(1); process.exit(1);
}); });
} }
module.exports.judge = async function (judge_state, problem, priority) { module.exports.judge = async function (judge_state, problem, priority) {
let type, param, extraFile = null; let type, param, extraFile = null;
switch (problem.type) { switch (problem.type) {
@ -71,7 +110,7 @@ module.exports.judge = async function (judge_state, problem, priority) {
}; };
// TODO: parse extraFileLocation // TODO: parse extraFileLocation
publicChannel.sendToQueue('judge', msgPack.encode({ content: req.content, extraData: null }), { priority: priority }); amqpSendChannel.sendToQueue('judge', msgPack.encode({ content: req.content, extraData: null }), { priority: priority });
} }
connect(); connect();

53
libs/judger_interfaces.js

@ -0,0 +1,53 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var RPCTaskType;
(function (RPCTaskType) {
RPCTaskType[RPCTaskType["Compile"] = 1] = "Compile";
RPCTaskType[RPCTaskType["RunStandard"] = 2] = "RunStandard";
RPCTaskType[RPCTaskType["RunSubmitAnswer"] = 3] = "RunSubmitAnswer";
RPCTaskType[RPCTaskType["RunInteraction"] = 4] = "RunInteraction";
})(RPCTaskType = exports.RPCTaskType || (exports.RPCTaskType = {}));
;
var ErrorType;
(function (ErrorType) {
ErrorType[ErrorType["SystemError"] = 0] = "SystemError";
ErrorType[ErrorType["TestDataError"] = 1] = "TestDataError";
})(ErrorType = exports.ErrorType || (exports.ErrorType = {}));
var TaskStatus;
(function (TaskStatus) {
TaskStatus[TaskStatus["Waiting"] = 0] = "Waiting";
TaskStatus[TaskStatus["Running"] = 1] = "Running";
TaskStatus[TaskStatus["Done"] = 2] = "Done";
TaskStatus[TaskStatus["Failed"] = 3] = "Failed";
TaskStatus[TaskStatus["Skipped"] = 4] = "Skipped";
})(TaskStatus = exports.TaskStatus || (exports.TaskStatus = {}));
var TestcaseResultType;
(function (TestcaseResultType) {
TestcaseResultType[TestcaseResultType["Accepted"] = 1] = "Accepted";
TestcaseResultType[TestcaseResultType["WrongAnswer"] = 2] = "WrongAnswer";
TestcaseResultType[TestcaseResultType["PartiallyCorrect"] = 3] = "PartiallyCorrect";
TestcaseResultType[TestcaseResultType["MemoryLimitExceeded"] = 4] = "MemoryLimitExceeded";
TestcaseResultType[TestcaseResultType["TimeLimitExceeded"] = 5] = "TimeLimitExceeded";
TestcaseResultType[TestcaseResultType["OutputLimitExceeded"] = 6] = "OutputLimitExceeded";
TestcaseResultType[TestcaseResultType["FileError"] = 7] = "FileError";
TestcaseResultType[TestcaseResultType["RuntimeError"] = 8] = "RuntimeError";
TestcaseResultType[TestcaseResultType["JudgementFailed"] = 9] = "JudgementFailed";
TestcaseResultType[TestcaseResultType["InvalidInteraction"] = 10] = "InvalidInteraction";
})(TestcaseResultType = exports.TestcaseResultType || (exports.TestcaseResultType = {}));
var RPCReplyType;
(function (RPCReplyType) {
RPCReplyType[RPCReplyType["Started"] = 1] = "Started";
RPCReplyType[RPCReplyType["Finished"] = 2] = "Finished";
RPCReplyType[RPCReplyType["Error"] = 3] = "Error";
})(RPCReplyType = exports.RPCReplyType || (exports.RPCReplyType = {}));
var ProgressReportType;
(function (ProgressReportType) {
ProgressReportType[ProgressReportType["Started"] = 1] = "Started";
ProgressReportType[ProgressReportType["Compiled"] = 2] = "Compiled";
ProgressReportType[ProgressReportType["Progress"] = 3] = "Progress";
ProgressReportType[ProgressReportType["Finished"] = 4] = "Finished";
ProgressReportType[ProgressReportType["Reported"] = 5] = "Reported";
})(ProgressReportType = exports.ProgressReportType || (exports.ProgressReportType = {}));
exports.redisBinarySuffix = '-bin';
exports.redisMetadataSuffix = '-meta';
//# sourceMappingURL=interfaces.js.map

49
modules/api_v2.js

@ -111,52 +111,3 @@ app.apiRouter.post('/api/v2/markdown', async (req, res) => {
res.send(e); res.send(e);
} }
}); });
app.apiRouter.post('/api/v2/judge/compiled', async (req, res) => {
try {
if (req.get('Token') !== syzoj.config.judge_token) return res.status(403).send({ err: 'Incorrect token' });
let data = req.body;
let JudgeState = syzoj.model('judge_state');
let judge_state = await JudgeState.findOne({ where: { task_id: req.body.taskId } });
if (!judge_state) {
res.send({ return: 1 }); // The task might have been rejudging.
return;
}
judge_state.compilation = req.body.result;
await judge_state.save();
res.send({ return: 0 });
} catch (e) {
syzoj.log(e);
res.status(500).send(e);
}
});
app.apiRouter.post('/api/v2/judge/finished', async (req, res) => {
try {
if (req.get('Token') !== syzoj.config.judge_token) return res.status(403).send({ err: 'Incorrect token' });
let data = req.body;
let JudgeState = syzoj.model('judge_state');
let judge_state = await JudgeState.findOne({ where: { task_id: req.body.taskId } });
if (!judge_state) {
res.send({ return: 1 }); // The task might have been rejudging.
return;
}
// await judge_state.updateResult(JSON.parse(req.body));
judge_state.score = data.score;
judge_state.pending = false;
judge_state.status = data.statusString;
judge_state.total_time = data.time;
judge_state.max_memory = data.memory;
judge_state.result = data.result;
await judge_state.save();
await judge_state.updateRelatedInfo();
res.send({ return: 0 });
} catch (e) {
syzoj.log(e);
res.status(500).send(e);
}
});

23
modules/winston.js

@ -0,0 +1,23 @@
const winston = require('winston');
const _ = require('lodash');
const util = require('util');
function formatter(args) {
var msg = args.level + ' - ' + args.message + (_.isEmpty(args.meta) ? '' : (' - ' + util.inspect(args.meta)));
return msg;
}
function configureWinston(verbose) {
winston.configure({
transports: [
new (winston.transports.Console)({ formatter: formatter })
]
});
if (verbose) {
winston.level = 'debug';
} else {
winston.level = 'info';
}
}
configureWinston(false);
Loading…
Cancel
Save