Browse Source

Merge branch 'monaco-editor' of github.com:syzoj/syzoj into monaco-editor

master
Menci 6 years ago
parent
commit
b58586a2bb
  1. 12
      libs/judger.js
  2. 1
      libs/submissions_process.js
  3. 23
      models/judge_state.js
  4. 14
      models/problem.js
  5. 2
      models/user.js
  6. 2
      modules/contest.js
  7. 6
      modules/problem.js
  8. 29
      modules/socketio.js
  9. 2
      views/contest.ejs
  10. 4
      views/problem.ejs
  11. 8
      views/statistics.ejs
  12. 2
      views/submission.ejs

12
libs/judger.js

@ -66,7 +66,7 @@ async function connect() {
waitingForTask = true;
winston.verbose(`Judge client ${socket.id} emitted waitForTask.`);
winston.warn(`Judge client ${socket.id} emitted waitForTask.`);
// Poll the judge queue, timeout = 10s.
let obj;
@ -80,9 +80,11 @@ async function connect() {
return;
}
winston.warn(`Judge task ${obj.data.content.taskId} poped from queue.`);
// Re-push to queue if got task but judge client already disconnected.
if (socket.disconnected) {
winston.warn(`Judge client ${socket.id} got task but disconnected re-pushing task to queue.`);
winston.warn(`Judge client ${socket.id} got task but disconnected re-pushing task ${obj.data.content.taskId} to queue.`);
judgeQueue.push(obj.data, obj.priority);
return;
}
@ -90,10 +92,10 @@ async function connect() {
// Send task to judge client, and wait for ack.
const task = obj.data;
pendingAckTaskObj = obj;
winston.verbose(`Sending task ${task.content.taskId} to judge client ${socket.id}.`);
winston.warn(`Sending task ${task.content.taskId} to judge client ${socket.id}.`);
socket.emit('onTask', msgPack.encode(task), () => {
// Acked.
winston.verbose(`Judge client ${socket.id} acked task ${task.content.taskId}.`);
winston.warn(`Judge client ${socket.id} acked task ${task.content.taskId}.`);
pendingAckTaskObj = null;
waitingForTask = false;
});
@ -232,6 +234,8 @@ module.exports.judge = async function (judge_state, problem, priority) {
content: content,
extraData: extraData
}, priority);
winston.warn(`Judge task ${content.taskId} enqueued.`);
}
module.exports.getCachedJudgeState = taskId => judgeStateCache.get(taskId);

1
libs/submissions_process.js

@ -66,6 +66,7 @@ const processOverallResult = (source, config) => {
score: st.score,
cases: st.cases.map(cs => ({
status: cs.status,
errorMessage: cs.errorMessage,
result: cs.result && {
type: cs.result.type,
time: config.showUsage ? cs.result.time : undefined,

23
models/judge_state.js

@ -118,19 +118,16 @@ class JudgeState extends Model {
}
async updateRelatedInfo(newSubmission) {
await syzoj.utils.lock(['JudgeState::updateRelatedInfo', 'problem', this.problem_id], async () => {
await syzoj.utils.lock(['JudgeState::updateRelatedInfo', 'user', this.user_id], async () => {
if (this.type === 0) {
await this.loadRelationships();
await this.user.refreshSubmitInfo();
await this.user.save();
await this.problem.resetSubmissionCount();
} else if (this.type === 1) {
let contest = await Contest.fromID(this.type_info);
await contest.newSubmission(this);
}
});
});
if (this.type === 0) {
await this.loadRelationships();
// No need to await them.
this.user.refreshSubmitInfo();
this.problem.resetSubmissionCount();
} else if (this.type === 1) {
let contest = await Contest.fromID(this.type_info);
await contest.newSubmission(this);
}
}
async rejudge() {

14
models/problem.js

@ -58,15 +58,15 @@ SELECT \
`id` \
FROM `judge_state` `inner_table` \
WHERE `problem_id` = `outer_table`.`problem_id` AND `user_id` = `outer_table`.`user_id` AND `status` = "Accepted" AND `type` = 0 \
ORDER BY LENGTH(`code`) ASC \
ORDER BY `code_length` ASC \
LIMIT 1 \
) AS `id`, \
( \
SELECT \
LENGTH(`code`) \
`code_length` \
FROM `judge_state` `inner_table` \
WHERE `problem_id` = `outer_table`.`problem_id` AND `user_id` = `outer_table`.`user_id` AND `status` = "Accepted" AND `type` = 0 \
ORDER BY LENGTH(`code`) ASC \
ORDER BY `code_length` ASC \
LIMIT 1 \
) AS `code_length` \
FROM `judge_state` `outer_table` \
@ -83,15 +83,15 @@ SELECT \
`id` \
FROM `judge_state` `inner_table` \
WHERE `problem_id` = `outer_table`.`problem_id` AND `user_id` = `outer_table`.`user_id` AND `status` = "Accepted" AND `type` = 0 \
ORDER BY LENGTH(`code`) DESC \
ORDER BY `code_length` DESC \
LIMIT 1 \
) AS `id`, \
( \
SELECT \
LENGTH(`code`) \
`code_length` \
FROM `judge_state` `inner_table` \
WHERE `problem_id` = `outer_table`.`problem_id` AND `user_id` = `outer_table`.`user_id` AND `status` = "Accepted" AND `type` = 0 \
ORDER BY LENGTH(`code`) DESC \
ORDER BY `code_length` DESC \
LIMIT 1 \
) AS `code_length` \
FROM `judge_state` `outer_table` \
@ -490,7 +490,7 @@ class Problem extends Model {
async resetSubmissionCount() {
let JudgeState = syzoj.model('judge_state');
await syzoj.utils.lock(['Problem::resetSubmissionCount', this.id], async () => {
this.submit_num = await JudgeState.count({ score: { $not: null }, problem_id: this.id, type: { $not: 1 } });
this.submit_num = await JudgeState.count({ problem_id: this.id, type: { $not: 1 } });
this.ac_num = await JudgeState.count({ score: 100, problem_id: this.id, type: { $not: 1 } });
await this.save();
});

2
models/user.js

@ -108,6 +108,8 @@ class User extends Model {
});
this.submit_num = cnt;
await this.save();
});
}

2
modules/contest.js

@ -249,7 +249,7 @@ app.get('/contest/:id/ranklist', async (req, res) => {
/*** XXX: Clumsy duplication, see ContestRanklist::updatePlayer() ***/
if (contest.type === 'noi' || contest.type === 'ioi') {
let multiplier = contest.ranklist.ranking_params[i] || 1.0;
let multiplier = (contest.ranklist.ranking_params || {})[i] || 1.0;
player.score_details[i].weighted_score = player.score_details[i].score == null ? null : Math.round(player.score_details[i].score * multiplier);
player.score += player.score_details[i].weighted_score;
}

6
modules/problem.js

@ -257,6 +257,7 @@ app.get('/problem/:id/export', async (req, res) => {
limit_and_hint: problem.limit_and_hint,
time_limit: problem.time_limit,
memory_limit: problem.memory_limit,
have_additional_file: problem.additional_file_id != null,
file_io: problem.file_io,
file_io_input_name: problem.file_io_input_name,
file_io_output_name: problem.file_io_output_name,
@ -465,6 +466,11 @@ app.post('/problem/:id/import', async (req, res) => {
let data = await download(req.body.url + (req.body.url.endsWith('/') ? 'testdata/download' : '/testdata/download'));
await fs.writeFileAsync(tmpFile.path, data);
await problem.updateTestdata(tmpFile.path, await res.locals.user.hasPrivilege('manage_problem'));
if (json.obj.have_additional_file) {
let additional_file = await download(req.body.url + (req.body.url.endsWith('/') ? 'download/additional_file' : '/download/additional_file'));
await fs.writeFileAsync(tmpFile.path, additional_file);
await problem.updateFile(tmpFile.path, 'additional_file', await res.locals.user.hasPrivilege('manage_problem'));
}
} catch (e) {
syzoj.log(e);
}

29
modules/socketio.js

@ -14,6 +14,7 @@ const finishedJudgeList = {};
const compiledList = [];
const clientDetailProgressList = {};
const clientDisplayConfigList = {};
const debug = false;
function processOverallResult(source, config) {
if (source == null)
@ -75,25 +76,25 @@ function forAllClients(ns, taskId, exec) {
});
}
else {
winston.warn(`Error while listing socketio clients in ${taskId}`, err);
if (debug) 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);
if (debug) winston.debug('initializing socketIO', name);
const newNamespace = ioInstance.of('/' + name);
newNamespace.on('connection', (socket) => {
socket.on('disconnect', () => {
winston.info(`Client ${socket.id} disconnected.`);
if (debug) 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.`);
if (debug) winston.info(`Client ${socket.id} connected.`);
let req;
try {
req = jwt.verify(reqJwt, syzoj.config.session_secret);
@ -102,12 +103,12 @@ function initializeSocketIO(s) {
}
clientDisplayConfigList[socket.id] = req.displayConfig;
const taskId = req.taskId;
winston.verbose(`A client trying to join ${name} namespace for ${taskId}.`);
if (debug) 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.');
if (debug) winston.info('Error while joining.');
cb({
ok: false,
message: err.toString()
@ -121,7 +122,7 @@ function initializeSocketIO(s) {
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])}`);
if (debug) winston.debug(`Judge task #${taskId} has been finished, ${JSON.stringify(currentJudgeList[taskId])}`);
return {
ok: true,
running: false,
@ -131,7 +132,7 @@ function initializeSocketIO(s) {
};
}
else {
winston.debug(`Judge task #${taskId} has not been finished`);
if (debug) winston.debug(`Judge task #${taskId} has not been finished`);
if (currentJudgeList[taskId]) {
clientDetailProgressList[socket.id] = {
version: 0,
@ -208,7 +209,7 @@ function initializeSocketIO(s) {
}
exports.initializeSocketIO = initializeSocketIO;
function createTask(taskId) {
winston.debug(`Judge task #${taskId} has started`);
if (debug) winston.debug(`Judge task #${taskId} has started`);
currentJudgeList[taskId] = {};
finishedJudgeList[taskId] = null;
forAllClients(detailProgressNamespace, taskId, (clientId) => {
@ -223,7 +224,7 @@ function createTask(taskId) {
}
exports.createTask = createTask;
function updateCompileStatus(taskId, result) {
winston.debug(`Updating compilation status for #${taskId}`);
if (debug) 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,
@ -232,7 +233,7 @@ function updateCompileStatus(taskId, result) {
}
exports.updateCompileStatus = updateCompileStatus;
function updateProgress(taskId, data) {
winston.verbose(`Updating progress for #${taskId}`);
if (debug) winston.verbose(`Updating progress for #${taskId}`);
currentJudgeList[taskId] = data;
const finalResult = judgeResult.convertResult(taskId, data);
const roughResult = {
@ -243,7 +244,7 @@ function updateProgress(taskId, data) {
};
forAllClients(detailProgressNamespace, taskId, (client) => {
try {
winston.debug(`Pushing progress update to ${client}`);
if (debug) winston.debug(`Pushing progress update to ${client}`);
if (clientDetailProgressList[client] && clientDisplayConfigList[client]) {
const original = clientDetailProgressList[client].content;
const updated = processOverallResult(currentJudgeList[taskId], clientDisplayConfigList[client]);
@ -283,7 +284,7 @@ function updateResult(taskId, data) {
};
finishedJudgeList[taskId] = roughResult;
forAllClients(roughProgressNamespace, taskId, (client) => {
winston.debug(`Pushing rough result to ${client}`);
if (debug) winston.debug(`Pushing rough result to ${client}`);
roughProgressNamespace.sockets[client].emit('finish', {
taskId: taskId,
result: processRoughResult(finishedJudgeList[taskId], clientDisplayConfigList[client])
@ -291,7 +292,7 @@ function updateResult(taskId, data) {
});
forAllClients(detailProgressNamespace, taskId, (client) => {
if (clientDisplayConfigList[client]) {
winston.debug(`Pushing detail result to ${client}`);
if (debug) winston.debug(`Pushing detail result to ${client}`);
detailProgressNamespace.sockets[client].emit('finish', {
taskId: taskId,
result: processOverallResult(currentJudgeList[taskId], clientDisplayConfigList[client]),

2
views/contest.ejs

@ -75,8 +75,10 @@
<a href="<%= syzoj.utils.makeUrl(['contest', 'submission', problem.judge_id]) %>">
<% if (typeof problem.status === 'string') { %>
<span class="status <%= problem.status.toLowerCase().split(' ').join('_') %>">
<b>
<i class="<%= icon[getStatusMeta(problem.status)] || 'remove' %> icon"></i>
<%= problem.feedback || problem.status %>
</b>
</span>
<% } else if (typeof problem.status === 'object') { %>
<% if (problem.status.accepted) { %>

4
views/problem.ejs

@ -38,8 +38,8 @@ if (contest) {
<% if (problem.type === 'interaction') { %>
<span class="ui label">题目类型:交互</span>
<% } else if (problem.file_io) { %>
<span class="ui label">输入文件: <%= problem.file_io_input_name %></span>
<span class="ui label">输出文件: <%= problem.file_io_output_name %></span>
<span class="ui label">输入文件<%= problem.file_io_input_name %></span>
<span class="ui label">输出文件<%= problem.file_io_output_name %></span>
<% } else { %>
<span class="ui label">标准输入输出</span>
<% } %>

8
views/statistics.ejs

@ -71,19 +71,21 @@ function getColorOfScore(score) {
<% for (let judge of statistics.judge_state) { %>
<% include util %>
<tr>
<td><a href="<%= syzoj.utils.makeUrl(['submission', judge.id]) %>">#<%= judge.id %></a></td>
<td><a href="<%= syzoj.utils.makeUrl(['problem', judge.problem_id]) %>">#<%= judge.problem_id %>. <%= judge.problem.title %></a></td>
<td><a href="<%= syzoj.utils.makeUrl(['submission', judge.id]) %>"><b>#<%= judge.id %></b></a></td>
<td><a href="<%= syzoj.utils.makeUrl(['problem', judge.problem_id]) %>"><b>#<%= judge.problem_id %>.</b> <%= judge.problem.title %></a></td>
<td><a href="<%= syzoj.utils.makeUrl(['submission', judge.id]) %>">
<b>
<span class="status <%= getStatusMeta(judge.status).toLowerCase().split(' ').join('_') %>">
<i class="<%= icon[getStatusMeta(judge.status)] || 'remove' %> icon"></i>
<%= judge.status %>
</span>
</b>
</a></td>
<td><a href="<%= syzoj.utils.makeUrl(['submission', judge.id]) %>"><span class="score score_<%= parseInt(judge.score / 10) || 0 %>"><%= judge.score %></span></a></td>
<% if (problem.type !== 'submit-answer') { %>
<td><%= judge.total_time %> ms</td>
<td><%= parseInt(judge.max_memory) || 0 %> K</td>
<td><a href="<%= syzoj.utils.makeUrl(['submission', judge.id]) %>"><%= syzoj.languages[judge.language].show %></a> / <%= syzoj.utils.formatSize(judge.code.length) %></td>
<td><a href="<%= syzoj.utils.makeUrl(['submission', judge.id]) %>"><b><%= syzoj.languages[judge.language].show %></b></a> / <%= syzoj.utils.formatSize(judge.code.length) %></td>
<% } else { %>
<td><%= syzoj.utils.formatSize(judge.max_memory) %></td>
<% } %>

2
views/submission.ejs

@ -85,7 +85,7 @@
<div class="content" :class="singleSubtask ? 'active' : ''">
<div class="accordion">
<template v-for="curCase, $caseIndex in subtask.cases">
<div class="title" :class="checkTestcaseOK(curCase) ? '' : 'unexpandable'">
<div class="title" :class="checkTestcaseOK(curCase) || curCase.errorMessage ? '' : 'unexpandable'">
<div class="ui grid">
<div class="three wide column">
<i class="dropdown icon"></i>

Loading…
Cancel
Save