diff --git a/models/judge_state.js b/models/judge_state.js index 1943b91..27ae41a 100644 --- a/models/judge_state.js +++ b/models/judge_state.js @@ -217,7 +217,8 @@ class JudgeState extends Model { let WaitingJudge = syzoj.model('waiting_judge'); let waiting_judge = await WaitingJudge.create({ judge_id: this.id, - priority: 2 + priority: 2, + type: 'submission' }); await waiting_judge.save(); diff --git a/models/waiting_judge.js b/models/waiting_judge.js index 82b15b6..4a1508f 100644 --- a/models/waiting_judge.js +++ b/models/waiting_judge.js @@ -29,7 +29,12 @@ let model = db.define('waiting_judge', { judge_id: { type: Sequelize.INTEGER }, // Smaller is higher - priority: { type: Sequelize.INTEGER } + priority: { type: Sequelize.INTEGER }, + + type: { + type: Sequelize.ENUM, + values: ['submission', 'custom-test'] + } }, { timestamps: false, tableName: 'waiting_judge', diff --git a/modules/api_v2.js b/modules/api_v2.js index 0ec9639..8a00142 100644 --- a/modules/api_v2.js +++ b/modules/api_v2.js @@ -89,30 +89,32 @@ app.apiRouter.post('/api/v2/judge/peek', async (req, res) => { let WaitingJudge = syzoj.model('waiting_judge'); let JudgeState = syzoj.model('judge_state'); - let judge_state; + let judge_state, custom_test; await syzoj.utils.lock('/api/v2/judge/peek', async () => { let waiting_judge = await WaitingJudge.findOne({ order: [['priority', 'ASC'], ['id', 'ASC']] }); if (!waiting_judge) { return; } - judge_state = await waiting_judge.getJudgeState(); - await judge_state.loadRelationships(); - await judge_state.problem.loadRelationships(); + if (waiting_judge.type === 'submission') { + judge_state = await waiting_judge.getJudgeState(); + await judge_state.loadRelationships(); + } else { + custom_test = await waiting_judge.getCustomTest(); + await custom_test.loadRelationships(); + } await waiting_judge.destroy(); }); if (judge_state) { - await judge_state.loadRelationships(); - await judge_state.problem.loadRelationships(); - if (judge_state.problem.type === 'submit-answer') { res.send({ have_task: 1, judge_id: judge_state.id, answer_file: judge_state.code, testdata: judge_state.problem.id, - problem_type: judge_state.problem.type + problem_type: judge_state.problem.type, + type: 'submission' }); } else { res.send({ @@ -126,9 +128,25 @@ app.apiRouter.post('/api/v2/judge/peek', async (req, res) => { 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 + problem_type: judge_state.problem.type, + type: 'submission' }); } + } else if (custom_test) { + console.log({ + have_task: 1, + judge_id: custom_test.id, + code: custom_test.code, + language: custom_test.language, + testdata: custom_test.problem.id, + time_limit: custom_test.problem.time_limit, + memory_limit: custom_test.problem.memory_limit, + file_io: custom_test.problem.file_io, + file_io_input_name: custom_test.problem.file_io_input_name, + file_io_output_name: custom_test.problem.file_io_output_name, + problem_type: custom_test.problem.type, + type: 'custom_test' + }); } else { res.send({ have_task: 0 }); } @@ -141,11 +159,18 @@ app.apiRouter.post('/api/v2/judge/update/:id', async (req, res) => { try { if (req.query.session_id !== syzoj.config.judge_token) return res.status(404).send({ err: 'Permission denied' }); - let JudgeState = syzoj.model('judge_state'); - let judge_state = await JudgeState.fromID(req.params.id); - await judge_state.updateResult(JSON.parse(req.body.result)); - await judge_state.save(); - await judge_state.updateRelatedInfo(); + if (req.body.type === 'custom-test') { + let CustomTest = syzoj.model('custom_test'); + let custom_test = CustomTest.fromID(req.params.id); + await custom_test.updateResult(JSON.parse(req.body.result)); + await custom_test.save(); + } else if (req.body.type === 'submission') { + let JudgeState = syzoj.model('judge_state'); + let judge_state = await JudgeState.fromID(req.params.id); + await judge_state.updateResult(JSON.parse(req.body.result)); + await judge_state.save(); + await judge_state.updateRelatedInfo(); + } res.send({ return: 0 }); } catch (e) { diff --git a/modules/problem.js b/modules/problem.js index e637a72..e558ba5 100644 --- a/modules/problem.js +++ b/modules/problem.js @@ -21,6 +21,7 @@ let Problem = syzoj.model('problem'); let JudgeState = syzoj.model('judge_state'); +let CustomTest = syzoj.model('custom_test'); let WaitingJudge = syzoj.model('waiting_judge'); let Contest = syzoj.model('contest'); let ProblemTag = syzoj.model('problem_tag'); @@ -638,7 +639,8 @@ app.post('/problem/:id/submit', app.multer.fields([{ name: 'answer', maxCount: 1 let waiting_judge = await WaitingJudge.create({ judge_id: judge_state.id, - priority: 1 + priority: 1, + type: 'submission' }); await waiting_judge.save(); @@ -817,3 +819,60 @@ app.get('/problem/:id/statistics/:type', async (req, res) => { }); } }); + +app.post('/problem/:id/custom-test', app.multer.fields([{ name: 'code_upload', maxCount: 1 }, { name: 'input_file', maxCount: 1 }]), async (req, res) => { + try { + let id = parseInt(req.params.id); + let problem = await Problem.fromID(id); + + if (!problem) throw new ErrorMessage('无此题目。'); + if (!res.locals.user) throw new ErrorMessage('请登录后继续。', { '登录': syzoj.utils.makeUrl(['login'], { 'url': syzoj.utils.makeUrl(['problem', id]) }) }); + if (!await problem.isAllowedUseBy(res.locals.user)) throw new ErrorMessage('您没有权限进行此操作。'); + + let filepath; + if (req.files['input_file'][0]) { + if (req.files['input_file'][0].size > syzoj.config.limit.custom_test_input) throw new ErrorMessage('输入数据过长。'); + filepath = req.files['input_file'][0].path; + } else { + if (req.body.input_file_textarea.length > syzoj.config.limit.custom_test_input) throw new ErrorMessage('输入数据过长。'); + filepath = await require('tmp-promise').tmpName({ template: '/tmp/tmp-XXXXXX' }); + await require('fs-extra').writeFileAsync(filepath, req.body.input_file_textarea); + } + + let code; + if (req.files['code_upload'][0]) { + if (req.files['code_upload'][0].size > syzoj.config.limit.submit_code) throw new ErrorMessage('代码过长。'); + code = (await require('fs-extra').readFileAsync(req.files['code_upload'][0].path)).toString(); + } else { + if (req.body.code.length > syzoj.config.limit.submit_code) throw new ErrorMessage('代码过长。'); + code = req.body.code; + } + + let custom_test = await CustomTest.create({ + input_filepath: filepath, + code: code, + language: req.body.language, + user_id: res.locals.user.id, + problem_id: id + }); + + await custom_test.save(); + + let waiting_judge = await WaitingJudge.create({ + judge_id: custom_test.id, + priority: 3, + type: 'custom_test' + }); + + await waiting_judge.save(); + + res.send({ + id: custom_test.id + }); + } catch (e) { + syzoj.log(e); + res.send({ + err: e + }); + } +}); diff --git a/static/style.css b/static/style.css index 6d4b905..06d9020 100644 --- a/static/style.css +++ b/static/style.css @@ -361,6 +361,9 @@ body > .ui.page.dimmer { } */ +:not(.status_detail).status.success, +.title:hover .status_detail.status.success, +.title.active .status_detail.status.success, :not(.status_detail).status.submitted, .title:hover .status_detail.status.submitted, .title.active .status_detail.status.submitted, diff --git a/views/custom_test_content.ejs b/views/custom_test_content.ejs new file mode 100644 index 0000000..499ccf0 --- /dev/null +++ b/views/custom_test_content.ejs @@ -0,0 +1,30 @@ +<% include util %> +
+
+
style="cursor: auto; "<% } %>> +
+
自定义测试
+
+ + <%= custom_test.status %>
+ <% if (!custom_test.pending) { %> +
用时:<%= custom_test.time %> ms
+
内存:<%= custom_test.memory %> KiB
+ <% } %> +
+
+ <% if (!custom_test.pending) { %> +
+ 选手输出 +
<%= custom_test.result.user_out %>
+
+ <% } %> +
+ <% if (!custom_test.pending) { %> + + <% } %> +
diff --git a/views/problem.ejs b/views/problem.ejs index ab79774..d3179ec 100644 --- a/views/problem.ejs +++ b/views/problem.ejs @@ -300,6 +300,7 @@ div[class*=ace_br] { +
<% } else { %> @@ -330,8 +331,11 @@ div[class*=ace_br] { +
+ +
自定义测试
+
<% } %> -
@@ -376,6 +380,86 @@ div[class*=ace_br] { }); }); + + + + <% } else { %>