Browse Source

Don't update zip after each upload of single file; Add delete file in

testdata page
pull/6/head
Menci 8 years ago
parent
commit
c9c2d29f7c
  1. 28
      models/problem.js
  2. 25
      modules/problem.js
  3. 89
      views/problem_data.ejs
  4. 4
      views/problem_testcases.ejs

28
models/problem.js

@ -349,12 +349,25 @@ class Problem extends Model {
if (oldSize + size > syzoj.config.limit.testdata) throw new ErrorMessage('数据包太大。'); if (oldSize + size > syzoj.config.limit.testdata) throw new ErrorMessage('数据包太大。');
await fs.moveAsync(filepath, path.join(dir, filename), { overwrite: true }); await fs.moveAsync(filepath, path.join(dir, filename), { overwrite: true });
await fs.removeAsync(dir + '.zip');
}
async deleteTestdataSingleFile(filename) {
let dir = this.getTestdataPath();
let fs = Promise.promisifyAll(require('fs-extra')), path = require('path');
await fs.removeAsync(path.join(dir, filename));
await fs.removeAsync(dir + '.zip');
}
async makeTestdataZip() {
let dir = this.getTestdataPath();
if (!await syzoj.utils.isDir(dir)) throw new ErrorMessage('无测试数据。');
let AdmZip = require('adm-zip'); let AdmZip = require('adm-zip');
let zip = new AdmZip(); let zip = new AdmZip();
list = await this.listTestdata(); let list = await this.listTestdata();
for (let file of list.files) zip.addLocalFile(path.join(dir, file.filename), '', file.filename); for (let file of list.files) zip.addLocalFile(require('path').join(dir, file.filename), '', file.filename);
zip.writeZip(dir + '.zip'); zip.writeZip(dir + '.zip');
} }
@ -380,7 +393,7 @@ class Problem extends Model {
return { return {
filename: x, filename: x,
size: stat.size size: stat.size
} };
}); });
list = list.filter(x => x); list = list.filter(x => x);
@ -395,13 +408,18 @@ class Problem extends Model {
if (stat.isFile()) { if (stat.isFile()) {
res.zip = { res.zip = {
size: stat.size size: stat.size
};
}
} catch (e) {
if (list) {
res.zip = {
size: null
};
} }
} }
} catch (e) {}
return res; return res;
} catch (e) { } catch (e) {
console.log(e);
return null; return null;
} }
} }

25
modules/problem.js

@ -676,6 +676,25 @@ app.post('/problem/:id/testdata/upload', app.multer.array('file'), async (req, r
} }
}); });
app.get('/problem/:id/testdata/delete/:filename', async (req, res) => {
try {
let id = parseInt(req.params.id);
let problem = await Problem.fromID(id);
if (!problem) throw new ErrorMessage('无此题目。');
if (!await problem.isAllowedEditBy(res.locals.user)) throw new ErrorMessage('您没有权限进行此操作。');
await problem.deleteTestdataSingleFile(req.params.filename);
res.redirect(syzoj.utils.makeUrl(['problem', id, 'testdata']));
} catch (e) {
syzoj.log(e);
res.render('error', {
err: e
});
}
});
app.get('/problem/:id/testdata/download/:filename?', async (req, res) => { app.get('/problem/:id/testdata/download/:filename?', async (req, res) => {
try { try {
let id = parseInt(req.params.id); let id = parseInt(req.params.id);
@ -684,6 +703,12 @@ app.get('/problem/:id/testdata/download/:filename?', async (req, res) => {
if (!problem) throw new ErrorMessage('无此题目。'); if (!problem) throw new ErrorMessage('无此题目。');
if (!await problem.isAllowedUseBy(res.locals.user)) throw new ErrorMessage('您没有权限进行此操作。'); if (!await problem.isAllowedUseBy(res.locals.user)) throw new ErrorMessage('您没有权限进行此操作。');
if (!req.params.filename) {
if (!await syzoj.utils.isFile(problem.getTestdataPath() + '.zip')) {
await problem.makeTestdataZip();
}
}
let path = require('path'); let path = require('path');
let filename = req.params.filename ? path.join(problem.getTestdataPath(), req.params.filename) : (problem.getTestdataPath() + '.zip'); let filename = req.params.filename ? path.join(problem.getTestdataPath(), req.params.filename) : (problem.getTestdataPath() + '.zip');
if (!await syzoj.utils.isFile(filename)) throw new ErrorMessage('文件不存在。'); if (!await syzoj.utils.isFile(filename)) throw new ErrorMessage('文件不存在。');

89
views/problem_data.ejs

@ -35,6 +35,11 @@ function getIcon(filename) {
} }
%> %>
<% include header %> <% include header %>
<style>
.ui.page.dimmer {
position: fixed;
}
</style>
<div class="padding"> <div class="padding">
<div class="ui grid"> <div class="ui grid">
<div class="seven wide column"> <div class="seven wide column">
@ -49,22 +54,60 @@ function getIcon(filename) {
<tr> <tr>
<th class="left aligned">文件名</th> <th class="left aligned">文件名</th>
<th style="width: 100px">文件大小</th> <th style="width: 100px">文件大小</th>
<th style="width: 50px">操作</th> <th style="width: 35px">下载</th>
<% if (problem.allowedEdit) { %>
<th style="width: 35px">删除</th>
<% } %>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<% if (testdata.zip) { %> <% if (testdata.zip) { %>
<tr> <tr>
<td class="left aligned"><i class="file archive outline icon"></i> 完整数据包</td> <td class="left aligned"><i class="file archive outline icon"></i> 完整数据包</td>
<td><%= syzoj.utils.formatSize(testdata.zip.size) %></td> <td><%- !testdata.zip.size ? '<i class="minus icon"></i>' : syzoj.utils.formatSize(testdata.zip.size) %></td>
<td><a style="color: #000; " href="<%= syzoj.utils.makeUrl(['problem', problem.id, 'testdata', 'download']) %>"><i class="download icon"></i></td> <td><a style="color: #000; " href="<%= syzoj.utils.makeUrl(['problem', problem.id, 'testdata', 'download']) %>"><i class="download icon"></i></td>
<% if (problem.allowedEdit) { %>
<td><i class="minus icon"></i></td>
<% } %>
</tr> </tr>
<% } %> <% } %>
<% let i = 0; %>
<% if (testdata.files) for (let file of testdata.files) { %> <% if (testdata.files) for (let file of testdata.files) { %>
<% i++; %>
<tr> <tr>
<td class="left aligned"><i class="<%= getIcon(file.filename) %> icon"></i> <%= file.filename %></td> <td class="left aligned"><i class="<%= getIcon(file.filename) %> icon"></i> <%= file.filename %></td>
<td><%= syzoj.utils.formatSize(file.size) %></td> <td><%= syzoj.utils.formatSize(file.size) %></td>
<td><a style="color: #000; " href="<%= syzoj.utils.makeUrl(['problem', problem.id, 'testdata', 'download', file.filename]) %>"><i class="download icon"></i></td> <td>
<a style="color: #000; " href="<%= syzoj.utils.makeUrl(['problem', problem.id, 'testdata', 'download', file.filename]) %>">
<i class="download icon"></i>
</a>
</td>
<% if (problem.allowedEdit) { %>
<td>
<a style="color: #000; " onclick="$('#modal-delete-<%= i %>').modal('show')">
<i class="remove icon"></i>
</a>
<div class="ui basic modal" id="modal-delete-<%= i %>">
<div class="ui icon header">
<i class="trash icon"></i>
<p style="margin-top: 15px; ">删除文件</p>
</div>
<div class="content" style="text-align: center; ">
<p>确认删除「 <samp><%= file.filename %></samp> 」吗?</p>
</div>
<div class="actions">
<div class="ui red basic cancel inverted button">
<i class="remove icon"></i>
</div>
<a class="ui green ok inverted button" href="<%= syzoj.utils.makeUrl(['problem', problem.id, 'testdata', 'delete', file.filename]) %>">
<i class="checkmark icon"></i>
</a>
</div>
</div>
</td>
<% } %>
</tr> </tr>
<% } %> <% } %>
</tbody> </tbody>
@ -73,18 +116,52 @@ function getIcon(filename) {
<h4 style="text-align: center; ">无测试数据</h4> <h4 style="text-align: center; ">无测试数据</h4>
<% } %> <% } %>
<% if (problem.allowedEdit) { %> <% if (problem.allowedEdit) { %>
<form class="ui form" action="<%= syzoj.utils.makeUrl(['problem', problem.id, 'testdata', 'upload']) %>" method="post" enctype="multipart/form-data"> <form id="form_upload" class="ui form" action="<%= syzoj.utils.makeUrl(['problem', problem.id, 'testdata', 'upload']) %>" method="post" enctype="multipart/form-data">
<div class="inline fields"> <div class="inline fields">
<div class="field" style="margin: 0 auto; "> <div class="field" style="margin: 0 auto; ">
<label for="answer">上传文件(可一次性上传多个)</label> <label for="answer">上传文件(可一次性上传多个)</label>
<input type="file" name="file" multiple> <input type="file" name="file" multiple id="upload_file">
<div class="ui center aligned vertical segment" style="padding-bottom: 0; "> <div class="ui center aligned vertical segment" style="padding-bottom: 0; ">
<button type="submit" class="ui button">提交</button> <div class="ui button" onclick="check_replace()">提交</div>
<a href="<%= syzoj.utils.makeUrl(['problem', problem.id]) %>" class="ui blue button">返回题目</a> <a href="<%= syzoj.utils.makeUrl(['problem', problem.id]) %>" class="ui blue button">返回题目</a>
</div> </div>
</div> </div>
</div> </div>
</form> </form>
<div class="ui basic modal" id="modal-replace">
<div class="ui icon header">
<i class="refresh icon"></i>
<p style="margin-top: 15px; ">替换文件</p>
</div>
<div class="content" style="text-align: center; ">
<p>确认替换以下文件吗?</p>
<div style="display: inline-block; text-align: left; " id="replaced_files"></div>
</div>
<div class="actions">
<div class="ui red basic cancel inverted button">
<i class="remove icon"></i>
</div>
<a class="ui green ok inverted button" onclick="$('#form_upload').submit()">
<i class="checkmark icon"></i>
</a>
</div>
</div>
<script>
function check_replace() {
var old_files = <%- JSON.stringify((testdata && testdata.files ? testdata.files : []).map(x => x.filename)) %>;
var replaced_files = Array.from($('#upload_file')[0].files).map(function (x) { return x.name; }).filter(function (x) { return old_files.includes(x); });
var s = '';
for (let file of replaced_files) s += '<samp>' + file + '</samp><br>';
if (s) {
$('#replaced_files').html(s);
$('#modal-replace').modal('show');
} else {
$('#form_upload').submit();
}
}
</script>
<% } %> <% } %>
</div> </div>
</div> </div>

4
views/problem_testcases.ejs

@ -30,8 +30,8 @@ let subtaskType = {
<% } %> <% } %>
<% for (let testcase of subtask.cases) { %> <% for (let testcase of subtask.cases) { %>
<tr class="center aligned"> <tr class="center aligned">
<td><%= testcase.input %></td> <td style="width: 50%; "><%= testcase.input %></td>
<td><%= testcase.output %></td> <td style="width: 50%; "><%= testcase.output %></td>
</tr> </tr>
<% } %> <% } %>
<% } %> <% } %>

Loading…
Cancel
Save