Browse Source

Allow admin to change problem id

master
Menci 8 years ago
parent
commit
89bac68583
  1. 2
      README.md
  2. 15
      app.js
  3. 16
      models/article-comment.js
  4. 8
      models/article.js
  5. 4
      models/common.js
  6. 6
      models/contest.js
  7. 16
      models/contest_player.js
  8. 16
      models/judge_state.js
  9. 33
      models/problem.js
  10. 6
      models/problem_tag_map.js
  11. 8
      models/waiting_judge.js
  12. 8
      modules/problem.js
  13. 16
      views/problem_edit.ejs

2
README.md

@ -17,7 +17,7 @@ Install dependencies with `npm install` or `yarn`.
Copy the `config-example.json` file to `config.json`, and change the configures.
## Database
SYZOJ 2 uses [Sequelize](http://sequelizejs.com), which supports many database systems, including MySQL and Sqlite.
SYZOJ 2 uses [Sequelize](http://sequelizejs.com), which supports many database systems, including MySQL and Sqlite. But SYZOJ 2 supports **only** MySQL and Sqlite.
By default it use the Sqlite database `syzoj.db`, you can change it in `config.json`

15
app.js

@ -30,7 +30,7 @@ global.syzoj = {
log(obj) {
console.log(obj);
},
run() {
async run() {
let Express = require('express');
global.app = Express();
@ -60,11 +60,11 @@ global.syzoj = {
let multer = require('multer');
app.multer = multer({ dest: syzoj.utils.resolvePath(syzoj.config.upload_dir, 'tmp') });
this.connectDatabase();
await this.connectDatabase();
this.loadHooks();
this.loadModules();
},
connectDatabase() {
async connectDatabase() {
let Sequelize = require('sequelize');
this.db = new Sequelize(this.config.db.database, this.config.db.username, this.config.db.password, {
host: this.config.db.host,
@ -75,6 +75,15 @@ global.syzoj = {
global.Promise = Sequelize.Promise;
this.db.countQuery = async (sql, options) => (await this.db.query(`SELECT COUNT(*) FROM (${sql}) AS \`__tmp_table\``, options))[0][0]['COUNT(*)'];
if (this.config.db.dialect.toLowerCase() === 'mysql') {
await this.db.query('SET foreign_key_checks = 0;');
} else if (this.config.db.dialect.toLowerCase() === 'sqlite') {
await this.db.query('PRAGMA foreign_keys = OFF;');
} else {
this.log('Unsupported database: ' + this.config.db.dialect);
process.exit();
}
this.loadModels();
},
loadModules() {

16
models/article-comment.js

@ -30,21 +30,9 @@ let model = db.define('comment', {
content: { type: Sequelize.TEXT },
article_id: {
type: Sequelize.INTEGER,
references: {
model: 'article',
key: 'id'
}
},
article_id: { type: Sequelize.INTEGER },
user_id: {
type: Sequelize.INTEGER,
references: {
model: 'user',
key: 'id'
}
},
user_id: { type: Sequelize.INTEGER },
public_time: { type: Sequelize.INTEGER }
}, {

8
models/article.js

@ -30,13 +30,7 @@ let model = db.define('article', {
title: { type: Sequelize.STRING(80) },
content: { type: Sequelize.TEXT },
user_id: {
type: Sequelize.INTEGER,
references: {
model: 'user',
key: 'id'
}
},
user_id: { type: Sequelize.INTEGER },
public_time: { type: Sequelize.INTEGER },
update_time: { type: Sequelize.INTEGER },

4
models/common.js

@ -83,6 +83,10 @@ class Model {
return this.fromRecord(this.model.findOne(options));
}
static async all() {
return (await this.model.findAll()).mapAsync(record => (this.fromRecord(record)));
}
static async count(where) {
// count(sql)
if (typeof where === 'string') {

6
models/contest.js

@ -92,10 +92,14 @@ class Contest extends Model {
}
async getProblems() {
if (this.problems === '') return [];
if (!this.problems) return [];
return this.problems.split('|').map(x => parseInt(x));
}
async setProblemsNoCheck(problemIDs) {
this.problems = problemIDs.join('|');
}
async setProblems(s) {
let a = [];
await s.split('|').forEachAsync(async x => {

16
models/contest_player.js

@ -28,20 +28,8 @@ let Contest = syzoj.model('contest');
let model = db.define('contest_player', {
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
contest_id: {
type: Sequelize.INTEGER,
references: {
model: 'contest',
key: 'id'
}
},
user_id: {
type: Sequelize.INTEGER,
references: {
model: 'user',
key: 'id'
}
},
contest_id: { type: Sequelize.INTEGER },
user_id: { type: Sequelize.INTEGER },
score: { type: Sequelize.INTEGER },
score_details: { type: Sequelize.TEXT, json: true },

16
models/judge_state.js

@ -39,21 +39,9 @@ let model = db.define('judge_state', {
result: { type: Sequelize.TEXT('medium'), json: true },
user_id: {
type: Sequelize.INTEGER,
references: {
model: User.model,
key: 'id'
}
},
user_id: { type: Sequelize.INTEGER },
problem_id: {
type: Sequelize.INTEGER,
references: {
model: Problem.model,
key: 'id'
}
},
problem_id: { type: Sequelize.INTEGER },
submit_time: { type: Sequelize.INTEGER },
/*

33
models/problem.js

@ -255,8 +255,9 @@ class Problem extends Model {
let tmp = this.testdata_id;
this.testdata_id = null;
await this.save();
let file = await TestData.fromID(tmp);
await file.destroy();
if (file) await file.destroy();
}
let filename = `test_data_${this.id}.zip`;
@ -425,6 +426,36 @@ class Problem extends Model {
}
}
async changeID(id) {
id = parseInt(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 `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 contests = await Contest.all();
for (let contest of contests) {
let problemIDs = await contest.getProblems();
let flag = false;
for (let i in problemIDs) {
if (problemIDs[i] === this.id) {
problemIDs[i] = id;
flag = true;
}
}
if (flag) {
await contest.setProblemsNoCheck(problemIDs);
await contest.save();
}
}
this.id = id;
this.save();
}
getModel() { return model; }
}

6
models/problem_tag_map.js

@ -26,11 +26,7 @@ let model = db.define('problem_tag_map', {
problem_id: { type: Sequelize.INTEGER, primaryKey: true },
tag_id: {
type: Sequelize.INTEGER,
primaryKey: true,
references: {
model: 'problem_tag',
key: 'id'
}
primaryKey: true
}
}, {
timestamps: false,

8
models/waiting_judge.js

@ -26,13 +26,7 @@ let JudgeState = syzoj.model('judge_state');
let model = db.define('waiting_judge', {
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
judge_id: {
type: Sequelize.INTEGER,
references: {
model: 'judge_state',
key: 'id'
}
}
judge_id: { type: Sequelize.INTEGER }
}, {
timestamps: false,
tableName: 'waiting_judge',

8
modules/problem.js

@ -204,6 +204,14 @@ app.post('/problem/:id/edit', async (req, res) => {
} else {
if (!await problem.isAllowedUseBy(res.locals.user)) throw 'Permission denied.';
if (!await problem.isAllowedEditBy(res.locals.user)) throw 'Permission denied.';
if (res.locals.user.is_admin) {
let customID = parseInt(req.body.id);
if (customID && customID !== id) {
if (await Problem.fromID(customID)) throw 'The ID has been used.';
await problem.changeID(customID);
}
}
}
problem.title = req.body.title;

16
views/problem_edit.ejs

@ -13,9 +13,19 @@
</div>
<div class="ui bottom attached tab segment active" data-tab="edit">
<div class="field">
<label for="id">题目编号</label>
<input type="text" id="id" name="id" placeholder="留空则自动延伸" value="<%= problem.id ? problem.id : '' %>" <%= problem.new ? '' : 'disabled' %>>
<label style="margin-top: 15px; " for="title">题目名称</label>
<% if (problem.new || user.is_admin) { %>
<label for="id">
<% if (problem.new) { %>
题目编号
<% } else { %>
修改题目编号
<% } %>
</label>
<input type="text" id="id" name="id" placeholder="<% if (problem.new) { %>留空则自动延伸<% } else { %>留空则不修改<% } %>" value="<%= problem.id ? problem.id : '' %>">
<div style="margin-top: 15px; "></div>
<% } %>
<label for="title">题目名称</label>
<input type="text" id="title" name="title" value="<%= problem.title %>">
<label style="margin-top: 15px; " for="description">题目描述</label>
<textarea rows="15" id="description" name="description"><%= problem.description %></textarea>

Loading…
Cancel
Save