Browse Source

Add non-traditional problem support; add problem additional_file

pull/6/head
Menci 8 years ago
parent
commit
6bb873810e
  1. 115
      models/file.js
  2. 21
      models/judge_state.js
  3. 109
      models/problem.js
  4. 64
      models/testdata.js
  5. 52
      modules/api.js
  6. 73
      modules/problem.js
  7. 17
      modules/submission.js
  8. 0
      uploads/additional_file/.gitkeep
  9. 0
      uploads/answer/.gitkeep
  10. 0
      uploads/testdata/.gitkeep
  11. 2
      utility.js
  12. 124
      views/problem.ejs
  13. 153
      views/problem_data.ejs
  14. 34
      views/statistics.ejs
  15. 29
      views/submission_content.ejs
  16. 3
      views/submissions.ejs
  17. 5
      views/submissions_item.ejs

115
models/file.js

@ -0,0 +1,115 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Sequelize = require('sequelize');
let db = syzoj.db;
let model = db.define('file', {
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
type: { type: Sequelize.STRING(80) },
md5: { type: Sequelize.STRING(80), unique: true }
}, {
timestamps: false,
tableName: 'file',
indexes: [
{
fields: ['type'],
},
{
fields: ['md5'],
}
]
});
let Model = require('./common');
class File extends Model {
static create(val) {
return File.fromRecord(File.model.build(Object.assign({
type: '',
md5: ''
}, val)));
}
getPath() {
return File.resolvePath(this.type, this.md5);
}
static resolvePath(type, md5) {
return syzoj.utils.resolvePath(syzoj.config.upload_dir, type, md5);
}
static async upload(path, type) {
let fs = Promise.promisifyAll(require('fs-extra'));
let buf = await fs.readFileAsync(path);
if (buf.length > syzoj.config.limit.data_size) throw new ErrorMessage('数据包太大。');
try {
let AdmZip = require('adm-zip');
let zip = new AdmZip(buf);
this.unzipSize = 0;
for (let x of zip.getEntries()) this.unzipSize += x.header.size;
} catch (e) {
this.unzipSize = null;
}
let key = syzoj.utils.md5(buf);
await fs.moveAsync(path, File.resolvePath(type, key), { overwrite: true });
let file = await File.findOne({ where: { md5: key } });
if (!file) {
file = await File.create({
type: type,
md5: key
});
await file.save();
}
return file;
}
async getUnzipSize() {
if (this.unzipSize === undefined) {
try {
let fs = Promise.promisifyAll(require('fs-extra'));
let buf = await fs.readFileAsync(this.getPath());
let AdmZip = require('adm-zip');
let zip = new AdmZip(buf);
this.unzipSize = 0;
for (let x of zip.getEntries()) this.unzipSize += x.header.size;
} catch (e) {
this.unzipSize = null;
}
}
if (this.unzipSize === null) throw new ErrorMessage('无效的 ZIP 文件。');
else return this.unzipSize;
}
getModel() { return model; }
}
File.model = model;
module.exports = File;

21
models/judge_state.js

@ -28,6 +28,8 @@ let Contest = syzoj.model('contest');
let model = db.define('judge_state', { let model = db.define('judge_state', {
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
// The data zip's md5 if it's a submit-answer problem
code: { type: Sequelize.TEXT('medium') }, code: { type: Sequelize.TEXT('medium') },
language: { type: Sequelize.STRING(20) }, language: { type: Sequelize.STRING(20) },
@ -163,8 +165,11 @@ class JudgeState extends Model {
this.score = result.score; this.score = result.score;
this.pending = result.pending; this.pending = result.pending;
this.status = result.status; this.status = result.status;
this.total_time = result.total_time; if (this.language) {
this.max_memory = result.max_memory; // language is empty if it's a submit-answer problem
this.total_time = result.total_time;
this.max_memory = result.max_memory;
}
this.result = result; this.result = result;
} }
@ -203,8 +208,11 @@ class JudgeState extends Model {
this.status = 'Waiting'; this.status = 'Waiting';
this.score = 0; this.score = 0;
this.total_time = 0; if (this.language) {
this.max_memory = 0; // language is empty if it's a submit-answer problem
this.total_time = 0;
this.max_memory = 0;
}
this.pending = true; this.pending = true;
this.result = { status: "Waiting", total_time: 0, max_memory: 0, score: 0, case_num: 0, compiler_output: "", pending: true }; this.result = { status: "Waiting", total_time: 0, max_memory: 0, score: 0, case_num: 0, compiler_output: "", pending: true };
await this.save(); await this.save();
@ -233,6 +241,11 @@ class JudgeState extends Model {
}); });
} }
async getProblemType() {
await this.loadRelationships();
return this.problem.type;
}
getModel() { return model; } getModel() { return model; }
} }

109
models/problem.js

@ -144,6 +144,56 @@ FROM `judge_state` `outer_table` \
WHERE \ WHERE \
`problem_id` = __PROBLEM_ID__ AND `status` = "Accepted" AND `type` = 0 \ `problem_id` = __PROBLEM_ID__ AND `status` = "Accepted" AND `type` = 0 \
ORDER BY `submit_time` ASC \ ORDER BY `submit_time` ASC \
',
min:
' \
SELECT \
DISTINCT(`user_id`) AS `user_id`, \
( \
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 `max_memory` ASC \
LIMIT 1 \
) AS `id`, \
( \
SELECT \
`max_memory` \
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 `max_memory` ASC \
LIMIT 1 \
) AS `max_memory` \
FROM `judge_state` `outer_table` \
WHERE \
`problem_id` = __PROBLEM_ID__ AND `status` = "Accepted" AND `type` = 0 \
ORDER BY `max_memory` ASC \
',
max:
' \
SELECT \
DISTINCT(`user_id`) AS `user_id`, \
( \
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 `max_memory` ASC \
LIMIT 1 \
) AS `id`, \
( \
SELECT \
`max_memory` \
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 `max_memory` ASC \
LIMIT 1 \
) AS `max_memory` \
FROM `judge_state` `outer_table` \
WHERE \
`problem_id` = __PROBLEM_ID__ AND `status` = "Accepted" AND `type` = 0 \
ORDER BY `max_memory` DESC \
' '
}; };
@ -151,7 +201,7 @@ let Sequelize = require('sequelize');
let db = syzoj.db; let db = syzoj.db;
let User = syzoj.model('user'); let User = syzoj.model('user');
let TestData = syzoj.model('testdata'); let File = syzoj.model('file');
let model = db.define('problem', { let model = db.define('problem', {
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
@ -182,13 +232,8 @@ let model = db.define('problem', {
time_limit: { type: Sequelize.INTEGER }, time_limit: { type: Sequelize.INTEGER },
memory_limit: { type: Sequelize.INTEGER }, memory_limit: { type: Sequelize.INTEGER },
testdata_id: { testdata_id: { type: Sequelize.INTEGER },
type: Sequelize.INTEGER, additional_file_id: { type: Sequelize.INTEGER },
references: {
model: 'file',
key: 'id'
}
},
ac_num: { type: Sequelize.INTEGER }, ac_num: { type: Sequelize.INTEGER },
submit_num: { type: Sequelize.INTEGER }, submit_num: { type: Sequelize.INTEGER },
@ -196,7 +241,12 @@ let model = db.define('problem', {
file_io: { type: Sequelize.BOOLEAN }, file_io: { type: Sequelize.BOOLEAN },
file_io_input_name: { type: Sequelize.TEXT }, file_io_input_name: { type: Sequelize.TEXT },
file_io_output_name: { type: Sequelize.TEXT } file_io_output_name: { type: Sequelize.TEXT },
type: {
type: Sequelize.ENUM,
values: ['traditional', 'submit-answer', 'interaction']
}
}, { }, {
timestamps: false, timestamps: false,
tableName: 'problem', tableName: 'problem',
@ -234,14 +284,17 @@ class Problem extends Model {
file_io: false, file_io: false,
file_io_input_name: '', file_io_input_name: '',
file_io_output_name: '' file_io_output_name: '',
type: ''
}, val))); }, val)));
} }
async loadRelationships() { async loadRelationships() {
this.user = await User.fromID(this.user_id); this.user = await User.fromID(this.user_id);
this.publicizer = await User.fromID(this.publicizer_id); this.publicizer = await User.fromID(this.publicizer_id);
this.testdata = await TestData.fromID(this.testdata_id); this.testdata = await File.fromID(this.testdata_id);
this.additional_file = await File.fromID(this.additional_file_id);
} }
async isAllowedEditBy(user) { async isAllowedEditBy(user) {
@ -263,36 +316,15 @@ class Problem extends Model {
return user.is_admin; return user.is_admin;
} }
async updateTestdata(path) { async updateFile(path, type) {
let fs = Promise.promisifyAll(require('fs-extra')); let file = await File.upload(path, type);
let buf = await fs.readFileAsync(path);
if (buf.length > syzoj.config.limit.data_size) throw new ErrorMessage('测试数据太大。'); if (type === 'testdata') {
this.testdata_id = file.id;
let key = syzoj.utils.md5(buf); } else if (type === 'additional_file') {
await fs.moveAsync(path, TestData.resolvePath(key), { overwrite: true }); this.additional_file_id = file.id;
if (this.testdata_id) {
let tmp = this.testdata_id;
this.testdata_id = null;
await this.save();
let file = await TestData.fromID(tmp);
if (file) await file.destroy();
} }
let filename = `test_data_${this.id}.zip`;
let file = await TestData.findOne({ where: { filename: filename } });
if (file) await file.destroy();
file = await TestData.create({
filename: filename,
md5: key
});
await file.save();
this.testdata_id = file.id;
await this.save(); await this.save();
} }
@ -453,7 +485,6 @@ class Problem extends Model {
await db.query('UPDATE `problem` SET `id` = ' + id + ' WHERE `id` = ' + this.id); await db.query('UPDATE `problem` SET `id` = ' + id + ' WHERE `id` = ' + this.id);
await db.query('UPDATE `judge_state` SET `problem_id` = ' + id + ' WHERE `problem_id` = ' + this.id); await db.query('UPDATE `judge_state` SET `problem_id` = ' + id + ' WHERE `problem_id` = ' + this.id);
await db.query('UPDATE `problem_tag_map` SET `problem_id` = ' + id + ' WHERE `problem_id` = ' + this.id); await db.query('UPDATE `problem_tag_map` SET `problem_id` = ' + id + ' WHERE `problem_id` = ' + this.id);
await db.query('UPDATE `file` SET `filename` = ' + `"test_data_${id}.zip"` + ' WHERE `filename` = ' + `"test_data_${this.id}.zip"`);
let Contest = syzoj.model('contest'); let Contest = syzoj.model('contest');
let contests = await Contest.all(); let contests = await Contest.all();

64
models/testdata.js

@ -1,64 +0,0 @@
/*
* This file is part of SYZOJ.
*
* Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
*
* SYZOJ is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* SYZOJ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with SYZOJ. If not, see <http://www.gnu.org/licenses/>.
*/
'use strict';
let Sequelize = require('sequelize');
let db = syzoj.db;
let model = db.define('file', {
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
filename: { type: Sequelize.STRING(80), unique: true },
md5: { type: Sequelize.STRING(80), unique: true }
}, {
timestamps: false,
tableName: 'file',
indexes: [
{
fields: ['filename'],
},
{
fields: ['md5'],
}
]
});
let Model = require('./common');
class TestData extends Model {
static create(val) {
return TestData.fromRecord(TestData.model.build(Object.assign({
filename: '',
md5: ''
}, val)));
}
getPath() {
return TestData.resolvePath(this.md5);
}
static resolvePath(md5) {
return syzoj.utils.resolvePath(syzoj.config.upload_dir, 'testdata', md5);
}
getModel() { return model; }
}
TestData.model = model;
module.exports = TestData;

52
modules/api.js

@ -23,7 +23,7 @@ let User = syzoj.model('user');
let Problem = syzoj.model('problem'); let Problem = syzoj.model('problem');
let WaitingJudge = syzoj.model('waiting_judge'); let WaitingJudge = syzoj.model('waiting_judge');
let JudgeState = syzoj.model('judge_state'); let JudgeState = syzoj.model('judge_state');
let TestData = syzoj.model('testdata'); let File = syzoj.model('file');
function setLoginCookie(username, password, res) { function setLoginCookie(username, password, res) {
res.cookie('login', JSON.stringify([username, password])); res.cookie('login', JSON.stringify([username, password]));
@ -109,18 +109,32 @@ app.get('/api/waiting_judge', async (req, res) => {
}); });
if (judge_state) { if (judge_state) {
res.send({ await judge_state.loadRelationships();
have_task: 1, await judge_state.problem.loadRelationships();
judge_id: judge_state.id,
code: judge_state.code, if (judge_state.problem.type === 'submit-answer') {
language: judge_state.language, res.send({
testdata: judge_state.problem.testdata ? judge_state.problem.testdata.md5 : '', have_task: 1,
time_limit: judge_state.problem.time_limit, judge_id: judge_state.id,
memory_limit: judge_state.problem.memory_limit, answer_file: judge_state.code,
file_io: judge_state.problem.file_io, testdata: judge_state.problem.testdata ? judge_state.problem.testdata.md5 : '',
file_io_input_name: judge_state.problem.file_io_input_name, problem_type: judge_state.problem.type
file_io_output_name: judge_state.problem.file_io_output_name });
}); } else {
res.send({
have_task: 1,
judge_id: judge_state.id,
code: judge_state.code,
language: judge_state.language,
testdata: judge_state.problem.testdata ? judge_state.problem.testdata.md5 : '',
time_limit: judge_state.problem.time_limit,
memory_limit: judge_state.problem.memory_limit,
file_io: judge_state.problem.file_io,
file_io_input_name: judge_state.problem.file_io_input_name,
file_io_output_name: judge_state.problem.file_io_output_name,
problem_type: judge_state.problem.type
});
}
} else { } else {
res.send({ have_task: 0 }); res.send({ have_task: 0 });
} }
@ -145,9 +159,17 @@ app.post('/api/update_judge/:id', async (req, res) => {
} }
}); });
app.get('/static/uploads/:md5', async (req, res) => { app.get('/static/uploads/testdata/:md5', async (req, res) => {
try {
res.sendFile(File.resolvePath('testdata', req.params.md5));
} catch (e) {
res.status(500).send(e);
}
});
app.get('/static/uploads/answer/:md5', async (req, res) => {
try { try {
res.sendFile(TestData.resolvePath(req.params.md5)); res.sendFile(File.resolvePath('answer', req.params.md5));
} catch (e) { } catch (e) {
res.status(500).send(e); res.status(500).send(e);
} }

73
modules/problem.js

@ -464,7 +464,7 @@ app.get('/problem/:id/data', async (req, res) => {
} }
}); });
app.post('/problem/:id/data', app.multer.single('testdata'), async (req, res) => { app.post('/problem/:id/data', app.multer.fields([{ name: 'testdata', maxCount: 1 }, { name: 'additional_file', maxCount: 1 }]), async (req, res) => {
try { try {
let id = parseInt(req.params.id); let id = parseInt(req.params.id);
let problem = await Problem.fromID(id); let problem = await Problem.fromID(id);
@ -480,11 +480,22 @@ app.post('/problem/:id/data', app.multer.single('testdata'), async (req, res) =>
problem.file_io_input_name = req.body.file_io_input_name; problem.file_io_input_name = req.body.file_io_input_name;
problem.file_io_output_name = req.body.file_io_output_name; problem.file_io_output_name = req.body.file_io_output_name;
if (problem.type === 'submit-answer' && req.body.type !== 'submit-answer' || problem.type !== 'submit-answer' && req.body.type === 'submit-answer') {
if (await JudgeState.count({ problem_id: id }) !== 0) {
throw new ErrorMessage('已有提交的题目不允许在提交答案和非提交答案之间更改。');
}
}
problem.type = req.body.type;
let validateMsg = await problem.validate(); let validateMsg = await problem.validate();
if (validateMsg) throw new ErrorMessage('无效的题目数据配置。', null, validateMsg); if (validateMsg) throw new ErrorMessage('无效的题目数据配置。', null, validateMsg);
if (req.file) { if (req.files['testdata']) {
await problem.updateTestdata(req.file.path); await problem.updateFile(req.files['testdata'][0].path, 'testdata');
}
if (req.files['additional_file']) {
await problem.updateFile(req.files['additional_file'][0].path, 'additional_file');
} }
await problem.save(); await problem.save();
@ -529,21 +540,37 @@ app.get('/problem/:id/dis_public', async (req, res) => {
await setPublic(req, res, false); await setPublic(req, res, false);
}); });
app.post('/problem/:id/submit', async (req, res) => { app.post('/problem/:id/submit', app.multer.fields([{ name: 'answer', maxCount: 1 }]), async (req, res) => {
try { try {
let id = parseInt(req.params.id); let id = parseInt(req.params.id);
let problem = await Problem.fromID(id); let problem = await Problem.fromID(id);
if (!problem) throw new ErrorMessage('无此题目。'); if (!problem) throw new ErrorMessage('无此题目。');
if (!syzoj.config.languages[req.body.language]) throw new ErrorMessage('不支持该语言。'); if (problem.type !== 'submit-answer' && !syzoj.config.languages[req.body.language]) throw new ErrorMessage('不支持该语言。');
if (!res.locals.user) throw new ErrorMessage('请登录后继续。', { '登录': syzoj.utils.makeUrl(['login'], { 'url': syzoj.utils.makeUrl(['problem', id]) }) }); if (!res.locals.user) throw new ErrorMessage('请登录后继续。', { '登录': syzoj.utils.makeUrl(['login'], { 'url': syzoj.utils.makeUrl(['problem', id]) }) });
let judge_state = await JudgeState.create({ let judge_state;
code: req.body.code, if (problem.type === 'submit-answer') {
language: req.body.language, let File = syzoj.model('file');
user_id: res.locals.user.id, let file = await File.upload(req.files['answer'][0].path, 'answer');
problem_id: req.params.id let size = await file.getUnzipSize();
});
if (!file.md5) throw new ErrorMessage('上传答案文件失败。');
judge_state = await JudgeState.create({
code: file.md5,
max_memory: size,
language: '',
user_id: res.locals.user.id,
problem_id: req.params.id
});
} else {
judge_state = await JudgeState.create({
code: req.body.code,
language: req.body.language,
user_id: res.locals.user.id,
problem_id: req.params.id
});
}
let contest_id = parseInt(req.query.contest_id), redirectToContest = false; let contest_id = parseInt(req.query.contest_id), redirectToContest = false;
if (contest_id) { if (contest_id) {
@ -579,7 +606,7 @@ app.post('/problem/:id/submit', async (req, res) => {
} }
}); });
app.get('/problem/:id/download', async (req, res) => { app.get('/problem/:id/download/testdata', async (req, res) => {
try { try {
let id = parseInt(req.params.id); let id = parseInt(req.params.id);
let problem = await Problem.fromID(id); let problem = await Problem.fromID(id);
@ -601,6 +628,28 @@ app.get('/problem/:id/download', async (req, res) => {
} }
}); });
app.get('/problem/:id/download/additional_file', async (req, res) => {
try {
let id = parseInt(req.params.id);
let problem = await Problem.fromID(id);
if (!problem) throw new ErrorMessage('无此题目。');
if (!await problem.isAllowedUseBy(res.locals.user)) throw new ErrorMessage('您没有权限进行此操作。');
await problem.loadRelationships();
if (!problem.additional_file) throw new ErrorMessage('无附加文件。');
res.download(problem.additional_file.getPath(), `additional_file_${id}.zip`);
} catch (e) {
syzoj.log(e);
res.status(404);
res.render('error', {
err: e
});
}
});
app.get('/problem/:id/statistics/:type', async (req, res) => { app.get('/problem/:id/statistics/:type', async (req, res) => {
try { try {
let id = parseInt(req.params.id); let id = parseInt(req.params.id);

17
modules/submission.js

@ -42,7 +42,10 @@ app.get('/submissions', async (req, res) => {
} }
}; };
if (req.query.language) where.language = req.query.language; if (req.query.language) {
if (req.query.language === 'submit-answer') where.language = '';
else where.language = req.query.language;
}
if (req.query.status) where.status = { $like: req.query.status + '%' }; if (req.query.status) where.status = { $like: req.query.status + '%' };
where.type = { $ne: 1 }; where.type = { $ne: 1 };
@ -136,8 +139,10 @@ app.get('/submission/:id', async (req, res) => {
await judge.loadRelationships(); await judge.loadRelationships();
judge.codeLength = judge.code.length; if (judge.problem.type !== 'submit-answer') {
judge.code = await syzoj.utils.highlight(judge.code, syzoj.config.languages[judge.language].highlight); judge.codeLength = judge.code.length;
judge.code = await syzoj.utils.highlight(judge.code, syzoj.config.languages[judge.language].highlight);
}
judge.allowedSeeCode = await judge.isAllowedSeeCodeBy(res.locals.user); judge.allowedSeeCode = await judge.isAllowedSeeCodeBy(res.locals.user);
judge.allowedSeeCase = await judge.isAllowedSeeCaseBy(res.locals.user); judge.allowedSeeCase = await judge.isAllowedSeeCaseBy(res.locals.user);
judge.allowedSeeData = await judge.isAllowedSeeDataBy(res.locals.user); judge.allowedSeeData = await judge.isAllowedSeeDataBy(res.locals.user);
@ -181,8 +186,10 @@ app.get('/submission/:id/ajax', async (req, res) => {
await judge.loadRelationships(); await judge.loadRelationships();
judge.codeLength = judge.code.length; if (judge.problem.type !== 'submit-answer') {
judge.code = await syzoj.utils.highlight(judge.code, syzoj.config.languages[judge.language].highlight); judge.codeLength = judge.code.length;
judge.code = await syzoj.utils.highlight(judge.code, syzoj.config.languages[judge.language].highlight);
}
judge.allowedSeeCode = await judge.isAllowedSeeCodeBy(res.locals.user); judge.allowedSeeCode = await judge.isAllowedSeeCodeBy(res.locals.user);
judge.allowedSeeCase = await judge.isAllowedSeeCaseBy(res.locals.user); judge.allowedSeeCase = await judge.isAllowedSeeCaseBy(res.locals.user);
judge.allowedSeeData = await judge.isAllowedSeeDataBy(res.locals.user); judge.allowedSeeData = await judge.isAllowedSeeDataBy(res.locals.user);

0
uploads/testdata/.placeholder → uploads/additional_file/.gitkeep

0
uploads/answer/.gitkeep

0
uploads/testdata/.gitkeep vendored

2
utility.js

@ -195,7 +195,7 @@ module.exports = {
gravatar(email, size) { gravatar(email, size) {
return gravatar.url(email, { s: size, d: 'mm' }).replace('www', 'cn'); return gravatar.url(email, { s: size, d: 'mm' }).replace('www', 'cn');
}, },
parseTestData(filename) { parseTestdata(filename) {
let zip = new AdmZip(filename); let zip = new AdmZip(filename);
let list = zip.getEntries().filter(e => !e.isDirectory).map(e => e.entryName); let list = zip.getEntries().filter(e => !e.isDirectory).map(e => e.entryName);
let res = []; let res = [];

124
views/problem.ejs

@ -88,8 +88,9 @@ if (contest) {
<a class="small ui primary button" href="#submit_code">提交</a> <a class="small ui primary button" href="#submit_code">提交</a>
<a class="small ui positive button" href="<%= syzoj.utils.makeUrl(['submissions'], { problem_id: problem.id }) %>">提交记录</a> <a class="small ui positive button" href="<%= syzoj.utils.makeUrl(['submissions'], { problem_id: problem.id }) %>">提交记录</a>
<a class="small ui orange button" href="<%= syzoj.utils.makeUrl(['problem', problem.id, 'statistics', 'fastest']) %>">统计</a> <a class="small ui orange button" href="<%= syzoj.utils.makeUrl(['problem', problem.id, 'statistics', 'fastest']) %>">统计</a>
<a class="small ui yellow button" href="<%= syzoj.utils.makeUrl(['problem', problem.id, 'download']) %>">下载测试数据</a> <% if (problem.testdata) { %><a class="small ui yellow button" href="<%= syzoj.utils.makeUrl(['problem', problem.id, 'download', 'testdata']) %>">下载测试数据</a><% } %>
<% } %> <% } %>
<% if (problem.additional_file) { %><a class="small ui teal button" href="<%= syzoj.utils.makeUrl(['problem', problem.id, 'download', 'additional_file']) %>">下载附加文件</a><% } %>
</div> </div>
<% if (!contest) { %> <% if (!contest) { %>
<div class="ui buttons right floated"> <div class="ui buttons right floated">
@ -187,71 +188,88 @@ if (contest) {
if (contest) formUrl = syzoj.utils.makeUrl(['problem', problem.id, 'submit'], { contest_id: contest.id }); if (contest) formUrl = syzoj.utils.makeUrl(['problem', problem.id, 'submit'], { contest_id: contest.id });
else formUrl = syzoj.utils.makeUrl(['problem', problem.id, 'submit']); else formUrl = syzoj.utils.makeUrl(['problem', problem.id, 'submit']);
%> %>
<form class="ui form" action="<%= formUrl %>" method="post" onsubmit="return submit_code()" id="submit_code"> <form class="ui form" action="<%= formUrl %>" method="post" onsubmit="return submit_code()" id="submit_code" enctype="multipart/form-data">
<input name="language" type="hidden" id="form"> <% if (problem.type === 'submit-answer') { %>
<input name="code" type="hidden"> <div class="inline fields">
<div class="ui grid"> <div class="field" style="margin: 0 auto; ">
<div class="four wide column" style="margin-right: -25px; "> <label for="answer">上传答案(请使用 ZIP 格式压缩)</label>
<div class="ui attached vertical fluid pointing menu" id="languages-menu" style="max-height: 370px; overflow-y: scroll; overflow-x: hidden; "> <input type="file" id="answer" name="answer">
<%
let language = Object.getOwnPropertyNames(syzoj.config.languages).shift();
if (state) {
language = state.language;
} else if (lastLanguage) language = lastLanguage;
%>
<% for (lang in syzoj.config.languages) { %>
<a class="item<%= lang === language ? ' active' : '' %>" data-value="<%= lang %>" data-mode="<%= syzoj.config.languages[lang].editor %>">
<%= syzoj.config.languages[lang].show %>
<div class="ui right floated" style="opacity: 0.4; margin-top: 8px; font-size: 0.7em; "><%= syzoj.config.languages[lang].version %></div>
</a>
<% } %>
</div> </div>
</div> </div>
<div class="twelve wide stretched column" style="padding-left: 0; margin-left: calc(-1rem - 1px); width: calc(75% + 1rem + 1px + 25px) !important; "> <% } else { %>
<div id="editor" style="border: 1px solid #D4D4D5; "><% if (state) { %><%= state.code %><% } %></div> <input name="language" type="hidden" id="form">
<input name="code" type="hidden">
<div class="ui grid">
<div class="four wide column" style="margin-right: -25px; ">
<div class="ui attached vertical fluid pointing menu" id="languages-menu" style="max-height: 370px; overflow-y: scroll; overflow-x: hidden; ">
<%
let language = Object.getOwnPropertyNames(syzoj.config.languages).shift();
if (state) {
language = state.language;
} else if (lastLanguage) language = lastLanguage;
%>
<% for (lang in syzoj.config.languages) { %>
<a class="item<%= lang === language ? ' active' : '' %>" data-value="<%= lang %>" data-mode="<%= syzoj.config.languages[lang].editor %>">
<%= syzoj.config.languages[lang].show %>
<div class="ui right floated" style="opacity: 0.4; margin-top: 8px; font-size: 0.7em; "><%= syzoj.config.languages[lang].version %></div>
</a>
<% } %>
</div>
</div>
<div class="twelve wide stretched column" style="padding-left: 0; margin-left: calc(-1rem - 1px); width: calc(75% + 1rem + 1px + 25px) !important; ">
<div id="editor" style="border: 1px solid #D4D4D5; "><% if (state) { %><%= state.code %><% } %></div>
</div>
</div> </div>
</div> <% } %>
<div class="ui center aligned vertical segment" style="padding-bottom: 0; "><button type="submit" class="ui button">提交</button></div> <div class="ui center aligned vertical segment" style="padding-bottom: 0; "><button type="submit" class="ui button">提交</button></div>
</form> </form>
</div> </div>
</div> </div>
<% } %> <% } %>
</div> </div>
<script src="/libs/ace/ace.js"></script> <% if (problem.type !== 'submit-answer') { %>
<script type="text/javascript"> <script src="/libs/ace/ace.js"></script>
var editor = ace.edit("editor"); <script type="text/javascript">
var editor = ace.edit("editor");
editor.setTheme("ace/theme/tomorrow"); editor.setTheme("ace/theme/tomorrow");
editor.getSession().setMode("ace/mode/" + $('#languages-menu .item.active').data('mode')); editor.getSession().setMode("ace/mode/" + $('#languages-menu .item.active').data('mode'));
editor.getSession().setUseSoftTabs(false); editor.getSession().setUseSoftTabs(false);
editor.container.style.lineHeight = 1.6; editor.container.style.lineHeight = 1.6;
editor.container.style.fontSize = '14px'; editor.container.style.fontSize = '14px';
editor.container.style.fontFamily = "'Roboto Mono', 'Bitstream Vera Sans Mono', 'Menlo', 'Consolas', 'Lucida Console', monospace"; editor.container.style.fontFamily = "'Roboto Mono', 'Bitstream Vera Sans Mono', 'Menlo', 'Consolas', 'Lucida Console', monospace";
editor.setShowPrintMargin(false); editor.setShowPrintMargin(false);
editor.renderer.updateFontSize(); editor.renderer.updateFontSize();
function submit_code() { function submit_code() {
if (!editor.getValue().trim()) return false; if (!editor.getValue().trim()) return false;
$('#submit_code input[name=language]').val($('#languages-menu .item.active').data('value')); $('#submit_code input[name=language]').val($('#languages-menu .item.active').data('value'));
$('#submit_code input[name=code]').val(editor.getValue()); $('#submit_code input[name=code]').val(editor.getValue());
return true; return true;
} }
$('#languages-menu')[0].scrollTop = $('#languages-menu .active')[0].offsetTop - $('#languages-menu')[0].firstElementChild.offsetTop; $('#languages-menu')[0].scrollTop = $('#languages-menu .active')[0].offsetTop - $('#languages-menu')[0].firstElementChild.offsetTop;
$(function () { $(function () {
$('#languages-menu .item').click(function() { $('#languages-menu .item').click(function() {
$(this) $(this)
.addClass('active') .addClass('active')
.closest('.ui.menu') .closest('.ui.menu')
.find('.item') .find('.item')
.not($(this)) .not($(this))
.removeClass('active') .removeClass('active')
; ;
editor.getSession().setMode("ace/mode/" + $(this).data('mode')); editor.getSession().setMode("ace/mode/" + $(this).data('mode'));
});
}); });
}); </script>
</script> <% } else { %>
<script>
function submit_code() {
if ($('#answer')[0].files.length === 0) return false;
}
</script>
<% } %>
<% include footer %> <% include footer %>

153
views/problem_data.ejs

@ -14,7 +14,7 @@ let subtaskType = {
<% if (problem.testdata) { %> <% if (problem.testdata) { %>
<% <%
try { try {
let list = syzoj.utils.parseTestData(problem.testdata.getPath()); let list = syzoj.utils.parseTestdata(problem.testdata.getPath());
%> %>
<% if (list.spj) { %> <% if (list.spj) { %>
<p>评测方式:Special Judge</p> <p>评测方式:Special Judge</p>
@ -51,76 +51,93 @@ let subtaskType = {
<% } %> <% } %>
</div> </div>
<div class="nine wide column"> <div class="nine wide column">
<form class="ui form" method="post" enctype="multipart/form-data"> <form class="ui form" method="post" enctype="multipart/form-data" onsubmit="return checkSubmit()">
<div class="two fields"> <input type="hidden" name="type" value="<%= problem.type %>">
<div class="field"> <div class="ui pointing secondary menu" id="problem-type-tab" style="margin-top: -10px; ">
<label for="doc-ds-ipt-1">时间限制(单位: ms)</label> <a class="<%= problem.type === 'traditional' ? 'active ' : '' %>item" data-tab="traditional">传统</a>
<input type="number" name="time_limit" value="<%= problem.time_limit %>"> <a class="<%= problem.type === 'interaction' ? 'active ' : '' %>item" data-tab="interaction">交互</a>
</div> <a class="<%= problem.type === 'submit-answer' ? 'active ' : '' %>item" data-tab="submit-answer">提交答案</a>
<div class="field"> </div>
<label for="doc-ds-ipt-1">内存限制(单位: MiB)</label> <div class="ui <%= problem.type !== 'submit-answer' ? 'active ' : '' %>tab" data-tab="traditional" data-tab="interaction">
<input type="number" name="memory_limit" value="<%= problem.memory_limit %>"> <div class="two fields">
</div> <div class="field">
<label for="doc-ds-ipt-1">时间限制(单位: ms)</label>
<input type="number" name="time_limit" value="<%= problem.time_limit %>">
</div> </div>
<% if (!problem.file_io) { %> <div class="field">
<div class="inline fields"> <label for="doc-ds-ipt-1">内存限制(单位: MiB)</label>
<label>IO 方式</label> <input type="number" name="memory_limit" value="<%= problem.memory_limit %>">
<div class="field">
<div class="ui radio checkbox">
<input name="io_method" value="std-io" id="std-io" type="radio" onclick="goDisable()" checked>
<label for="std-io">标准 IO</label>
</div> </div>
</div> </div>
<div class="field"> <% if (!problem.file_io) { %>
<div class="ui radio checkbox"> <div class="inline fields">
<input name="io_method" value="file-io" id="file-io" type="radio" onclick="goEnable()"> <label>IO 方式</label>
<label for="file-io">文件 IO</label> <div class="field">
<div class="ui radio checkbox">
<input name="io_method" value="std-io" id="std-io" type="radio" onclick="goDisable()" checked>
<label for="std-io">标准 IO</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input name="io_method" value="file-io" id="file-io" type="radio" onclick="goEnable()">
<label for="file-io">文件 IO</label>
</div>
</div> </div>
</div> </div>
</div> <div class="two fields">
<div class="two fields"> <div class="field">
<div class="field"> <label for="file_io_input_name">输入文件名</label>
<label for="file_io_input_name">输入文件名</label> <input type="text" id="file-io-input-name" name="file_io_input_name" value="<%= problem.file_io_input_name %>" disabled>
<input type="text" id="file-io-input-name" name="file_io_input_name" value="<%= problem.file_io_input_name %>" disabled> </div>
</div> <div class="field">
<div class="field"> <label for="file_io_output_name">输出文件名</label>
<label for="file_io_output_name">输出文件名</label> <input type="text" id="file-io-output-name" name="file_io_output_name" value="<%= problem.file_io_output_name %>" disabled>
<input type="text" id="file-io-output-name" name="file_io_output_name" value="<%= problem.file_io_output_name %>" disabled> </div>
</div> </div>
</div> <% } else { %>
<% } else { %> <div class="inline fields">
<div class="inline fields"> <label>IO 方式</label>
<label>IO 方式</label> <div class="field">
<div class="field"> <div class="ui radio checkbox">
<div class="ui radio checkbox"> <input name="io_method" value="std-io" id="std-io" type="radio" onclick="goDisable()">
<input name="io_method" value="std-io" id="std-io" type="radio" onclick="goDisable()"> <label for="std-io">标准 IO</label>
<label for="std-io">标准 IO</label> </div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input name="io_method" value="file-io" id="file-io" type="radio" onclick="goEnable()" checked>
<label for="file-io">文件 IO</label>
</div>
</div> </div>
</div> </div>
<div class="field"> <div class="two fields">
<div class="ui radio checkbox"> <div class="field">
<input name="io_method" value="file-io" id="file-io" type="radio" onclick="goEnable()" checked> <label for="file_io_input_name">输入文件名</label>
<label for="file-io">文件 IO</label> <input type="text" id="file-io-input-name" name="file_io_input_name" value="<%= problem.file_io_input_name %>">
</div>
<div class="field">
<label for="file_io_output_name">输出文件名</label>
<input type="text" id="file-io-output-name" name="file_io_output_name" value="<%= problem.file_io_output_name %>">
</div> </div>
</div> </div>
<% } %>
</div> </div>
<div class="two fields"> <div class="ui <%= problem.type === 'submit-answer' ? 'active ' : '' %>tab" data-tab="submit-answer" style="margin-bottom: 10px; ">
<div class="field"> <b>为了避免系统出错,已有提交的题目不允许在提交答案和非提交答案之间更改。</b><br>
<label for="file_io_input_name">输入文件名</label> 提交答案题目不需要设置时间限制、空间限制以及 IO 方式。<br>
<input type="text" id="file-io-input-name" name="file_io_input_name" value="<%= problem.file_io_input_name %>"> 提交时使用的答案文件名需要与测试数据中每个测试点输出文件相同,且后缀名为 <code>.out</code>。
</div>
<div class="field">
<label for="file_io_output_name">输出文件名</label>
<input type="text" id="file-io-output-name" name="file_io_output_name" value="<%= problem.file_io_output_name %>">
</div>
</div> </div>
<% } %>
<div class="field"> <div class="field">
<label for="testdata"><% if (!problem.testdata_id) { %>上传测试数据<% } else { %>更新测试数据<% } %></label> <label for="testdata">上传测试数据(请使用 ZIP 格式)</label>
<input type="file" id="testdata" name="testdata"> <input type="file" id="testdata" name="testdata">
</div> </div>
<button type="submit" class="ui button">提交</button> <div class="field">
<a href="<%= syzoj.utils.makeUrl(['problem', problem.id]) %>" class="ui blue button">返回题目</a> <label for="additional_file">上传附加文件(请使用 ZIP 格式)</label>
<input type="file" id="additional_file" name="additional_file">
</div>
<button type="submit" class="ui button">提交</button>
<a href="<%= syzoj.utils.makeUrl(['problem', problem.id]) %>" class="ui blue button">返回题目</a>
</form> </form>
</div> </div>
</div> </div>
@ -135,7 +152,7 @@ function goDisable() {
document.getElementById('file-io-output-name').disabled = true; document.getElementById('file-io-output-name').disabled = true;
} }
$(document).ready(function () { $(function () {
$('#file-io-input-name').on('input keyup change', function (e) { $('#file-io-input-name').on('input keyup change', function (e) {
var prob = $('#file-io-input-name').val(); var prob = $('#file-io-input-name').val();
if (prob.lastIndexOf('.') !== -1) prob = prob.substring(0, prob.lastIndexOf('.')); if (prob.lastIndexOf('.') !== -1) prob = prob.substring(0, prob.lastIndexOf('.'));
@ -146,6 +163,26 @@ $(document).ready(function () {
$('#file-io-output-name').val($('#file-io-output-name').attr('placeholder')); $('#file-io-output-name').val($('#file-io-output-name').attr('placeholder'));
} }
}); });
$('#problem-type-tab .item').tab();
$('a[data-tab="traditional"]').click(function () {
$('input[name=type]').val('traditional');
if ($('div[data-tab="interaction"]').attr('data-tab', 'traditional').length) $('a[data-tab="traditional"]').click();
});
$('a[data-tab="interaction"]').click(function () {
$('input[name=type]').val('interaction');
if ($('div[data-tab="traditional"]').attr('data-tab', 'interaction').length) $('a[data-tab="interaction"]').click();
});
$('a[data-tab="submit-answer"]').click(function () {
$('input[name=type]').val('submit-answer');
});
}); });
function checkSubmit() {
;
}
</script> </script>
<% include footer %> <% include footer %>

34
views/statistics.ejs

@ -1,11 +1,13 @@
<% <%
this.title = '统计'; this.title = '统计';
let types = { let types = {
fastest: '最快', fastest: problem.type === 'submit-answer' ? null : '最快',
slowest: '最慢', slowest: problem.type === 'submit-answer' ? null : '最慢',
shortest: '最短', shortest: problem.type === 'submit-answer' ? null : '最短',
longest: '最长', longest: problem.type === 'submit-answer' ? null : '最长',
earliest: '最早' earliest: '最早',
min: problem.type === 'submit-answer' ? '最小' : '最小内存',
max: problem.type === 'submit-answer' ? '最大' : '最大内存'
}; };
%> %>
<% include header %> <% include header %>
@ -38,7 +40,7 @@ function getColorOfScore(score) {
<i class="dropdown icon"></i> <i class="dropdown icon"></i>
<div class="menu"> <div class="menu">
<% for (let type in types) { %> <% for (let type in types) { %>
<% if (type !== statistics.type) { %> <% if (type !== statistics.type && types[type] !== null) { %>
<a class="item" href="<%= syzoj.utils.makeUrl(['problem', problem.id, 'statistics', type]) %>"><%= types[type] %></a> <a class="item" href="<%= syzoj.utils.makeUrl(['problem', problem.id, 'statistics', type]) %>"><%= types[type] %></a>
<% } %> <% } %>
<% } %> <% } %>
@ -54,9 +56,13 @@ function getColorOfScore(score) {
<th>题目</th> <th>题目</th>
<th>状态</th> <th>状态</th>
<th>分数</th> <th>分数</th>
<th>总时间</th> <% if (problem.type !== 'submit-answer') { %>
<th>内存</th> <th>总时间</th>
<th>代码</th> <th>内存</th>
<th>代码</th>
<% } else { %>
<th>答案文件</th>
<% } %>
<th>提交者</th> <th>提交者</th>
<th>提交时间</th> <th>提交时间</th>
</tr> </tr>
@ -74,9 +80,13 @@ function getColorOfScore(score) {
</span> </span>
</a></td> </a></td>
<td><a href="<%= syzoj.utils.makeUrl(['submission', judge.id]) %>"><span class="score score_<%= parseInt(judge.result.score / 10) || 0 %>"><%= judge.result.score %></span></a></td> <td><a href="<%= syzoj.utils.makeUrl(['submission', judge.id]) %>"><span class="score score_<%= parseInt(judge.result.score / 10) || 0 %>"><%= judge.result.score %></span></a></td>
<td><%= judge.result.total_time %> ms</td> <% if (problem.type !== 'submit-answer') { %>
<td><%= parseInt(judge.result.max_memory) || 0 %> K</td> <td><%= judge.result.total_time %> ms</td>
<td><a href="<%= syzoj.utils.makeUrl(['submission', judge.id]) %>"><%= syzoj.config.languages[judge.language].show %></a> / <%= syzoj.utils.formatSize(judge.code.length) %></td> <td><%= parseInt(judge.result.max_memory) || 0 %> K</td>
<td><a href="<%= syzoj.utils.makeUrl(['submission', judge.id]) %>"><%= syzoj.config.languages[judge.language].show %></a> / <%= syzoj.utils.formatSize(judge.code.length) %></td>
<% } else { %>
<td><%= syzoj.utils.formatSize(judge.max_memory) %></td>
<% } %>
<td><a href="<%= syzoj.utils.makeUrl(['user', judge.user_id]) %>"><%= judge.user.username %></a><% if (judge.user.nameplate) { %><%- judge.user.nameplate %><% } %></td> <td><a href="<%= syzoj.utils.makeUrl(['user', judge.user_id]) %>"><%= judge.user.username %></a><% if (judge.user.nameplate) { %><%- judge.user.nameplate %><% } %></td>
<td><%= syzoj.utils.formatDate(judge.submit_time) %></td> <td><%= syzoj.utils.formatDate(judge.submit_time) %></td>
</tr> </tr>

29
views/submission_content.ejs

@ -1,3 +1,4 @@
<% include util %>
<% <%
// Sanitize judge results for backward compatibility and clarity // Sanitize judge results for backward compatibility and clarity
if (!judge.result.subtasks) { if (!judge.result.subtasks) {
@ -38,9 +39,13 @@ else problemUrl = syzoj.utils.makeUrl(['problem', judge.problem_id]);
<% if ((typeof contest === 'undefined' || !contest) || !((!user || !user.is_admin) && !contest.ended && (contest.type === 'acm' || contest.type === 'noi'))) { %> <% if ((typeof contest === 'undefined' || !contest) || !((!user || !user.is_admin) && !contest.ended && (contest.type === 'acm' || contest.type === 'noi'))) { %>
<th>分数</th> <th>分数</th>
<% } %> <% } %>
<th>总时间</th> <% if (judge.problem.type !== 'submit-answer') { %>
<th>内存</th> <th>总时间</th>
<th>代码</th> <th>内存</th>
<th>代码</th>
<% } else { %>
<th>文件大小</th>
<% } %>
<th>提交者</th> <th>提交者</th>
<th>提交时间</th> <th>提交时间</th>
</tr> </tr>
@ -56,12 +61,16 @@ else problemUrl = syzoj.utils.makeUrl(['problem', judge.problem_id]);
<% if ((typeof contest === 'undefined' || !contest) || !((!user || !user.is_admin) && !contest.ended && (contest.type === 'acm' || contest.type === 'noi'))) { %> <% if ((typeof contest === 'undefined' || !contest) || !((!user || !user.is_admin) && !contest.ended && (contest.type === 'acm' || contest.type === 'noi'))) { %>
<td class="score score_<%= parseInt(judge.result.score / 10) || 0 %>"><%= judge.result.score %></td> <td class="score score_<%= parseInt(judge.result.score / 10) || 0 %>"><%= judge.result.score %></td>
<% } %> <% } %>
<td><%= judge.result.total_time %> ms</td> <% if (judge.problem.type !== 'submit-answer') { %>
<td><%= parseInt(judge.result.max_memory) || 0 %> K</td> <td><%= judge.result.total_time %> ms</td>
<% if (judge.allowedSeeCode) { %> <td><%= parseInt(judge.result.max_memory) || 0 %> K</td>
<td><%= syzoj.config.languages[judge.language].show %> / <%= syzoj.utils.formatSize(judge.codeLength) %></td> <% if (judge.allowedSeeCode) { %>
<td><%= syzoj.config.languages[judge.language].show %> / <%= syzoj.utils.formatSize(judge.codeLength) %></td>
<% } else { %>
<td><%= syzoj.config.languages[judge.language].show %> / 隐藏 %></td>
<% } %>
<% } else { %> <% } else { %>
<td><%= syzoj.config.languages[judge.language].show %> / 隐藏 %></td> <td><%= syzoj.utils.formatSize(judge.max_memory) %></td>
<% } %> <% } %>
<td><a href="<%= syzoj.utils.makeUrl(['user', judge.user_id]) %>"><%= judge.user.username %></a><% if (judge.user.nameplate) { %><%- judge.user.nameplate %><% } %></td> <td><a href="<%= syzoj.utils.makeUrl(['user', judge.user_id]) %>"><%= judge.user.username %></a><% if (judge.user.nameplate) { %><%- judge.user.nameplate %><% } %></td>
<td><%= syzoj.utils.formatDate(judge.submit_time) %></td> <td><%= syzoj.utils.formatDate(judge.submit_time) %></td>
@ -76,7 +85,7 @@ else problemUrl = syzoj.utils.makeUrl(['problem', judge.problem_id]);
} }
window.applyTextFit(); window.applyTextFit();
</script> </script>
<% if (judge.allowedSeeCode) { %> <% if (judge.problem.type !== 'submit-answer' && judge.allowedSeeCode) { %>
<div class="ui existing segment" style="position: relative; "> <div class="ui existing segment" style="position: relative; ">
<% if (judge.allowedRejudge) { %> <% if (judge.allowedRejudge) { %>
<a id="rejudge-button" href="<%= syzoj.utils.makeUrl(['submission', judge.id, 'rejudge']) %>" class="ui button" style="position: absolute; top: 0px; right: -4px; border-top-left-radius: 0; border-bottom-right-radius: 0; <% if (judge.pending) { %>display: none; <% } %>">重新评测</a> <a id="rejudge-button" href="<%= syzoj.utils.makeUrl(['submission', judge.id, 'rejudge']) %>" class="ui button" style="position: absolute; top: 0px; right: -4px; border-top-left-radius: 0; border-bottom-right-radius: 0; <% if (judge.pending) { %>display: none; <% } %>">重新评测</a>
@ -96,7 +105,7 @@ else problemUrl = syzoj.utils.makeUrl(['problem', judge.problem_id]);
}); });
</script> </script>
<% } %> <% } %>
<% if (judge.result.compiler_output && judge.status === 'Compile Error' && judge.allowedSeeCode) { %> <% if (judge.problem.type !== 'submit-answer' && judge.result.compiler_output && judge.status === 'Compile Error' && judge.allowedSeeCode) { %>
<h3 class="ui header">编译信息</h3> <h3 class="ui header">编译信息</h3>
<div class="ui existing segment"><pre style="margin-top: 0; margin-bottom: 0; "><code><%- syzoj.utils.ansiToHTML(judge.result.compiler_output) %></code></pre></div> <div class="ui existing segment"><pre style="margin-top: 0; margin-bottom: 0; "><code><%- syzoj.utils.ansiToHTML(judge.result.compiler_output) %></code></pre></div>
<% } else if (judge.result.spj_compiler_output) { %> <% } else if (judge.result.spj_compiler_output) { %>

3
views/submissions.ejs

@ -21,6 +21,7 @@
<div class="default text"></div> <div class="default text"></div>
<div class="menu"> <div class="menu">
<div class="item" data-value="">不限</div> <div class="item" data-value="">不限</div>
<div class="item" data-value="submit-answer">提交答案</div>
<% for (let lang in syzoj.config.languages) { %> <% for (let lang in syzoj.config.languages) { %>
<div class="item" data-value="<%= lang %>"><%= syzoj.config.languages[lang].show %></div> <div class="item" data-value="<%= lang %>"><%= syzoj.config.languages[lang].show %></div>
<% } %> <% } %>
@ -70,7 +71,7 @@
<th>分数</th> <th>分数</th>
<th>总时间</th> <th>总时间</th>
<th>内存</th> <th>内存</th>
<th>代码</th> <th>代码 / 答案文件</th>
<th>提交者</th> <th>提交者</th>
<th>提交时间</th> <th>提交时间</th>
</tr> </tr>

5
views/submissions_item.ejs

@ -19,6 +19,7 @@ textFit(e, { maxFontSize: 14 });
<% if ((typeof contest === 'undefined' || !contest) || !((!user || !user.is_admin) && !contest.ended && (contest.type === 'acm' || contest.type === 'noi'))) { %> <% if ((typeof contest === 'undefined' || !contest) || !((!user || !user.is_admin) && !contest.ended && (contest.type === 'acm' || contest.type === 'noi'))) { %>
<td><a href="<%= syzoj.utils.makeUrl(['submission', judge.id]) %>"><span class="score score_<%= parseInt(judge.result.score / 10) || 0 %>"><%= judge.result.score %></span></a></td> <td><a href="<%= syzoj.utils.makeUrl(['submission', judge.id]) %>"><span class="score score_<%= parseInt(judge.result.score / 10) || 0 %>"><%= judge.result.score %></span></a></td>
<% } %> <% } %>
<% if (judge.problem.type !== 'submit-answer') { %>
<td><%= judge.result.total_time %> ms</td> <td><%= judge.result.total_time %> ms</td>
<td><%= parseInt(judge.result.max_memory) || 0 %> K</td> <td><%= parseInt(judge.result.max_memory) || 0 %> K</td>
<% if (judge.allowedSeeCode) { %> <% if (judge.allowedSeeCode) { %>
@ -26,6 +27,10 @@ textFit(e, { maxFontSize: 14 });
<% } else { %> <% } else { %>
<td><a href="<%= syzoj.utils.makeUrl(['submission', judge.id]) %>"><%= syzoj.config.languages[judge.language].show %></a> / 隐藏</td> <td><a href="<%= syzoj.utils.makeUrl(['submission', judge.id]) %>"><%= syzoj.config.languages[judge.language].show %></a> / 隐藏</td>
<% } %> <% } %>
<% } else { %>
<td>-</td><td>-</td>
<td><%= syzoj.utils.formatSize(judge.max_memory) %></td>
<% } %>
<td><a href="<%= syzoj.utils.makeUrl(['user', judge.user_id]) %>"><%= judge.user.username %></a><% if (judge.user.nameplate) { %><%- judge.user.nameplate %><% } %></td> <td><a href="<%= syzoj.utils.makeUrl(['user', judge.user_id]) %>"><%= judge.user.username %></a><% if (judge.user.nameplate) { %><%- judge.user.nameplate %><% } %></td>
<td><%= syzoj.utils.formatDate(judge.submit_time) %> <td><%= syzoj.utils.formatDate(judge.submit_time) %>
<% if (isPending(judge.status)) { %> <% if (isPending(judge.status)) { %>

Loading…
Cancel
Save