You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
687 lines
20 KiB
687 lines
20 KiB
6 years ago
|
const statisticsStatements = {
|
||
8 years ago
|
fastest:
|
||
7 years ago
|
'\
|
||
8 years ago
|
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 `total_time` ASC \
|
||
8 years ago
|
LIMIT 1 \
|
||
8 years ago
|
) AS `id`, \
|
||
|
( \
|
||
|
SELECT \
|
||
|
`total_time` \
|
||
|
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 `total_time` ASC \
|
||
8 years ago
|
LIMIT 1 \
|
||
8 years ago
|
) AS `total_time` \
|
||
|
FROM `judge_state` `outer_table` \
|
||
|
WHERE \
|
||
|
`problem_id` = __PROBLEM_ID__ AND `status` = "Accepted" AND `type` = 0 \
|
||
|
ORDER BY `total_time` ASC \
|
||
|
',
|
||
|
slowest:
|
||
7 years ago
|
' \
|
||
8 years ago
|
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 `total_time` DESC \
|
||
8 years ago
|
LIMIT 1 \
|
||
8 years ago
|
) AS `id`, \
|
||
|
( \
|
||
|
SELECT \
|
||
|
`total_time` \
|
||
|
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 `total_time` DESC \
|
||
8 years ago
|
LIMIT 1 \
|
||
8 years ago
|
) AS `total_time` \
|
||
|
FROM `judge_state` `outer_table` \
|
||
|
WHERE \
|
||
|
`problem_id` = __PROBLEM_ID__ AND `status` = "Accepted" AND `type` = 0 \
|
||
|
ORDER BY `total_time` DESC \
|
||
|
',
|
||
|
shortest:
|
||
7 years ago
|
' \
|
||
8 years ago
|
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 \
|
||
6 years ago
|
ORDER BY `code_length` ASC \
|
||
8 years ago
|
LIMIT 1 \
|
||
8 years ago
|
) AS `id`, \
|
||
|
( \
|
||
|
SELECT \
|
||
6 years ago
|
`code_length` \
|
||
8 years ago
|
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 \
|
||
6 years ago
|
ORDER BY `code_length` ASC \
|
||
8 years ago
|
LIMIT 1 \
|
||
8 years ago
|
) AS `code_length` \
|
||
|
FROM `judge_state` `outer_table` \
|
||
|
WHERE \
|
||
|
`problem_id` = __PROBLEM_ID__ AND `status` = "Accepted" AND `type` = 0 \
|
||
|
ORDER BY `code_length` ASC \
|
||
|
',
|
||
|
longest:
|
||
7 years ago
|
' \
|
||
8 years ago
|
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 \
|
||
6 years ago
|
ORDER BY `code_length` DESC \
|
||
8 years ago
|
LIMIT 1 \
|
||
8 years ago
|
) AS `id`, \
|
||
|
( \
|
||
|
SELECT \
|
||
6 years ago
|
`code_length` \
|
||
8 years ago
|
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 \
|
||
6 years ago
|
ORDER BY `code_length` DESC \
|
||
8 years ago
|
LIMIT 1 \
|
||
8 years ago
|
) AS `code_length` \
|
||
|
FROM `judge_state` `outer_table` \
|
||
|
WHERE \
|
||
|
`problem_id` = __PROBLEM_ID__ AND `status` = "Accepted" AND `type` = 0 \
|
||
|
ORDER BY `code_length` DESC \
|
||
|
',
|
||
|
earliest:
|
||
7 years ago
|
' \
|
||
8 years ago
|
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 `submit_time` ASC \
|
||
8 years ago
|
LIMIT 1 \
|
||
8 years ago
|
) AS `id`, \
|
||
|
( \
|
||
|
SELECT \
|
||
|
`submit_time` \
|
||
|
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 `submit_time` ASC \
|
||
8 years ago
|
LIMIT 1 \
|
||
8 years ago
|
) AS `submit_time` \
|
||
|
FROM `judge_state` `outer_table` \
|
||
|
WHERE \
|
||
|
`problem_id` = __PROBLEM_ID__ AND `status` = "Accepted" AND `type` = 0 \
|
||
|
ORDER BY `submit_time` ASC \
|
||
8 years ago
|
',
|
||
|
min:
|
||
7 years ago
|
' \
|
||
8 years ago
|
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:
|
||
7 years ago
|
' \
|
||
8 years ago
|
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 \
|
||
8 years ago
|
'
|
||
|
};
|
||
|
|
||
6 years ago
|
import * as TypeORM from "typeorm";
|
||
|
import Model from "./common";
|
||
8 years ago
|
|
||
6 years ago
|
declare var syzoj, ErrorMessage: any;
|
||
8 years ago
|
|
||
6 years ago
|
import User from "./user";
|
||
|
import File from "./file";
|
||
|
import JudgeState from "./judge_state";
|
||
|
import Contest from "./contest";
|
||
|
import ProblemTag from "./problem_tag";
|
||
|
import ProblemTagMap from "./problem_tag_map";
|
||
8 years ago
|
|
||
6 years ago
|
import * as fs from "fs-extra";
|
||
|
import * as path from "path";
|
||
|
import * as util from "util";
|
||
8 years ago
|
|
||
6 years ago
|
enum ProblemType {
|
||
|
Traditional = "traditional",
|
||
|
SubmitAnswer = "submit-answer",
|
||
|
Interaction = "interaction"
|
||
|
}
|
||
8 years ago
|
|
||
6 years ago
|
@TypeORM.Entity()
|
||
|
export default class Problem extends Model {
|
||
|
@TypeORM.PrimaryGeneratedColumn()
|
||
|
id: number;
|
||
8 years ago
|
|
||
6 years ago
|
@TypeORM.Column({ nullable: true, type: "varchar", length: 80 })
|
||
|
title: string;
|
||
8 years ago
|
|
||
6 years ago
|
@TypeORM.Index()
|
||
|
@TypeORM.Column({ nullable: true, type: "integer" })
|
||
|
user_id: number;
|
||
8 years ago
|
|
||
6 years ago
|
@TypeORM.Column({ nullable: true, type: "integer" })
|
||
|
publicizer_id: number;
|
||
8 years ago
|
|
||
6 years ago
|
@TypeORM.Column({ nullable: true, type: "boolean" })
|
||
|
is_anonymous: boolean;
|
||
7 years ago
|
|
||
6 years ago
|
@TypeORM.Column({ nullable: true, type: "text" })
|
||
|
description: string;
|
||
|
|
||
|
@TypeORM.Column({ nullable: true, type: "text" })
|
||
|
input_format: string;
|
||
|
|
||
|
@TypeORM.Column({ nullable: true, type: "text" })
|
||
|
output_format: string;
|
||
|
|
||
|
@TypeORM.Column({ nullable: true, type: "text" })
|
||
|
example: string;
|
||
|
|
||
|
@TypeORM.Column({ nullable: true, type: "text" })
|
||
|
limit_and_hint: string;
|
||
|
|
||
|
@TypeORM.Column({ nullable: true, type: "integer" })
|
||
|
time_limit: number;
|
||
|
|
||
|
@TypeORM.Column({ nullable: true, type: "integer" })
|
||
|
memory_limit: number;
|
||
|
|
||
|
@TypeORM.Column({ nullable: true, type: "integer" })
|
||
|
additional_file_id: number;
|
||
|
|
||
|
@TypeORM.Column({ nullable: true, type: "integer" })
|
||
|
ac_num: number;
|
||
|
|
||
|
@TypeORM.Column({ nullable: true, type: "integer" })
|
||
|
submit_num: number;
|
||
|
|
||
|
@TypeORM.Index()
|
||
|
@TypeORM.Column({ nullable: true, type: "boolean" })
|
||
|
is_public: boolean;
|
||
|
|
||
|
@TypeORM.Column({ nullable: true, type: "boolean" })
|
||
|
file_io: boolean;
|
||
|
|
||
|
@TypeORM.Column({ nullable: true, type: "text" })
|
||
|
file_io_input_name: string;
|
||
|
|
||
|
@TypeORM.Column({ nullable: true, type: "text" })
|
||
|
file_io_output_name: string;
|
||
|
|
||
|
@TypeORM.Index()
|
||
|
@TypeORM.Column({ nullable: true, type: "datetime" })
|
||
|
publicize_time: Date;
|
||
|
|
||
|
@TypeORM.Column({ nullable: true,
|
||
|
type: "enum",
|
||
|
enum: ProblemType,
|
||
|
default: ProblemType.Traditional
|
||
|
})
|
||
|
type: ProblemType;
|
||
|
|
||
|
user?: User;
|
||
|
publicizer?: User;
|
||
|
additional_file?: File;
|
||
8 years ago
|
|
||
|
async loadRelationships() {
|
||
6 years ago
|
this.user = await User.findById(this.user_id);
|
||
|
this.publicizer = await User.findById(this.publicizer_id);
|
||
|
this.additional_file = await File.findById(this.additional_file_id);
|
||
8 years ago
|
}
|
||
|
|
||
|
async isAllowedEditBy(user) {
|
||
8 years ago
|
if (!user) return false;
|
||
|
if (await user.hasPrivilege('manage_problem')) return true;
|
||
|
return this.user_id === user.id;
|
||
8 years ago
|
}
|
||
|
|
||
|
async isAllowedUseBy(user) {
|
||
8 years ago
|
if (this.is_public) return true;
|
||
8 years ago
|
if (!user) return false;
|
||
|
if (await user.hasPrivilege('manage_problem')) return true;
|
||
8 years ago
|
return this.user_id === user.id;
|
||
8 years ago
|
}
|
||
|
|
||
|
async isAllowedManageBy(user) {
|
||
|
if (!user) return false;
|
||
|
if (await user.hasPrivilege('manage_problem')) return true;
|
||
|
return user.is_admin;
|
||
8 years ago
|
}
|
||
|
|
||
8 years ago
|
getTestdataPath() {
|
||
|
return syzoj.utils.resolvePath(syzoj.config.upload_dir, 'testdata', this.id.toString());
|
||
|
}
|
||
|
|
||
7 years ago
|
getTestdataArchivePath() {
|
||
|
return syzoj.utils.resolvePath(syzoj.config.upload_dir, 'testdata-archive', this.id.toString() + '.zip');
|
||
|
}
|
||
|
|
||
8 years ago
|
async updateTestdata(path, noLimit) {
|
||
8 years ago
|
await syzoj.utils.lock(['Problem::Testdata', this.id], async () => {
|
||
|
let p7zip = new (require('node-7z'));
|
||
8 years ago
|
|
||
7 years ago
|
let unzipSize = 0, unzipCount;
|
||
8 years ago
|
await p7zip.list(path).progress(files => {
|
||
7 years ago
|
unzipCount = files.length;
|
||
8 years ago
|
for (let file of files) unzipSize += file.size;
|
||
|
});
|
||
7 years ago
|
if (!noLimit && unzipCount > syzoj.config.limit.testdata_filecount) throw new ErrorMessage('数据包中的文件太多。');
|
||
8 years ago
|
if (!noLimit && unzipSize > syzoj.config.limit.testdata) throw new ErrorMessage('数据包太大。');
|
||
8 years ago
|
|
||
8 years ago
|
let dir = this.getTestdataPath();
|
||
7 years ago
|
await fs.remove(dir);
|
||
|
await fs.ensureDir(dir);
|
||
8 years ago
|
|
||
6 years ago
|
let execFileAsync = util.promisify(require('child_process').execFile);
|
||
7 years ago
|
await execFileAsync(__dirname + '/../bin/unzip', ['-j', '-o', '-d', dir, path]);
|
||
7 years ago
|
await fs.move(path, this.getTestdataArchivePath(), { overwrite: true });
|
||
8 years ago
|
});
|
||
8 years ago
|
}
|
||
|
|
||
8 years ago
|
async uploadTestdataSingleFile(filename, filepath, size, noLimit) {
|
||
8 years ago
|
await syzoj.utils.lock(['Promise::Testdata', this.id], async () => {
|
||
|
let dir = this.getTestdataPath();
|
||
7 years ago
|
await fs.ensureDir(dir);
|
||
8 years ago
|
|
||
7 years ago
|
let oldSize = 0, list = await this.listTestdata(), replace = false, oldCount = 0;
|
||
8 years ago
|
if (list) {
|
||
7 years ago
|
oldCount = list.files.length;
|
||
|
for (let file of list.files) {
|
||
|
if (file.filename !== filename) oldSize += file.size;
|
||
|
else replace = true;
|
||
|
}
|
||
8 years ago
|
}
|
||
8 years ago
|
|
||
8 years ago
|
if (!noLimit && oldSize + size > syzoj.config.limit.testdata) throw new ErrorMessage('数据包太大。');
|
||
6 years ago
|
if (!noLimit && oldCount + (!replace as any as number) > syzoj.config.limit.testdata_filecount) throw new ErrorMessage('数据包中的文件太多。');
|
||
8 years ago
|
|
||
7 years ago
|
await fs.move(filepath, path.join(dir, filename), { overwrite: true });
|
||
6 years ago
|
|
||
6 years ago
|
let execFileAsync = util.promisify(require('child_process').execFile);
|
||
6 years ago
|
try { await execFileAsync('dos2unix', [path.join(dir, filename)]); } catch (e) {}
|
||
|
|
||
7 years ago
|
await fs.remove(this.getTestdataArchivePath());
|
||
8 years ago
|
});
|
||
8 years ago
|
}
|
||
|
|
||
|
async deleteTestdataSingleFile(filename) {
|
||
8 years ago
|
await syzoj.utils.lock(['Promise::Testdata', this.id], async () => {
|
||
7 years ago
|
await fs.remove(path.join(this.getTestdataPath(), filename));
|
||
|
await fs.remove(this.getTestdataArchivePath());
|
||
8 years ago
|
});
|
||
8 years ago
|
}
|
||
|
|
||
|
async makeTestdataZip() {
|
||
8 years ago
|
await syzoj.utils.lock(['Promise::Testdata', this.id], async () => {
|
||
|
let dir = this.getTestdataPath();
|
||
|
if (!await syzoj.utils.isDir(dir)) throw new ErrorMessage('无测试数据。');
|
||
8 years ago
|
|
||
8 years ago
|
let p7zip = new (require('node-7z'));
|
||
8 years ago
|
|
||
7 years ago
|
let list = await this.listTestdata(), pathlist = list.files.map(file => path.join(dir, file.filename));
|
||
7 years ago
|
if (!pathlist.length) throw new ErrorMessage('无测试数据。');
|
||
7 years ago
|
await fs.ensureDir(path.resolve(this.getTestdataArchivePath(), '..'));
|
||
|
await p7zip.add(this.getTestdataArchivePath(), pathlist);
|
||
8 years ago
|
});
|
||
8 years ago
|
}
|
||
|
|
||
|
async hasSpecialJudge() {
|
||
|
try {
|
||
|
let dir = this.getTestdataPath();
|
||
7 years ago
|
let list = await fs.readdir(dir);
|
||
8 years ago
|
return list.includes('spj.js') || list.find(x => x.startsWith('spj_')) !== undefined;
|
||
|
} catch (e) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async listTestdata() {
|
||
|
try {
|
||
|
let dir = this.getTestdataPath();
|
||
6 years ago
|
let filenameList = await fs.readdir(dir);
|
||
|
let list = await Promise.all(filenameList.map(async x => {
|
||
7 years ago
|
let stat = await fs.stat(path.join(dir, x));
|
||
8 years ago
|
if (!stat.isFile()) return undefined;
|
||
|
return {
|
||
|
filename: x,
|
||
|
size: stat.size
|
||
8 years ago
|
};
|
||
6 years ago
|
}));
|
||
8 years ago
|
|
||
|
list = list.filter(x => x);
|
||
|
|
||
|
let res = {
|
||
|
files: list,
|
||
|
zip: null
|
||
|
};
|
||
|
|
||
|
try {
|
||
7 years ago
|
let stat = await fs.stat(this.getTestdataArchivePath());
|
||
8 years ago
|
if (stat.isFile()) {
|
||
|
res.zip = {
|
||
|
size: stat.size
|
||
8 years ago
|
};
|
||
|
}
|
||
|
} catch (e) {
|
||
|
if (list) {
|
||
|
res.zip = {
|
||
|
size: null
|
||
|
};
|
||
8 years ago
|
}
|
||
8 years ago
|
}
|
||
8 years ago
|
|
||
|
return res;
|
||
|
} catch (e) {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
8 years ago
|
async updateFile(path, type, noLimit) {
|
||
|
let file = await File.upload(path, type, noLimit);
|
||
8 years ago
|
|
||
8 years ago
|
if (type === 'additional_file') {
|
||
8 years ago
|
this.additional_file_id = file.id;
|
||
8 years ago
|
}
|
||
|
|
||
8 years ago
|
await this.save();
|
||
|
}
|
||
|
|
||
|
async validate() {
|
||
|
if (this.time_limit <= 0) return 'Invalid time limit';
|
||
|
if (this.time_limit > syzoj.config.limit.time_limit) return 'Time limit too large';
|
||
|
if (this.memory_limit <= 0) return 'Invalid memory limit';
|
||
|
if (this.memory_limit > syzoj.config.limit.memory_limit) return 'Memory limit too large';
|
||
7 years ago
|
if (!['traditional', 'submit-answer', 'interaction'].includes(this.type)) return 'Invalid problem type';
|
||
8 years ago
|
|
||
7 years ago
|
if (this.type === 'traditional') {
|
||
|
let filenameRE = /^[\w \-\+\.]*$/;
|
||
|
if (this.file_io_input_name && !filenameRE.test(this.file_io_input_name)) return 'Invalid input file name';
|
||
|
if (this.file_io_output_name && !filenameRE.test(this.file_io_output_name)) return 'Invalid output file name';
|
||
|
|
||
|
if (this.file_io) {
|
||
|
if (!this.file_io_input_name) return 'No input file name';
|
||
|
if (!this.file_io_output_name) return 'No output file name';
|
||
|
}
|
||
8 years ago
|
}
|
||
|
|
||
|
return null;
|
||
8 years ago
|
}
|
||
|
|
||
8 years ago
|
async getJudgeState(user, acFirst) {
|
||
8 years ago
|
if (!user) return null;
|
||
|
|
||
6 years ago
|
let where: any = {
|
||
8 years ago
|
user_id: user.id,
|
||
|
problem_id: this.id
|
||
8 years ago
|
};
|
||
8 years ago
|
|
||
8 years ago
|
if (acFirst) {
|
||
|
where.status = 'Accepted';
|
||
8 years ago
|
|
||
8 years ago
|
let state = await JudgeState.findOne({
|
||
|
where: where,
|
||
6 years ago
|
order: {
|
||
|
submit_time: 'DESC'
|
||
|
}
|
||
8 years ago
|
});
|
||
|
|
||
|
if (state) return state;
|
||
8 years ago
|
}
|
||
|
|
||
8 years ago
|
if (where.status) delete where.status;
|
||
|
|
||
|
return await JudgeState.findOne({
|
||
|
where: where,
|
||
6 years ago
|
order: {
|
||
|
submit_time: 'DESC'
|
||
|
}
|
||
8 years ago
|
});
|
||
8 years ago
|
}
|
||
|
|
||
7 years ago
|
async resetSubmissionCount() {
|
||
7 years ago
|
await syzoj.utils.lock(['Problem::resetSubmissionCount', this.id], async () => {
|
||
6 years ago
|
this.submit_num = await JudgeState.count({ problem_id: this.id, type: TypeORM.Not(1) });
|
||
|
this.ac_num = await JudgeState.count({ score: 100, problem_id: this.id, type: TypeORM.Not(1) });
|
||
7 years ago
|
await this.save();
|
||
|
});
|
||
7 years ago
|
}
|
||
|
|
||
8 years ago
|
// type: fastest / slowest / shortest / longest / earliest
|
||
|
async countStatistics(type) {
|
||
|
let statement = statisticsStatements[type];
|
||
|
if (!statement) return null;
|
||
|
|
||
6 years ago
|
const entityManager = TypeORM.getManager();
|
||
|
|
||
8 years ago
|
statement = statement.replace('__PROBLEM_ID__', this.id);
|
||
6 years ago
|
return JudgeState.countQuery(statement);
|
||
8 years ago
|
}
|
||
|
|
||
|
// type: fastest / slowest / shortest / longest / earliest
|
||
|
async getStatistics(type, paginate) {
|
||
6 years ago
|
const entityManager = TypeORM.getManager();
|
||
|
|
||
8 years ago
|
let statistics = {
|
||
|
type: type,
|
||
|
judge_state: null,
|
||
|
scoreDistribution: null,
|
||
|
prefixSum: null,
|
||
|
suffixSum: null
|
||
|
};
|
||
|
|
||
|
let statement = statisticsStatements[type];
|
||
|
if (!statement) return null;
|
||
|
|
||
|
statement = statement.replace('__PROBLEM_ID__', this.id);
|
||
8 years ago
|
|
||
|
let a;
|
||
|
if (!paginate.pageCnt) a = [];
|
||
6 years ago
|
else a = (await entityManager.query(statement + `LIMIT ${paginate.perPage} OFFSET ${(paginate.currPage - 1) * paginate.perPage}`))[0];
|
||
8 years ago
|
|
||
6 years ago
|
statistics.judge_state = await a.mapAsync(async x => JudgeState.findById(x.id));
|
||
8 years ago
|
|
||
6 years ago
|
a = (await entityManager.query('SELECT `score`, COUNT(*) AS `count` FROM `judge_state` WHERE `problem_id` = __PROBLEM_ID__ AND `type` = 0 AND `pending` = 0 GROUP BY `score`'.replace('__PROBLEM_ID__', this.id.toString())))[0];
|
||
8 years ago
|
|
||
|
let scoreCount = [];
|
||
|
for (let score of a) {
|
||
|
score.score = Math.min(Math.round(score.score), 100);
|
||
|
scoreCount[score.score] = score.count;
|
||
|
}
|
||
|
if (scoreCount[0] === undefined) scoreCount[0] = 0;
|
||
|
if (scoreCount[100] === undefined) scoreCount[100] = 0;
|
||
|
|
||
|
statistics.scoreDistribution = [];
|
||
|
for (let i = 0; i < scoreCount.length; i++) {
|
||
|
if (scoreCount[i] !== undefined) statistics.scoreDistribution.push({ score: i, count: scoreCount[i] });
|
||
|
}
|
||
|
|
||
|
statistics.prefixSum = JSON.parse(JSON.stringify(statistics.scoreDistribution));
|
||
|
statistics.suffixSum = JSON.parse(JSON.stringify(statistics.scoreDistribution));
|
||
|
|
||
|
for (let i = 1; i < statistics.prefixSum.length; i++) {
|
||
|
statistics.prefixSum[i].count += statistics.prefixSum[i - 1].count;
|
||
|
}
|
||
|
|
||
|
for (let i = statistics.prefixSum.length - 1; i >= 1; i--) {
|
||
|
statistics.suffixSum[i - 1].count += statistics.suffixSum[i].count;
|
||
|
}
|
||
|
|
||
|
return statistics;
|
||
|
}
|
||
|
|
||
8 years ago
|
async getTags() {
|
||
6 years ago
|
let maps = await ProblemTagMap.find({
|
||
|
where: {
|
||
|
problem_id: this.id
|
||
|
}
|
||
8 years ago
|
});
|
||
|
|
||
6 years ago
|
let res = await (maps as any).mapAsync(async map => {
|
||
|
return ProblemTag.findById(map.tag_id);
|
||
8 years ago
|
});
|
||
|
|
||
|
res.sort((a, b) => {
|
||
8 years ago
|
return a.color > b.color ? 1 : -1;
|
||
8 years ago
|
});
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
8 years ago
|
async setTags(newTagIDs) {
|
||
|
let oldTagIDs = (await this.getTags()).map(x => x.id);
|
||
|
|
||
|
let delTagIDs = oldTagIDs.filter(x => !newTagIDs.includes(x));
|
||
|
let addTagIDs = newTagIDs.filter(x => !oldTagIDs.includes(x));
|
||
|
|
||
|
for (let tagID of delTagIDs) {
|
||
7 years ago
|
let map = await ProblemTagMap.findOne({
|
||
|
where: {
|
||
|
problem_id: this.id,
|
||
|
tag_id: tagID
|
||
|
}
|
||
|
});
|
||
8 years ago
|
|
||
|
await map.destroy();
|
||
|
}
|
||
|
|
||
|
for (let tagID of addTagIDs) {
|
||
|
let map = await ProblemTagMap.create({
|
||
|
problem_id: this.id,
|
||
|
tag_id: tagID
|
||
|
});
|
||
|
|
||
|
await map.save();
|
||
|
}
|
||
|
}
|
||
|
|
||
8 years ago
|
async changeID(id) {
|
||
6 years ago
|
const entityManager = TypeORM.getManager();
|
||
|
|
||
8 years ago
|
id = parseInt(id);
|
||
6 years ago
|
await entityManager.query('UPDATE `problem` SET `id` = ' + id + ' WHERE `id` = ' + this.id);
|
||
|
await entityManager.query('UPDATE `judge_state` SET `problem_id` = ' + id + ' WHERE `problem_id` = ' + this.id);
|
||
|
await entityManager.query('UPDATE `problem_tag_map` SET `problem_id` = ' + id + ' WHERE `problem_id` = ' + this.id);
|
||
|
await entityManager.query('UPDATE `article` SET `problem_id` = ' + id + ' WHERE `problem_id` = ' + this.id);
|
||
8 years ago
|
|
||
6 years ago
|
|
||
|
let contests = await Contest.find();
|
||
8 years ago
|
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();
|
||
|
}
|
||
|
}
|
||
|
|
||
7 years ago
|
let oldTestdataDir = this.getTestdataPath(), oldTestdataZip = this.getTestdataArchivePath();
|
||
8 years ago
|
|
||
8 years ago
|
this.id = id;
|
||
8 years ago
|
|
||
|
// Move testdata
|
||
7 years ago
|
let newTestdataDir = this.getTestdataPath(), newTestdataZip = this.getTestdataArchivePath();
|
||
8 years ago
|
if (await syzoj.utils.isDir(oldTestdataDir)) {
|
||
7 years ago
|
await fs.move(oldTestdataDir, newTestdataDir);
|
||
8 years ago
|
}
|
||
|
|
||
|
if (await syzoj.utils.isFile(oldTestdataZip)) {
|
||
7 years ago
|
await fs.move(oldTestdataZip, newTestdataZip);
|
||
8 years ago
|
}
|
||
|
|
||
8 years ago
|
await this.save();
|
||
8 years ago
|
}
|
||
|
|
||
7 years ago
|
async delete() {
|
||
6 years ago
|
const entityManager = TypeORM.getManager();
|
||
|
|
||
7 years ago
|
let oldTestdataDir = this.getTestdataPath(), oldTestdataZip = this.getTestdataPath();
|
||
|
await fs.remove(oldTestdataDir);
|
||
|
await fs.remove(oldTestdataZip);
|
||
7 years ago
|
|
||
6 years ago
|
let submissions = await JudgeState.find({
|
||
|
where: {
|
||
|
problem_id: this.id
|
||
|
}
|
||
|
}), submitCnt = {}, acUsers = new Set();
|
||
7 years ago
|
for (let sm of submissions) {
|
||
|
if (sm.status === 'Accepted') acUsers.add(sm.user_id);
|
||
|
if (!submitCnt[sm.user_id]) {
|
||
|
submitCnt[sm.user_id] = 1;
|
||
|
} else {
|
||
|
submitCnt[sm.user_id]++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (let u in submitCnt) {
|
||
6 years ago
|
let user = await User.findById(parseInt(u));
|
||
7 years ago
|
user.submit_num -= submitCnt[u];
|
||
|
if (acUsers.has(parseInt(u))) user.ac_num--;
|
||
|
await user.save();
|
||
|
}
|
||
|
|
||
6 years ago
|
await entityManager.query('DELETE FROM `problem` WHERE `id` = ' + this.id);
|
||
|
await entityManager.query('DELETE FROM `judge_state` WHERE `problem_id` = ' + this.id);
|
||
|
await entityManager.query('DELETE FROM `problem_tag_map` WHERE `problem_id` = ' + this.id);
|
||
|
await entityManager.query('DELETE FROM `article` WHERE `problem_id` = ' + this.id);
|
||
7 years ago
|
}
|
||
8 years ago
|
}
|