root
6 years ago
12 changed files with 1265 additions and 1022 deletions
@ -0,0 +1,78 @@
|
||||
"use strict"; |
||||
Object.defineProperty(exports, "__esModule", { value: true }); |
||||
const _ = require("lodash"); |
||||
const winston = require("winston"); |
||||
const interfaces = require("./judger_interfaces"); |
||||
const compileError = "Compile Error", systemError = "System Error", testdataError = "No Testdata"; |
||||
exports.statusToString = {}; |
||||
exports.statusToString[interfaces.TestcaseResultType.Accepted] = "Accepted"; |
||||
exports.statusToString[interfaces.TestcaseResultType.WrongAnswer] = "Wrong Answer"; |
||||
exports.statusToString[interfaces.TestcaseResultType.PartiallyCorrect] = "Partially Correct"; |
||||
exports.statusToString[interfaces.TestcaseResultType.MemoryLimitExceeded] = "Memory Limit Exceeded"; |
||||
exports.statusToString[interfaces.TestcaseResultType.TimeLimitExceeded] = "Time Limit Exceeded"; |
||||
exports.statusToString[interfaces.TestcaseResultType.OutputLimitExceeded] = "Output Limit Exceeded"; |
||||
exports.statusToString[interfaces.TestcaseResultType.RuntimeError] = "Runtime Error"; |
||||
exports.statusToString[interfaces.TestcaseResultType.FileError] = "File Error"; |
||||
exports.statusToString[interfaces.TestcaseResultType.JudgementFailed] = "Judgement Failed"; |
||||
exports.statusToString[interfaces.TestcaseResultType.InvalidInteraction] = "Invalid Interaction"; |
||||
function firstNonAC(t) { |
||||
if (t.every(v => v === interfaces.TestcaseResultType.Accepted)) { |
||||
return interfaces.TestcaseResultType.Accepted; |
||||
} |
||||
else { |
||||
return t.find(r => r !== interfaces.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.TaskStatus.Failed) { |
||||
statusString = compileError; |
||||
} |
||||
else if (source.error != null) { |
||||
done = false; |
||||
if (source.error === interfaces.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 ? c.time : 0), _.sum); |
||||
memory = forEveryTestcase(c => (c.memory ? c.memory : 0), _.max); |
||||
if (source.judge.subtasks.some(s => s.cases.some(c => c.status === interfaces.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.TaskStatus.Done : interfaces.TaskStatus.Failed, |
||||
statusString: statusString, |
||||
result: source |
||||
}; |
||||
winston.debug(`Result for ${taskId}`, result); |
||||
return result; |
||||
} |
||||
exports.convertResult = convertResult; |
||||
//# sourceMappingURL=judgeResult.js.map
|
@ -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
|
@ -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'; |
||||
} |
||||
} |
||||
|
||||
module.exports.configureWinston = configureWinston; |
@ -0,0 +1,298 @@
|
||||
"use strict"; |
||||
Object.defineProperty(exports, "__esModule", { value: true }); |
||||
const socketio = require("socket.io"); |
||||
const diff = require("jsondiffpatch"); |
||||
const jwt = require("jsonwebtoken"); |
||||
const winston = require("winston"); |
||||
const judgeResult = require("../libs/judgeResult"); |
||||
const interfaces = require("../libs/judger_interfaces"); |
||||
let ioInstance; |
||||
let detailProgressNamespace; |
||||
let roughProgressNamespace; |
||||
let compileProgressNamespace; |
||||
const currentJudgeList = {}; |
||||
const finishedJudgeList = {}; |
||||
const compiledList = []; |
||||
const clientDetailProgressList = {}; |
||||
const clientDisplayConfigList = {}; |
||||
|
||||
function processOverallResult(source, config) { |
||||
if (source == null) |
||||
return null; |
||||
if (source.error != null) { |
||||
return { |
||||
error: source.error, |
||||
systemMessage: source.systemMessage |
||||
}; |
||||
} |
||||
return { |
||||
compile: source.compile, |
||||
judge: config.showDetailResult ? (source.judge && { |
||||
subtasks: source.judge.subtasks && source.judge.subtasks.map(st => ({ |
||||
score: st.score, |
||||
cases: st.cases.map(cs => ({ |
||||
status: cs.status, |
||||
result: cs.result && { |
||||
type: cs.result.type, |
||||
time: config.showUsage ? cs.result.time : undefined, |
||||
memory: config.showUsage ? cs.result.memory : undefined, |
||||
scoringRate: cs.result.scoringRate, |
||||
systemMessage: cs.result.systemMessage, |
||||
input: config.showTestdata ? cs.result.input : undefined, |
||||
output: config.showTestdata ? cs.result.output : undefined, |
||||
userOutput: config.showTestdata ? cs.result.userOutput : undefined, |
||||
userError: config.showTestdata ? cs.result.userError : undefined, |
||||
spjMessage: config.showTestdata ? cs.result.spjMessage : undefined, |
||||
} |
||||
})) |
||||
})) |
||||
}) : null |
||||
}; |
||||
} |
||||
function getCompileStatus(status) { |
||||
if (["System Error", "Compile Error", "No Testdata"].includes(status)) { |
||||
return status; |
||||
} |
||||
else { |
||||
return "Submitted"; |
||||
} |
||||
} |
||||
function processRoughResult(source, config) { |
||||
const result = config.showResult ? |
||||
source.result : |
||||
getCompileStatus(source.result); |
||||
return { |
||||
result: result, |
||||
time: config.showUsage ? source.time : null, |
||||
memory: config.showUsage ? source.memory : null, |
||||
score: config.showScore ? source.score : null |
||||
}; |
||||
} |
||||
function forAllClients(ns, taskId, exec) { |
||||
ns.in(taskId.toString()).clients((err, clients) => { |
||||
if (!err) { |
||||
clients.forEach(client => { |
||||
exec(client); |
||||
}); |
||||
} |
||||
else { |
||||
winston.warn(`Error while listing socketio clients in ${taskId}`, err); |
||||
} |
||||
}); |
||||
} |
||||
function initializeSocketIO(s) { |
||||
ioInstance = socketio(s); |
||||
const initializeNamespace = (name, exec) => { |
||||
winston.debug('initializing socketIO', name); |
||||
const newNamespace = ioInstance.of('/' + name); |
||||
newNamespace.on('connection', (socket) => { |
||||
socket.on('disconnect', () => { |
||||
winston.info(`Client ${socket.id} disconnected.`); |
||||
delete clientDisplayConfigList[socket.id]; |
||||
if (clientDetailProgressList[socket.id]) { |
||||
delete clientDetailProgressList[socket.id]; |
||||
} |
||||
}); |
||||
socket.on('join', (reqJwt, cb) => { |
||||
winston.info(`Client ${socket.id} connected.`); |
||||
let req; |
||||
try { |
||||
req = jwt.verify(reqJwt, syzoj.config.judge_token); |
||||
if (req.type !== name) { |
||||
throw new Error("Request type in token mismatch."); |
||||
} |
||||
clientDisplayConfigList[socket.id] = req.displayConfig; |
||||
const taskId = req.taskId; |
||||
winston.verbose(`A client trying to join ${name} namespace for ${taskId}.`); |
||||
socket.join(taskId.toString()); |
||||
exec(req, socket).then(x => cb(x), err => cb({ ok: false, message: err.toString() })); |
||||
} |
||||
catch (err) { |
||||
winston.info('Error while joining.'); |
||||
cb({ |
||||
ok: false, |
||||
message: err.toString() |
||||
}); |
||||
return; |
||||
} |
||||
}); |
||||
}); |
||||
return newNamespace; |
||||
}; |
||||
detailProgressNamespace = initializeNamespace('detail', async (req, socket) => { |
||||
const taskId = req.taskId; |
||||
if (finishedJudgeList[taskId]) { |
||||
winston.debug(`Judge task #${taskId} has been finished, ${JSON.stringify(currentJudgeList[taskId])}`); |
||||
return { |
||||
ok: true, |
||||
running: false, |
||||
finished: true, |
||||
result: processOverallResult(currentJudgeList[taskId], clientDisplayConfigList[socket.id]), |
||||
roughResult: processRoughResult(finishedJudgeList[taskId], clientDisplayConfigList[socket.id]) |
||||
}; |
||||
} |
||||
else { |
||||
winston.debug(`Judge task #${taskId} has not been finished`); |
||||
if (currentJudgeList[taskId]) { |
||||
clientDetailProgressList[socket.id] = { |
||||
version: 0, |
||||
content: processOverallResult(currentJudgeList[taskId], clientDisplayConfigList[socket.id]) |
||||
}; |
||||
return { |
||||
ok: true, |
||||
finished: false, |
||||
running: true, |
||||
current: clientDetailProgressList[socket.id] |
||||
}; |
||||
} |
||||
else { |
||||
return { |
||||
ok: true, |
||||
finished: false, |
||||
running: false |
||||
}; |
||||
} |
||||
} |
||||
}); |
||||
roughProgressNamespace = initializeNamespace('rough', async (req, socket) => { |
||||
const taskId = req.taskId; |
||||
if (finishedJudgeList[taskId]) { |
||||
return { |
||||
ok: true, |
||||
running: false, |
||||
finished: true, |
||||
result: processRoughResult(finishedJudgeList[taskId], clientDisplayConfigList[socket.id]) |
||||
}; |
||||
} |
||||
else if (currentJudgeList[taskId]) { |
||||
return { |
||||
ok: true, |
||||
running: true, |
||||
finished: false |
||||
}; |
||||
} |
||||
else { |
||||
return { |
||||
ok: true, |
||||
running: false, |
||||
finished: false |
||||
}; |
||||
} |
||||
}); |
||||
compileProgressNamespace = initializeNamespace('compile', async (req, socket) => { |
||||
const taskId = req.taskId; |
||||
if (compiledList[taskId]) { |
||||
return { |
||||
ok: true, |
||||
running: false, |
||||
finished: true, |
||||
result: compiledList[taskId] |
||||
}; |
||||
} |
||||
else if (currentJudgeList[taskId]) { |
||||
return { |
||||
ok: true, |
||||
running: true, |
||||
finished: false |
||||
}; |
||||
} |
||||
else { |
||||
return { |
||||
ok: true, |
||||
running: false, |
||||
finished: false |
||||
}; |
||||
} |
||||
}); |
||||
} |
||||
exports.initializeSocketIO = initializeSocketIO; |
||||
function createTask(taskId) { |
||||
winston.debug(`Judge task #${taskId} has started`); |
||||
currentJudgeList[taskId] = {}; |
||||
finishedJudgeList[taskId] = null; |
||||
forAllClients(detailProgressNamespace, taskId, (clientId) => { |
||||
clientDetailProgressList[clientId] = { |
||||
version: 0, |
||||
content: {} |
||||
}; |
||||
}); |
||||
roughProgressNamespace.to(taskId.toString()).emit("start", { taskId: taskId }); |
||||
detailProgressNamespace.to(taskId.toString()).emit("start", { taskId: taskId }); |
||||
compileProgressNamespace.to(taskId.toString()).emit("start", { taskId: taskId }); |
||||
} |
||||
exports.createTask = createTask; |
||||
function updateCompileStatus(taskId, result) { |
||||
winston.debug(`Updating compilation status for #${taskId}`); |
||||
compiledList[taskId] = { result: result.status === interfaces.TaskStatus.Done ? 'Submitted' : 'Compile Error' }; |
||||
compileProgressNamespace.to(taskId.toString()).emit('finish', { |
||||
taskId: taskId, |
||||
result: compiledList[taskId] |
||||
}); |
||||
} |
||||
exports.updateCompileStatus = updateCompileStatus; |
||||
function updateProgress(taskId, data) { |
||||
winston.verbose(`Updating progress for #${taskId}`); |
||||
currentJudgeList[taskId] = data; |
||||
forAllClients(detailProgressNamespace, taskId, (client) => { |
||||
winston.debug(`Pushing progress update to ${client}`); |
||||
if (clientDetailProgressList[client] && clientDisplayConfigList[client]) { |
||||
const original = clientDetailProgressList[client].content; |
||||
const updated = processOverallResult(currentJudgeList[taskId], clientDisplayConfigList[client]); |
||||
const version = clientDetailProgressList[client].version; |
||||
detailProgressNamespace.sockets[client].emit('update', { |
||||
taskId: taskId, |
||||
from: version, |
||||
to: version + 1, |
||||
delta: diff.diff(original, updated) |
||||
}); |
||||
clientDetailProgressList[client].version++; |
||||
} |
||||
}); |
||||
} |
||||
exports.updateProgress = updateProgress; |
||||
function updateResult(taskId, data) { |
||||
currentJudgeList[taskId] = data; |
||||
if (compiledList[taskId] == null) { |
||||
if (data.error != null) { |
||||
compiledList[taskId] = { result: "System Error" }; |
||||
compileProgressNamespace.to(taskId.toString()).emit('finish', { |
||||
taskId: taskId, |
||||
result: compiledList[taskId] |
||||
}); |
||||
} |
||||
} |
||||
const finalResult = judgeResult.convertResult(taskId, data); |
||||
const roughResult = { |
||||
result: finalResult.statusString, |
||||
time: finalResult.time, |
||||
memory: finalResult.memory, |
||||
score: finalResult.score |
||||
}; |
||||
finishedJudgeList[taskId] = roughResult; |
||||
forAllClients(roughProgressNamespace, taskId, (client) => { |
||||
winston.debug(`Pushing rough result to ${client}`); |
||||
roughProgressNamespace.sockets[client].emit('finish', { |
||||
taskId: taskId, |
||||
result: processRoughResult(finishedJudgeList[taskId], clientDisplayConfigList[client]) |
||||
}); |
||||
}); |
||||
forAllClients(detailProgressNamespace, taskId, (client) => { |
||||
if (clientDisplayConfigList[client]) { |
||||
winston.debug(`Pushing detail result to ${client}`); |
||||
detailProgressNamespace.sockets[client].emit('finish', { |
||||
taskId: taskId, |
||||
result: processOverallResult(currentJudgeList[taskId], clientDisplayConfigList[client]), |
||||
roughResult: processRoughResult(finishedJudgeList[taskId], clientDisplayConfigList[client]) |
||||
}); |
||||
delete clientDetailProgressList[client]; |
||||
} |
||||
}); |
||||
} |
||||
exports.updateResult = updateResult; |
||||
function cleanupProgress(taskId) { |
||||
setTimeout(() => { delete currentJudgeList[taskId]; }, 10000); |
||||
} |
||||
exports.cleanupProgress = cleanupProgress; |
||||
//# sourceMappingURL=socketio.js.map
|
||||
|
||||
initializeSocketIO(app.server); |
Loading…
Reference in new issue