Menci
6 years ago
45 changed files with 1876 additions and 1866 deletions
@ -1,56 +0,0 @@
|
||||
let Sequelize = require('sequelize'); |
||||
let db = syzoj.db; |
||||
|
||||
let User = syzoj.model('user'); |
||||
let Article = syzoj.model('article'); |
||||
|
||||
let model = db.define('comment', { |
||||
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, |
||||
|
||||
content: { type: Sequelize.TEXT }, |
||||
|
||||
article_id: { type: Sequelize.INTEGER }, |
||||
|
||||
user_id: { type: Sequelize.INTEGER }, |
||||
|
||||
public_time: { type: Sequelize.INTEGER } |
||||
}, { |
||||
timestamps: false, |
||||
tableName: 'comment', |
||||
indexes: [ |
||||
{ |
||||
fields: ['article_id'] |
||||
}, |
||||
{ |
||||
fields: ['user_id'] |
||||
} |
||||
] |
||||
}); |
||||
|
||||
let Model = require('./common'); |
||||
class ArticleComment extends Model { |
||||
static async create(val) { |
||||
return ArticleComment.fromRecord(ArticleComment.model.build(Object.assign({ |
||||
content: '', |
||||
article_id: 0, |
||||
user_id: 0, |
||||
public_time: 0, |
||||
}, val))); |
||||
} |
||||
|
||||
async loadRelationships() { |
||||
this.user = await User.fromID(this.user_id); |
||||
this.article = await Article.fromID(this.article_id); |
||||
} |
||||
|
||||
async isAllowedEditBy(user) { |
||||
await this.loadRelationships(); |
||||
return user && (user.is_admin || this.user_id === user.id || user.id === this.article.user_id); |
||||
} |
||||
|
||||
getModel() { return model; } |
||||
}; |
||||
|
||||
ArticleComment.model = model; |
||||
|
||||
module.exports = ArticleComment; |
@ -0,0 +1,38 @@
|
||||
import * as TypeORM from "typeorm"; |
||||
import Model from "./common"; |
||||
|
||||
import User from "./user"; |
||||
import Article from "./article"; |
||||
|
||||
@TypeORM.Entity() |
||||
export default class ArticleComment extends Model { |
||||
@TypeORM.PrimaryGeneratedColumn() |
||||
id: number; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "text" }) |
||||
content: string; |
||||
|
||||
@TypeORM.Index() |
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
article_id: number; |
||||
|
||||
@TypeORM.Index() |
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
user_id: number; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
public_time: number; |
||||
|
||||
user?: User; |
||||
article?: Article; |
||||
|
||||
async loadRelationships() { |
||||
this.user = await User.findById(this.user_id); |
||||
this.article = await Article.findById(this.article_id); |
||||
} |
||||
|
||||
async isAllowedEditBy(user) { |
||||
await this.loadRelationships(); |
||||
return user && (user.is_admin || this.user_id === user.id || user.id === this.article.user_id); |
||||
} |
||||
}; |
@ -1,91 +0,0 @@
|
||||
let Sequelize = require('sequelize'); |
||||
let db = syzoj.db; |
||||
|
||||
let User = syzoj.model('user'); |
||||
const Problem = syzoj.model('problem'); |
||||
|
||||
let model = db.define('article', { |
||||
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, |
||||
|
||||
title: { type: Sequelize.STRING(80) }, |
||||
content: { type: Sequelize.TEXT('medium') }, |
||||
|
||||
user_id: { type: Sequelize.INTEGER }, |
||||
problem_id: { type: Sequelize.INTEGER }, |
||||
|
||||
public_time: { type: Sequelize.INTEGER }, |
||||
update_time: { type: Sequelize.INTEGER }, |
||||
sort_time: { type: Sequelize.INTEGER }, |
||||
|
||||
comments_num: { type: Sequelize.INTEGER }, |
||||
allow_comment: { type: Sequelize.BOOLEAN }, |
||||
|
||||
is_notice: { type: Sequelize.BOOLEAN } |
||||
}, { |
||||
timestamps: false, |
||||
tableName: 'article', |
||||
indexes: [ |
||||
{ |
||||
fields: ['user_id'] |
||||
}, |
||||
{ |
||||
fields: ['problem_id'] |
||||
}, |
||||
{ |
||||
fields: ['sort_time'] |
||||
} |
||||
] |
||||
}); |
||||
|
||||
let Model = require('./common'); |
||||
class Article extends Model { |
||||
static async create(val) { |
||||
return Article.fromRecord(Article.model.build(Object.assign({ |
||||
title: '', |
||||
content: '', |
||||
|
||||
user_id: 0, |
||||
problem_id: 0, |
||||
|
||||
public_time: 0, |
||||
update_time: 0, |
||||
sort_time: 0, |
||||
|
||||
comments_num: 0, |
||||
allow_comment: true, |
||||
|
||||
is_notice: false |
||||
}, val))); |
||||
} |
||||
|
||||
async loadRelationships() { |
||||
this.user = await User.fromID(this.user_id); |
||||
} |
||||
|
||||
async isAllowedEditBy(user) { |
||||
return user && (user.is_admin || this.user_id === user.id); |
||||
} |
||||
|
||||
async isAllowedCommentBy(user) { |
||||
return user && (this.allow_comment || user.is_admin || this.user_id === user.id); |
||||
} |
||||
|
||||
async resetReplyCountAndTime() { |
||||
let ArticleComment = syzoj.model('article-comment'); |
||||
await syzoj.utils.lock(['Article::resetReplyCountAndTime', this.id], async () => { |
||||
this.comments_num = await ArticleComment.count({ article_id: this.id }); |
||||
if (this.comments_num === 0) { |
||||
this.sort_time = this.public_time; |
||||
} else { |
||||
this.sort_time = await ArticleComment.model.max('public_time', { where: { article_id: this.id } }); |
||||
} |
||||
await this.save(); |
||||
}); |
||||
} |
||||
|
||||
getModel() { return model; } |
||||
}; |
||||
|
||||
Article.model = model; |
||||
|
||||
module.exports = Article; |
@ -0,0 +1,78 @@
|
||||
import * as TypeORM from "typeorm"; |
||||
import Model from "./common"; |
||||
|
||||
import User from "./user"; |
||||
import Problem from "./problem"; |
||||
import ArticleComment from "./article-comment"; |
||||
|
||||
declare var syzoj: any; |
||||
|
||||
@TypeORM.Entity() |
||||
export default class Article extends Model { |
||||
@TypeORM.PrimaryGeneratedColumn() |
||||
id: number; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "varchar", length: 80 }) |
||||
title: string; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "mediumtext" }) |
||||
content: string; |
||||
|
||||
@TypeORM.Index() |
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
user_id: number; |
||||
|
||||
@TypeORM.Index() |
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
problem_id: number; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
public_time: number; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
update_time: number; |
||||
|
||||
@TypeORM.Index() |
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
sort_time: number; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
comments_num: number; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "boolean" }) |
||||
allow_comment: boolean; |
||||
|
||||
@TypeORM.Index() |
||||
@TypeORM.Column({ nullable: true, type: "boolean" }) |
||||
is_notice: boolean; |
||||
|
||||
user?: User; |
||||
problem?: Problem; |
||||
|
||||
async loadRelationships() { |
||||
this.user = await User.findById(this.user_id); |
||||
} |
||||
|
||||
async isAllowedEditBy(user) { |
||||
return user && (user.is_admin || this.user_id === user.id); |
||||
} |
||||
|
||||
async isAllowedCommentBy(user) { |
||||
return user && (this.allow_comment || user.is_admin || this.user_id === user.id); |
||||
} |
||||
|
||||
async resetReplyCountAndTime() { |
||||
await syzoj.utils.lock(['Article::resetReplyCountAndTime', this.id], async () => { |
||||
this.comments_num = await ArticleComment.count({ article_id: this.id }); |
||||
if (this.comments_num === 0) { |
||||
this.sort_time = this.public_time; |
||||
} else { |
||||
this.sort_time = (await ArticleComment.findOne({ |
||||
where: { article_id: this.id }, |
||||
order: { public_time: "DESC" } |
||||
})).public_time; |
||||
} |
||||
await this.save(); |
||||
}); |
||||
} |
||||
}; |
@ -1,136 +0,0 @@
|
||||
let Sequelize = require('sequelize'); |
||||
|
||||
class Model { |
||||
constructor(record) { |
||||
this.record = record; |
||||
this.loadFields(); |
||||
} |
||||
|
||||
loadFields() { |
||||
let model = this.getModel(); |
||||
let obj = JSON.parse(JSON.stringify(this.record.get({ plain: true }))); |
||||
for (let key in obj) { |
||||
if (!model.tableAttributes[key]) continue; |
||||
|
||||
if (model.tableAttributes[key].type instanceof Sequelize.JSON && typeof obj[key] === 'string') { |
||||
try { |
||||
this[key] = JSON.parse(obj[key]); |
||||
} catch (e) { |
||||
this[key] = {}; |
||||
} |
||||
} else this[key] = obj[key]; |
||||
} |
||||
} |
||||
|
||||
toPlain() { |
||||
let model = this.getModel(); |
||||
let obj = JSON.parse(JSON.stringify(this.record.get({ plain: true }))); |
||||
for (let key in obj) { |
||||
obj[key] = this[key]; |
||||
} |
||||
return obj; |
||||
} |
||||
|
||||
async save() { |
||||
let obj = this.toPlain(); |
||||
for (let key in obj) this.record.set(key, obj[key]); |
||||
|
||||
let isNew = this.record.isNewRecord; |
||||
|
||||
await syzoj.utils.withTimeoutRetry(() => this.record.save()); |
||||
if (!isNew) return; |
||||
|
||||
await this.reload(); |
||||
} |
||||
|
||||
async reload() { |
||||
await this.record.reload(); |
||||
this.loadFields(); |
||||
} |
||||
|
||||
async destroy() { |
||||
return this.record.destroy(); |
||||
} |
||||
|
||||
static async fromRecord(record) { |
||||
record = await record; |
||||
if (!record) return null; |
||||
return new this(await record); |
||||
} |
||||
|
||||
static async fromID(id) { |
||||
return this.fromRecord(this.model.findByPk(id)); |
||||
} |
||||
|
||||
static async findOne(options) { |
||||
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') { |
||||
let sql = where; |
||||
return syzoj.db.countQuery(sql); |
||||
} |
||||
|
||||
// count(where)
|
||||
return this.model.count({ where: where }); |
||||
} |
||||
|
||||
static async query(paginate, where, order, largeData) { |
||||
let records = []; |
||||
|
||||
if (typeof paginate === 'string') { |
||||
// query(sql)
|
||||
let sql = paginate; |
||||
records = await syzoj.db.query(sql, { model: this.model }); |
||||
} else { |
||||
if (paginate && !Array.isArray(paginate) && !paginate.pageCnt) return []; |
||||
|
||||
let options = { |
||||
where: where, |
||||
order: order |
||||
}; |
||||
if (Array.isArray(paginate)) { |
||||
options.offset = paginate[0] - 1; |
||||
options.limit = paginate[1] - paginate[0] + 1; |
||||
} else if (paginate) { |
||||
options.offset = (paginate.currPage - 1) * paginate.perPage; |
||||
options.limit = parseInt(paginate.perPage); |
||||
} |
||||
|
||||
if (!largeData) records = await this.model.findAll(options); |
||||
else { |
||||
let sql = await getSqlFromFindAll(this.model, options); |
||||
let i = sql.indexOf('FROM'); |
||||
sql = 'SELECT id ' + sql.substr(i, sql.length - i); |
||||
sql = `SELECT a.* FROM (${sql}) AS b JOIN ${this.model.name} AS a ON a.id = b.id ORDER BY id DESC`; |
||||
records = await syzoj.db.query(sql, { model: this.model }); |
||||
} |
||||
} |
||||
|
||||
return records.mapAsync(record => (this.fromRecord(record))); |
||||
} |
||||
} |
||||
|
||||
function getSqlFromFindAll(Model, options) { |
||||
let id = require('uuid')(); |
||||
|
||||
return new Promise((resolve, reject) => { |
||||
Model.addHook('beforeFindAfterOptions', id, options => { |
||||
Model.removeHook('beforeFindAfterOptions', id); |
||||
|
||||
resolve(Model.sequelize.dialect.QueryGenerator.selectQuery(Model.getTableName(), options, Model).slice(0, -1)); |
||||
|
||||
return new Promise(() => {}); |
||||
}); |
||||
|
||||
return Model.findAll(options).catch(reject); |
||||
}); |
||||
} |
||||
|
||||
module.exports = Model; |
@ -0,0 +1,63 @@
|
||||
import * as TypeORM from "typeorm"; |
||||
|
||||
interface Paginater { |
||||
pageCnt: number; |
||||
perPage: number; |
||||
currPage: number; |
||||
} |
||||
|
||||
export default class Model extends TypeORM.BaseEntity { |
||||
static async findById<T extends TypeORM.BaseEntity>(this: TypeORM.ObjectType<T>, id?: number): Promise<T | undefined> { |
||||
return await (this as any).findOne(parseInt(id as any) || 0); |
||||
} |
||||
|
||||
async destroy() { |
||||
await TypeORM.getManager().remove(this); |
||||
} |
||||
|
||||
static async countQuery<T extends TypeORM.BaseEntity>(this: TypeORM.ObjectType<T>, query: TypeORM.SelectQueryBuilder<T> | string) { |
||||
let parameters: any[] = null; |
||||
if (typeof query !== 'string') { |
||||
[query, parameters] = query.getQueryAndParameters(); |
||||
} |
||||
|
||||
return parseInt(( |
||||
await TypeORM.getManager().query(`SELECT COUNT(*) FROM (${query}) AS \`__tmp_table\``, parameters) |
||||
)[0]['COUNT(*)']); |
||||
} |
||||
|
||||
static async queryAll(queryBuilder) { |
||||
return await queryBuilder.getMany(); |
||||
} |
||||
|
||||
static async queryPage(paginater: Paginater, where, order) { |
||||
if (!paginater.pageCnt) return []; |
||||
|
||||
let queryBuilder = where instanceof TypeORM.SelectQueryBuilder |
||||
? where |
||||
: this.createQueryBuilder().where(where); |
||||
|
||||
if (order) queryBuilder.orderBy(order); |
||||
|
||||
queryBuilder = queryBuilder.skip((paginater.currPage - 1) * paginater.perPage) |
||||
.take(paginater.perPage); |
||||
|
||||
return queryBuilder.getMany(); |
||||
} |
||||
|
||||
static async queryRange(range: any[], where, order) { |
||||
range[0] = parseInt(range[0]); |
||||
range[1] = parseInt(range[1]); |
||||
|
||||
let queryBuilder = where instanceof TypeORM.SelectQueryBuilder |
||||
? where |
||||
: this.createQueryBuilder().where(where); |
||||
|
||||
if (order) queryBuilder.orderBy(order); |
||||
|
||||
queryBuilder = queryBuilder.skip(range[0] - 1) |
||||
.take(range[1] - range[0] + 1); |
||||
|
||||
return queryBuilder.getMany(); |
||||
} |
||||
} |
@ -1,81 +0,0 @@
|
||||
let Sequelize = require('sequelize'); |
||||
let db = syzoj.db; |
||||
|
||||
let User = syzoj.model('user'); |
||||
let Problem = syzoj.model('problem'); |
||||
|
||||
let model = db.define('custom_test', { |
||||
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, |
||||
|
||||
input_filepath: { type: Sequelize.TEXT }, |
||||
code: { type: Sequelize.TEXT('medium') }, |
||||
language: { type: Sequelize.STRING(20) }, |
||||
|
||||
status: { type: Sequelize.STRING(50) }, |
||||
|
||||
time: { type: Sequelize.INTEGER }, |
||||
pending: { type: Sequelize.BOOLEAN }, |
||||
memory: { type: Sequelize.INTEGER }, |
||||
|
||||
result: { type: Sequelize.JSON }, |
||||
|
||||
user_id: { type: Sequelize.INTEGER }, |
||||
|
||||
problem_id: { type: Sequelize.INTEGER }, |
||||
|
||||
submit_time: { type: Sequelize.INTEGER } |
||||
}, { |
||||
timestamps: false, |
||||
tableName: 'custom_test', |
||||
indexes: [ |
||||
{ |
||||
fields: ['status'], |
||||
}, |
||||
{ |
||||
fields: ['user_id'], |
||||
}, |
||||
{ |
||||
fields: ['problem_id'], |
||||
} |
||||
] |
||||
}); |
||||
|
||||
let Model = require('./common'); |
||||
class CustomTest extends Model { |
||||
static async create(val) { |
||||
return CustomTest.fromRecord(CustomTest.model.build(Object.assign({ |
||||
input_filepath: '', |
||||
code: '', |
||||
language: '', |
||||
user_id: 0, |
||||
problem_id: 0, |
||||
submit_time: parseInt((new Date()).getTime() / 1000), |
||||
|
||||
pending: true, |
||||
|
||||
time: 0, |
||||
memory: 0, |
||||
result: {}, |
||||
status: 'Waiting', |
||||
}, val))); |
||||
} |
||||
|
||||
async loadRelationships() { |
||||
this.user = await User.fromID(this.user_id); |
||||
this.problem = await Problem.fromID(this.problem_id); |
||||
} |
||||
|
||||
async updateResult(result) { |
||||
this.pending = result.pending; |
||||
this.status = result.status; |
||||
this.time = result.time_used; |
||||
this.memory = result.memory_used; |
||||
this.result = result; |
||||
} |
||||
|
||||
getModel() { return model; } |
||||
} |
||||
|
||||
CustomTest.model = model; |
||||
|
||||
module.exports = CustomTest; |
@ -1,31 +0,0 @@
|
||||
let Sequelize = require('sequelize'); |
||||
let db = syzoj.db; |
||||
|
||||
let model = db.define('formatted_code', { |
||||
key: { type: Sequelize.STRING(50), primaryKey: true }, |
||||
code: { type: Sequelize.TEXT('medium') } |
||||
}, { |
||||
timestamps: false, |
||||
tableName: 'formatted_code', |
||||
indexes: [ |
||||
{ |
||||
fields: ['key'] |
||||
} |
||||
] |
||||
}); |
||||
|
||||
let Model = require('./common'); |
||||
class FormattedCode extends Model { |
||||
static async create(val) { |
||||
return FormattedCode.fromRecord(FormattedCode.model.build(Object.assign({ |
||||
key: "", |
||||
code: "" |
||||
}, val))); |
||||
} |
||||
|
||||
getModel() { return model; } |
||||
} |
||||
|
||||
FormattedCode.model = model; |
||||
|
||||
module.exports = FormattedCode; |
@ -0,0 +1,11 @@
|
||||
import * as TypeORM from "typeorm"; |
||||
import Model from "./common"; |
||||
|
||||
@TypeORM.Entity() |
||||
export default class FormattedCode extends Model { |
||||
@TypeORM.Column({ type: "varchar", length: 50, primary: true }) |
||||
key: string; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "mediumtext" }) |
||||
code: string; |
||||
} |
@ -1,195 +0,0 @@
|
||||
let Sequelize = require('sequelize'); |
||||
const randomstring = require('randomstring'); |
||||
let db = syzoj.db; |
||||
|
||||
let User = syzoj.model('user'); |
||||
let Problem = syzoj.model('problem'); |
||||
let Contest = syzoj.model('contest'); |
||||
|
||||
let Judger = syzoj.lib('judger'); |
||||
|
||||
let model = db.define('judge_state', { |
||||
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') }, |
||||
language: { type: Sequelize.STRING(20) }, |
||||
|
||||
status: { type: Sequelize.STRING(50) }, |
||||
task_id: { type: Sequelize.STRING(50) }, |
||||
score: { type: Sequelize.INTEGER }, |
||||
total_time: { type: Sequelize.INTEGER }, |
||||
code_length: { type: Sequelize.INTEGER }, |
||||
pending: { type: Sequelize.BOOLEAN }, |
||||
max_memory: { type: Sequelize.INTEGER }, |
||||
|
||||
// For NOI contest
|
||||
compilation: { type: Sequelize.JSON }, |
||||
|
||||
result: { type: Sequelize.JSON }, |
||||
|
||||
user_id: { type: Sequelize.INTEGER }, |
||||
|
||||
problem_id: { type: Sequelize.INTEGER }, |
||||
|
||||
submit_time: { type: Sequelize.INTEGER }, |
||||
/* |
||||
* "type" indicate it's contest's submission(type = 1) or normal submission(type = 0) |
||||
* if it's contest's submission (type = 1), the type_info is contest_id |
||||
* use this way represent because it's easy to expand // Menci:这锅我不背,是 Chenyao 留下来的坑。
|
||||
*/ |
||||
type: { type: Sequelize.INTEGER }, |
||||
type_info: { type: Sequelize.INTEGER }, |
||||
is_public: { type: Sequelize.BOOLEAN } |
||||
}, { |
||||
timestamps: false, |
||||
tableName: 'judge_state', |
||||
indexes: [ |
||||
{ |
||||
fields: ['status'], |
||||
}, |
||||
{ |
||||
fields: ['score'], |
||||
}, |
||||
{ |
||||
fields: ['user_id'], |
||||
}, |
||||
{ |
||||
fields: ['problem_id'], |
||||
}, |
||||
{ |
||||
fields: ['task_id'], |
||||
}, |
||||
{ |
||||
fields: ['id', 'is_public', 'type_info', 'type'] |
||||
} |
||||
] |
||||
}); |
||||
|
||||
let Model = require('./common'); |
||||
class JudgeState extends Model { |
||||
static async create(val) { |
||||
return JudgeState.fromRecord(JudgeState.model.build(Object.assign({ |
||||
code: '', |
||||
code_length: 0, |
||||
language: null, |
||||
user_id: 0, |
||||
problem_id: 0, |
||||
submit_time: parseInt((new Date()).getTime() / 1000), |
||||
|
||||
type: 0, |
||||
type_info: 0, |
||||
|
||||
pending: false, |
||||
|
||||
score: null, |
||||
total_time: null, |
||||
max_memory: null, |
||||
status: 'Unknown', |
||||
compilation: {}, |
||||
result: {}, |
||||
task_id: randomstring.generate(10), |
||||
is_public: false |
||||
}, val))); |
||||
} |
||||
|
||||
async loadRelationships() { |
||||
if (!this.user) { |
||||
this.user = await User.fromID(this.user_id); |
||||
} |
||||
if (!this.problem) { |
||||
if (this.problem_id) this.problem = await Problem.fromID(this.problem_id); |
||||
} |
||||
} |
||||
|
||||
async isAllowedVisitBy(user) { |
||||
await this.loadRelationships(); |
||||
|
||||
if (user && user.id === this.problem.user_id) return true; |
||||
else if (this.type === 0) return this.problem.is_public || (user && (await user.hasPrivilege('manage_problem'))); |
||||
else if (this.type === 1) { |
||||
let contest = await Contest.fromID(this.type_info); |
||||
if (contest.isRunning()) { |
||||
return user && await contest.isSupervisior(user); |
||||
} else { |
||||
return true; |
||||
} |
||||
} |
||||
} |
||||
|
||||
async updateRelatedInfo(newSubmission) { |
||||
if (this.type === 0) { |
||||
await this.loadRelationships(); |
||||
|
||||
// No need to await them.
|
||||
this.user.refreshSubmitInfo(); |
||||
this.problem.resetSubmissionCount(); |
||||
} else if (this.type === 1) { |
||||
let contest = await Contest.fromID(this.type_info); |
||||
await contest.newSubmission(this); |
||||
} |
||||
} |
||||
|
||||
async rejudge() { |
||||
await syzoj.utils.lock(['JudgeState::rejudge', this.id], async () => { |
||||
await this.loadRelationships(); |
||||
|
||||
let oldStatus = this.status; |
||||
|
||||
this.status = 'Unknown'; |
||||
this.pending = false; |
||||
this.score = null; |
||||
if (this.language) { |
||||
// language is empty if it's a submit-answer problem
|
||||
this.total_time = null; |
||||
this.max_memory = null; |
||||
} |
||||
this.result = {}; |
||||
this.task_id = randomstring.generate(10); |
||||
await this.save(); |
||||
|
||||
/* |
||||
let WaitingJudge = syzoj.model('waiting_judge'); |
||||
let waiting_judge = await WaitingJudge.create({ |
||||
judge_id: this.id, |
||||
priority: 2, |
||||
type: 'submission' |
||||
}); |
||||
|
||||
await waiting_judge.save(); |
||||
*/ |
||||
|
||||
await this.problem.resetSubmissionCount(); |
||||
if (oldStatus === 'Accepted') { |
||||
await this.user.refreshSubmitInfo(); |
||||
await this.user.save(); |
||||
} |
||||
|
||||
if (this.type === 1) { |
||||
let contest = await Contest.fromID(this.type_info); |
||||
await contest.newSubmission(this); |
||||
} |
||||
|
||||
try { |
||||
await Judger.judge(this, this.problem, 1); |
||||
this.pending = true; |
||||
this.status = 'Waiting'; |
||||
await this.save(); |
||||
} catch (err) { |
||||
console.log("Error while connecting to judge frontend: " + err.toString()); |
||||
throw new ErrorMessage("无法开始评测。"); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
async getProblemType() { |
||||
await this.loadRelationships(); |
||||
return this.problem.type; |
||||
} |
||||
|
||||
getModel() { return model; } |
||||
} |
||||
|
||||
JudgeState.model = model; |
||||
|
||||
module.exports = JudgeState; |
@ -0,0 +1,165 @@
|
||||
import * as TypeORM from "typeorm"; |
||||
import Model from "./common"; |
||||
|
||||
declare var syzoj, ErrorMessage: any; |
||||
|
||||
import User from "./user"; |
||||
import Problem from "./problem"; |
||||
import Contest from "./contest"; |
||||
|
||||
const Judger = syzoj.lib('judger'); |
||||
|
||||
@TypeORM.Entity() |
||||
@TypeORM.Index(['id', 'is_public', 'type_info', 'type']) |
||||
export default class JudgeState extends Model { |
||||
@TypeORM.PrimaryGeneratedColumn() |
||||
id: number; |
||||
|
||||
// The data zip's md5 if it's a submit-answer problem
|
||||
@TypeORM.Column({ nullable: true, type: "mediumtext" }) |
||||
code: string; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "varchar", length: 20 }) |
||||
language: string; |
||||
|
||||
@TypeORM.Index() |
||||
@TypeORM.Column({ nullable: true, type: "varchar", length: 50 }) |
||||
status: string; |
||||
|
||||
@TypeORM.Index() |
||||
@TypeORM.Column({ nullable: true, type: "varchar", length: 50 }) |
||||
task_id: string; |
||||
|
||||
@TypeORM.Index() |
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
score: number; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
total_time: number; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
code_length: number; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "boolean" }) |
||||
pending: boolean; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
max_memory: number; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "json" }) |
||||
compilation: any; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "json" }) |
||||
result: any; |
||||
|
||||
@TypeORM.Index() |
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
user_id: number; |
||||
|
||||
@TypeORM.Index() |
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
problem_id: number; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
submit_time: number; |
||||
|
||||
/* |
||||
* "type" indicate it's contest's submission(type = 1) or normal submission(type = 0) |
||||
* if it's contest's submission (type = 1), the type_info is contest_id |
||||
* use this way represent because it's easy to expand // Menci:这锅我不背,是 Chenyao 留下来的坑。
|
||||
*/ |
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
type: number; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
type_info: number; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "boolean" }) |
||||
is_public: boolean; |
||||
|
||||
user?: User; |
||||
problem?: Problem; |
||||
|
||||
async loadRelationships() { |
||||
if (!this.user) { |
||||
this.user = await User.findById(this.user_id); |
||||
} |
||||
if (!this.problem) { |
||||
if (this.problem_id) this.problem = await Problem.findById(this.problem_id); |
||||
} |
||||
} |
||||
|
||||
async isAllowedVisitBy(user) { |
||||
await this.loadRelationships(); |
||||
|
||||
if (user && user.id === this.problem.user_id) return true; |
||||
else if (this.type === 0) return this.problem.is_public || (user && (await user.hasPrivilege('manage_problem'))); |
||||
else if (this.type === 1) { |
||||
let contest = await Contest.findById(this.type_info); |
||||
if (contest.isRunning()) { |
||||
return user && await contest.isSupervisior(user); |
||||
} else { |
||||
return true; |
||||
} |
||||
} |
||||
} |
||||
|
||||
async updateRelatedInfo(newSubmission) { |
||||
if (this.type === 0) { |
||||
await this.loadRelationships(); |
||||
|
||||
// No need to await them.
|
||||
this.user.refreshSubmitInfo(); |
||||
this.problem.resetSubmissionCount(); |
||||
} else if (this.type === 1) { |
||||
let contest = await Contest.findById(this.type_info); |
||||
await contest.newSubmission(this); |
||||
} |
||||
} |
||||
|
||||
async rejudge() { |
||||
await syzoj.utils.lock(['JudgeState::rejudge', this.id], async () => { |
||||
await this.loadRelationships(); |
||||
|
||||
let oldStatus = this.status; |
||||
|
||||
this.status = 'Unknown'; |
||||
this.pending = false; |
||||
this.score = null; |
||||
if (this.language) { |
||||
// language is empty if it's a submit-answer problem
|
||||
this.total_time = null; |
||||
this.max_memory = null; |
||||
} |
||||
this.result = {}; |
||||
this.task_id = require('randomstring').generate(10); |
||||
await this.save(); |
||||
|
||||
await this.problem.resetSubmissionCount(); |
||||
if (oldStatus === 'Accepted') { |
||||
await this.user.refreshSubmitInfo(); |
||||
await this.user.save(); |
||||
} |
||||
|
||||
if (this.type === 1) { |
||||
let contest = await Contest.findById(this.type_info); |
||||
await contest.newSubmission(this); |
||||
} |
||||
|
||||
try { |
||||
await Judger.judge(this, this.problem, 1); |
||||
this.pending = true; |
||||
this.status = 'Waiting'; |
||||
await this.save(); |
||||
} catch (err) { |
||||
console.log("Error while connecting to judge frontend: " + err.toString()); |
||||
throw new ErrorMessage("无法开始评测。"); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
async getProblemType() { |
||||
await this.loadRelationships(); |
||||
return this.problem.type; |
||||
} |
||||
} |
@ -1,33 +0,0 @@
|
||||
let Sequelize = require('sequelize'); |
||||
let db = syzoj.db; |
||||
|
||||
let model = db.define('problem_tag', { |
||||
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, |
||||
name: { type: Sequelize.STRING }, |
||||
color: { type: Sequelize.STRING }, |
||||
}, { |
||||
timestamps: false, |
||||
tableName: 'problem_tag', |
||||
indexes: [ |
||||
{ |
||||
unique: true, |
||||
fields: ['name'], |
||||
} |
||||
] |
||||
}); |
||||
|
||||
let Model = require('./common'); |
||||
class ProblemTag extends Model { |
||||
static async create(val) { |
||||
return ProblemTag.fromRecord(ProblemTag.model.build(Object.assign({ |
||||
name: '', |
||||
color: '' |
||||
}, val))); |
||||
} |
||||
|
||||
getModel() { return model; } |
||||
} |
||||
|
||||
ProblemTag.model = model; |
||||
|
||||
module.exports = ProblemTag; |
@ -0,0 +1,15 @@
|
||||
import * as TypeORM from "typeorm"; |
||||
import Model from "./common"; |
||||
|
||||
@TypeORM.Entity() |
||||
export default class ProblemTag extends Model { |
||||
@TypeORM.PrimaryGeneratedColumn() |
||||
id: number; |
||||
|
||||
@TypeORM.Index({ unique: true }) |
||||
@TypeORM.Column({ nullable: true, type: "varchar", length: 255 }) |
||||
name: string; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "varchar", length: 255 }) |
||||
color: string; |
||||
} |
@ -1,37 +0,0 @@
|
||||
let Sequelize = require('sequelize'); |
||||
let db = syzoj.db; |
||||
|
||||
let model = db.define('problem_tag_map', { |
||||
problem_id: { type: Sequelize.INTEGER, primaryKey: true }, |
||||
tag_id: { |
||||
type: Sequelize.INTEGER, |
||||
primaryKey: true |
||||
} |
||||
}, { |
||||
timestamps: false, |
||||
tableName: 'problem_tag_map', |
||||
indexes: [ |
||||
{ |
||||
fields: ['problem_id'] |
||||
}, |
||||
{ |
||||
fields: ['tag_id'] |
||||
} |
||||
] |
||||
}); |
||||
|
||||
let Model = require('./common'); |
||||
class ProblemTagMap extends Model { |
||||
static async create(val) { |
||||
return ProblemTagMap.fromRecord(ProblemTagMap.model.build(Object.assign({ |
||||
problem_id: 0, |
||||
tag_id: 0 |
||||
}, val))); |
||||
} |
||||
|
||||
getModel() { return model; } |
||||
} |
||||
|
||||
ProblemTagMap.model = model; |
||||
|
||||
module.exports = ProblemTagMap; |
@ -0,0 +1,13 @@
|
||||
import * as TypeORM from "typeorm"; |
||||
import Model from "./common"; |
||||
|
||||
@TypeORM.Entity() |
||||
export default class ProblemTagMap extends Model { |
||||
@TypeORM.Index() |
||||
@TypeORM.Column({ type: "integer", primary: true }) |
||||
problem_id: number; |
||||
|
||||
@TypeORM.Index() |
||||
@TypeORM.Column({ type: "integer", primary: true }) |
||||
tag_id: number; |
||||
} |
@ -1,53 +0,0 @@
|
||||
let Sequelize = require('sequelize'); |
||||
let db = syzoj.db; |
||||
const User = syzoj.model('user'); |
||||
const Contest = syzoj.model('contest'); |
||||
|
||||
let model = db.define('rating_calculation', { |
||||
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, |
||||
contest_id: { type: Sequelize.INTEGER } |
||||
}, { |
||||
timestamps: false, |
||||
tableName: 'rating_calculation', |
||||
indexes: [ |
||||
{ |
||||
fields: ['contest_id'] |
||||
}, |
||||
] |
||||
}); |
||||
|
||||
let Model = require('./common'); |
||||
class RatingCalculation extends Model { |
||||
static async create(contest_id) { |
||||
return RatingCalculation.fromRecord(RatingCalculation.model.create({ contest_id: contest_id })); |
||||
} |
||||
|
||||
async loadRelationships() { |
||||
this.contest = await Contest.fromID(this.contest_id); |
||||
} |
||||
|
||||
getModel() { return model; } |
||||
|
||||
async delete() { |
||||
const RatingHistory = syzoj.model('rating_history'); |
||||
const histories = await RatingHistory.query(null, { |
||||
rating_calculation_id: this.id |
||||
}); |
||||
for (const history of histories) { |
||||
await history.loadRelationships(); |
||||
const user = history.user; |
||||
await history.destroy(); |
||||
const ratingItem = (await RatingHistory.findOne({ |
||||
where: { user_id: user.id }, |
||||
order: [['rating_calculation_id','DESC']] |
||||
})); |
||||
user.rating = ratingItem ? ratingItem.rating_after : syzoj.config.default.user.rating; |
||||
await user.save(); |
||||
} |
||||
await this.destroy(); |
||||
} |
||||
} |
||||
|
||||
RatingCalculation.model = model; |
||||
|
||||
module.exports = RatingCalculation; |
@ -0,0 +1,47 @@
|
||||
import * as TypeORM from "typeorm"; |
||||
import Model from "./common"; |
||||
|
||||
declare var syzoj: any; |
||||
|
||||
import Contest from "./contest"; |
||||
import RatingHistory from "./rating_history"; |
||||
|
||||
@TypeORM.Entity() |
||||
export default class RatingCalculation extends Model { |
||||
@TypeORM.PrimaryGeneratedColumn() |
||||
id: number; |
||||
|
||||
@TypeORM.Index({}) |
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
contest_id: number; |
||||
|
||||
contest?: Contest; |
||||
|
||||
async loadRelationships() { |
||||
this.contest = await Contest.findById(this.contest_id); |
||||
} |
||||
|
||||
async delete() { |
||||
const histories = await RatingHistory.find({ |
||||
where: { |
||||
rating_calculation_id: this.id |
||||
} |
||||
}); |
||||
for (const history of histories) { |
||||
await history.loadRelationships(); |
||||
const user = history.user; |
||||
await history.destroy(); |
||||
const ratingItem = (await RatingHistory.findOne({ |
||||
where: { |
||||
user_id: user.id |
||||
}, |
||||
order: { |
||||
rating_calculation_id: 'DESC' |
||||
} |
||||
})); |
||||
user.rating = ratingItem ? ratingItem.rating_after : syzoj.config.default.user.rating; |
||||
await user.save(); |
||||
} |
||||
await this.destroy(); |
||||
} |
||||
} |
@ -1,43 +0,0 @@
|
||||
let Sequelize = require('sequelize'); |
||||
const User = syzoj.model('user'); |
||||
let db = syzoj.db; |
||||
|
||||
let model = db.define('rating_history', { |
||||
rating_calculation_id: { type: Sequelize.INTEGER, primaryKey: true }, |
||||
user_id: { type: Sequelize.INTEGER, primaryKey: true }, |
||||
rating_after: { type: Sequelize.INTEGER }, |
||||
rank: { type: Sequelize.INTEGER }, |
||||
}, { |
||||
timestamps: false, |
||||
tableName: 'rating_history', |
||||
indexes: [ |
||||
{ |
||||
fields: ['rating_calculation_id'] |
||||
}, |
||||
{ |
||||
fields: ['user_id'] |
||||
}, |
||||
] |
||||
}); |
||||
|
||||
let Model = require('./common'); |
||||
class RatingHistory extends Model { |
||||
static async create(rating_calculation_id, user_id, rating, rank) { |
||||
return RatingHistory.fromRecord(RatingHistory.model.build({ |
||||
rating_calculation_id: rating_calculation_id, |
||||
user_id: user_id, |
||||
rating_after: rating, |
||||
rank: rank |
||||
})); |
||||
} |
||||
|
||||
async loadRelationships() { |
||||
this.user = await User.fromID(this.user_id); |
||||
} |
||||
|
||||
getModel() { return model; } |
||||
} |
||||
|
||||
RatingHistory.model = model; |
||||
|
||||
module.exports = RatingHistory; |
@ -0,0 +1,27 @@
|
||||
import * as TypeORM from "typeorm"; |
||||
import Model from "./common"; |
||||
|
||||
declare var syzoj: any; |
||||
|
||||
import User from "./user"; |
||||
|
||||
@TypeORM.Entity() |
||||
export default class RatingHistory extends Model { |
||||
@TypeORM.Column({ type: "integer", primary: true }) |
||||
rating_calculation_id: number; |
||||
|
||||
@TypeORM.Column({ type: "integer", primary: true }) |
||||
user_id: number; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
rating_after: number; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
rank: number; |
||||
|
||||
user: User; |
||||
|
||||
async loadRelationships() { |
||||
this.user = await User.findById(this.user_id); |
||||
} |
||||
} |
@ -1,235 +0,0 @@
|
||||
let Sequelize = require('sequelize'); |
||||
let db = syzoj.db; |
||||
|
||||
let model = db.define('user', { |
||||
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, |
||||
username: { type: Sequelize.STRING(80), unique: true }, |
||||
email: { type: Sequelize.STRING(120) }, |
||||
password: { type: Sequelize.STRING(120) }, |
||||
|
||||
nickname: { type: Sequelize.STRING(80) }, |
||||
nameplate: { type: Sequelize.TEXT }, |
||||
information: { type: Sequelize.TEXT }, |
||||
|
||||
ac_num: { type: Sequelize.INTEGER }, |
||||
submit_num: { type: Sequelize.INTEGER }, |
||||
|
||||
is_admin: { type: Sequelize.BOOLEAN }, |
||||
is_show: { type: Sequelize.BOOLEAN }, |
||||
public_email: { type: Sequelize.BOOLEAN }, |
||||
prefer_formatted_code: { type: Sequelize.BOOLEAN }, |
||||
|
||||
sex: { type: Sequelize.INTEGER }, |
||||
rating: { type: Sequelize.INTEGER }, |
||||
|
||||
register_time: { type: Sequelize.INTEGER } |
||||
}, { |
||||
timestamps: false, |
||||
tableName: 'user', |
||||
indexes: [ |
||||
{ |
||||
fields: ['username'], |
||||
unique: true |
||||
}, |
||||
{ |
||||
fields: ['nickname'], |
||||
}, |
||||
{ |
||||
fields: ['ac_num'], |
||||
} |
||||
] |
||||
}); |
||||
|
||||
let Model = require('./common'); |
||||
class User extends Model { |
||||
static async create(val) { |
||||
return User.fromRecord(User.model.build(Object.assign({ |
||||
username: '', |
||||
password: '', |
||||
email: '', |
||||
|
||||
nickname: '', |
||||
is_admin: false, |
||||
ac_num: 0, |
||||
submit_num: 0, |
||||
sex: 0, |
||||
is_show: syzoj.config.default.user.show, |
||||
rating: syzoj.config.default.user.rating, |
||||
register_time: parseInt((new Date()).getTime() / 1000), |
||||
prefer_formatted_code: true |
||||
}, val))); |
||||
} |
||||
|
||||
static async fromEmail(email) { |
||||
return User.fromRecord(User.model.findOne({ |
||||
where: { |
||||
email: email |
||||
} |
||||
})); |
||||
} |
||||
|
||||
static async fromName(name) { |
||||
return User.fromRecord(User.model.findOne({ |
||||
where: { |
||||
username: name |
||||
} |
||||
})); |
||||
} |
||||
|
||||
async isAllowedEditBy(user) { |
||||
if (!user) return false; |
||||
if (await user.hasPrivilege('manage_user')) return true; |
||||
return user && (user.is_admin || this.id === user.id); |
||||
} |
||||
|
||||
async refreshSubmitInfo() { |
||||
await syzoj.utils.lock(['User::refreshSubmitInfo', this.id], async () => { |
||||
let JudgeState = syzoj.model('judge_state'); |
||||
this.ac_num = await JudgeState.model.count({ |
||||
col: 'problem_id', |
||||
distinct: true, |
||||
where: { |
||||
user_id: this.id, |
||||
status: 'Accepted', |
||||
type: { |
||||
$ne: 1 // Not a contest submission
|
||||
} |
||||
} |
||||
}); |
||||
|
||||
this.submit_num = await JudgeState.count({ |
||||
user_id: this.id, |
||||
type: { |
||||
$ne: 1 // Not a contest submission
|
||||
} |
||||
}); |
||||
|
||||
await this.save(); |
||||
}); |
||||
} |
||||
|
||||
async getACProblems() { |
||||
let JudgeState = syzoj.model('judge_state'); |
||||
|
||||
let queryResult = await JudgeState.model.aggregate('problem_id', 'DISTINCT', { |
||||
plain: false, |
||||
where: { |
||||
user_id: this.id, |
||||
status: 'Accepted', |
||||
type: { |
||||
$ne: 1 // Not a contest submissio
|
||||
} |
||||
}, |
||||
order: [["problem_id", "ASC"]] |
||||
}); |
||||
|
||||
return queryResult.map(record => record['DISTINCT']) |
||||
} |
||||
|
||||
async getArticles() { |
||||
let Article = syzoj.model('article'); |
||||
|
||||
let all = await Article.model.findAll({ |
||||
attributes: ['id', 'title', 'public_time'], |
||||
where: { |
||||
user_id: this.id |
||||
} |
||||
}); |
||||
|
||||
return all.map(x => ({ |
||||
id: x.get('id'), |
||||
title: x.get('title'), |
||||
public_time: x.get('public_time') |
||||
})); |
||||
} |
||||
|
||||
async getStatistics() { |
||||
let JudgeState = syzoj.model('judge_state'); |
||||
|
||||
let statuses = { |
||||
"Accepted": ["Accepted"], |
||||
"Wrong Answer": ["Wrong Answer", "File Error", "Output Limit Exceeded"], |
||||
"Runtime Error": ["Runtime Error"], |
||||
"Time Limit Exceeded": ["Time Limit Exceeded"], |
||||
"Memory Limit Exceeded": ["Memory Limit Exceeded"], |
||||
"Compile Error": ["Compile Error"] |
||||
}; |
||||
|
||||
let res = {}; |
||||
for (let status in statuses) { |
||||
res[status] = 0; |
||||
for (let s of statuses[status]) { |
||||
res[status] += await JudgeState.count({ |
||||
user_id: this.id, |
||||
type: 0, |
||||
status: s |
||||
}); |
||||
} |
||||
} |
||||
|
||||
return res; |
||||
} |
||||
|
||||
async renderInformation() { |
||||
this.information = await syzoj.utils.markdown(this.information); |
||||
} |
||||
|
||||
async getPrivileges() { |
||||
let UserPrivilege = syzoj.model('user_privilege'); |
||||
let privileges = await UserPrivilege.query(null, { |
||||
user_id: this.id |
||||
}); |
||||
|
||||
return privileges.map(x => x.privilege); |
||||
} |
||||
|
||||
async setPrivileges(newPrivileges) { |
||||
let UserPrivilege = syzoj.model('user_privilege'); |
||||
|
||||
let oldPrivileges = await this.getPrivileges(); |
||||
|
||||
let delPrivileges = oldPrivileges.filter(x => !newPrivileges.includes(x)); |
||||
let addPrivileges = newPrivileges.filter(x => !oldPrivileges.includes(x)); |
||||
|
||||
for (let privilege of delPrivileges) { |
||||
let obj = await UserPrivilege.findOne({ where: { |
||||
user_id: this.id, |
||||
privilege: privilege |
||||
} }); |
||||
|
||||
await obj.destroy(); |
||||
} |
||||
|
||||
for (let privilege of addPrivileges) { |
||||
let obj = await UserPrivilege.create({ |
||||
user_id: this.id, |
||||
privilege: privilege |
||||
}); |
||||
|
||||
await obj.save(); |
||||
} |
||||
} |
||||
|
||||
async hasPrivilege(privilege) { |
||||
if (this.is_admin) return true; |
||||
|
||||
let UserPrivilege = syzoj.model('user_privilege'); |
||||
let x = await UserPrivilege.findOne({ where: { user_id: this.id, privilege: privilege } }); |
||||
return !(!x); |
||||
} |
||||
|
||||
async getLastSubmitLanguage() { |
||||
let JudgeState = syzoj.model('judge_state'); |
||||
|
||||
let a = await JudgeState.query([1, 1], { user_id: this.id }, [['submit_time', 'desc']]); |
||||
if (a[0]) return a[0].language; |
||||
|
||||
return null; |
||||
} |
||||
|
||||
getModel() { return model; } |
||||
} |
||||
|
||||
User.model = model; |
||||
|
||||
module.exports = User; |
@ -0,0 +1,205 @@
|
||||
import * as TypeORM from "typeorm"; |
||||
import Model from "./common"; |
||||
|
||||
declare var syzoj: any; |
||||
|
||||
import JudgeState from "./judge_state"; |
||||
import UserPrivilege from "./user_privilege"; |
||||
import Article from "./article"; |
||||
|
||||
@TypeORM.Entity() |
||||
export default class User extends Model { |
||||
@TypeORM.PrimaryGeneratedColumn() |
||||
id: number; |
||||
|
||||
@TypeORM.Index({ unique: true }) |
||||
@TypeORM.Column({ nullable: true, type: "varchar", length: 80 }) |
||||
username: string; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "varchar", length: 120 }) |
||||
email: string; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "varchar", length: 120 }) |
||||
password: string; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "varchar", length: 80 }) |
||||
nickname: string; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "text" }) |
||||
nameplate: string; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "text" }) |
||||
information: string; |
||||
|
||||
@TypeORM.Index() |
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
ac_num: number; |
||||
|
||||
@TypeORM.Index() |
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
submit_num: number; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "boolean" }) |
||||
is_admin: boolean; |
||||
|
||||
@TypeORM.Index() |
||||
@TypeORM.Column({ nullable: true, type: "boolean" }) |
||||
is_show: boolean; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "boolean" }) |
||||
public_email: boolean; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "boolean" }) |
||||
prefer_formatted_code: boolean; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
sex: number; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
rating: number; |
||||
|
||||
@TypeORM.Column({ nullable: true, type: "integer" }) |
||||
register_time: number; |
||||
|
||||
static async fromEmail(email): Promise<User> { |
||||
return User.findOne({ |
||||
where: { |
||||
email: email |
||||
} |
||||
}); |
||||
} |
||||
|
||||
static async fromName(name): Promise<User> { |
||||
return User.findOne({ |
||||
where: { |
||||
username: name |
||||
} |
||||
}); |
||||
} |
||||
|
||||
async isAllowedEditBy(user) { |
||||
if (!user) return false; |
||||
if (await user.hasPrivilege('manage_user')) return true; |
||||
return user && (user.is_admin || this.id === user.id); |
||||
} |
||||
|
||||
getQueryBuilderForACProblems() { |
||||
return JudgeState.createQueryBuilder() |
||||
.select(`DISTINCT(problem_id)`) |
||||
.where('user_id = :user_id', { user_id: this.id }) |
||||
.andWhere('status = :status', { status: 'Accepted' }) |
||||
.andWhere('type != 1') |
||||
.orderBy({ problem_id: 'ASC' }) |
||||
} |
||||
|
||||
async refreshSubmitInfo() { |
||||
await syzoj.utils.lock(['User::refreshSubmitInfo', this.id], async () => { |
||||
this.ac_num = await JudgeState.countQuery(this.getQueryBuilderForACProblems()); |
||||
this.submit_num = await JudgeState.count({ |
||||
user_id: this.id, |
||||
type: TypeORM.Not(1) // Not a contest submission
|
||||
}); |
||||
|
||||
await this.save(); |
||||
}); |
||||
} |
||||
|
||||
async getACProblems() { |
||||
let queryResult = await this.getQueryBuilderForACProblems().getRawMany(); |
||||
|
||||
return queryResult.map(record => record['problem_id']) |
||||
} |
||||
|
||||
async getArticles() { |
||||
return await Article.find({ |
||||
where: { |
||||
user_id: this.id |
||||
} |
||||
}); |
||||
} |
||||
|
||||
async getStatistics() { |
||||
let statuses = { |
||||
"Accepted": ["Accepted"], |
||||
"Wrong Answer": ["Wrong Answer", "File Error", "Output Limit Exceeded"], |
||||
"Runtime Error": ["Runtime Error"], |
||||
"Time Limit Exceeded": ["Time Limit Exceeded"], |
||||
"Memory Limit Exceeded": ["Memory Limit Exceeded"], |
||||
"Compile Error": ["Compile Error"] |
||||
}; |
||||
|
||||
let res = {}; |
||||
for (let status in statuses) { |
||||
res[status] = 0; |
||||
for (let s of statuses[status]) { |
||||
res[status] += await JudgeState.count({ |
||||
user_id: this.id, |
||||
type: 0, |
||||
status: s |
||||
}); |
||||
} |
||||
} |
||||
|
||||
return res; |
||||
} |
||||
|
||||
async renderInformation() { |
||||
this.information = await syzoj.utils.markdown(this.information); |
||||
} |
||||
|
||||
async getPrivileges() { |
||||
let privileges = await UserPrivilege.find({ |
||||
where: { |
||||
user_id: this.id |
||||
} |
||||
}); |
||||
|
||||
return privileges.map(x => x.privilege); |
||||
} |
||||
|
||||
async setPrivileges(newPrivileges) { |
||||
let oldPrivileges = await this.getPrivileges(); |
||||
|
||||
let delPrivileges = oldPrivileges.filter(x => !newPrivileges.includes(x)); |
||||
let addPrivileges = newPrivileges.filter(x => !oldPrivileges.includes(x)); |
||||
|
||||
for (let privilege of delPrivileges) { |
||||
let obj = await UserPrivilege.findOne({ where: { |
||||
user_id: this.id, |
||||
privilege: privilege |
||||
} }); |
||||
|
||||
await obj.destroy(); |
||||
} |
||||
|
||||
for (let privilege of addPrivileges) { |
||||
let obj = await UserPrivilege.create({ |
||||
user_id: this.id, |
||||
privilege: privilege |
||||
}); |
||||
|
||||
await obj.save(); |
||||
} |
||||
} |
||||
|
||||
async hasPrivilege(privilege) { |
||||
if (this.is_admin) return true; |
||||
|
||||
let x = await UserPrivilege.findOne({ where: { user_id: this.id, privilege: privilege } }); |
||||
return !!x; |
||||
} |
||||
|
||||
async getLastSubmitLanguage() { |
||||
let a = await JudgeState.findOne({ |
||||
where: { |
||||
user_id: this.id |
||||
}, |
||||
order: { |
||||
submit_time: 'DESC' |
||||
} |
||||
}); |
||||
if (a) return a.language; |
||||
|
||||
return null; |
||||
} |
||||
} |
@ -1,37 +0,0 @@
|
||||
let Sequelize = require('sequelize'); |
||||
let db = syzoj.db; |
||||
|
||||
let model = db.define('user_privilege', { |
||||
user_id: { type: Sequelize.INTEGER, primaryKey: true }, |
||||
privilege: { |
||||
type: Sequelize.STRING, |
||||
primaryKey: true |
||||
} |
||||
}, { |
||||
timestamps: false, |
||||
tableName: 'user_privilege', |
||||
indexes: [ |
||||
{ |
||||
fields: ['user_id'] |
||||
}, |
||||
{ |
||||
fields: ['privilege'] |
||||
} |
||||
] |
||||
}); |
||||
|
||||
let Model = require('./common'); |
||||
class UserPrivilege extends Model { |
||||
static async create(val) { |
||||
return UserPrivilege.fromRecord(UserPrivilege.model.build(Object.assign({ |
||||
user_id: 0, |
||||
privilege: '' |
||||
}, val))); |
||||
} |
||||
|
||||
getModel() { return model; } |
||||
} |
||||
|
||||
UserPrivilege.model = model; |
||||
|
||||
module.exports = UserPrivilege; |
@ -0,0 +1,13 @@
|
||||
import * as TypeORM from "typeorm"; |
||||
import Model from "./common"; |
||||
|
||||
@TypeORM.Entity() |
||||
export default class UserPrivilege extends Model { |
||||
@TypeORM.Index() |
||||
@TypeORM.Column({ type: "integer", primary: true }) |
||||
user_id: number; |
||||
|
||||
@TypeORM.Index() |
||||
@TypeORM.Column({ type: "varchar", length: 80, primary: true }) |
||||
privilege: string; |
||||
} |
@ -1,50 +0,0 @@
|
||||
let Sequelize = require('sequelize'); |
||||
let db = syzoj.db; |
||||
|
||||
let JudgeState = syzoj.model('judge_state'); |
||||
let CustomTest = syzoj.model('custom_test'); |
||||
|
||||
let model = db.define('waiting_judge', { |
||||
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, |
||||
judge_id: { type: Sequelize.INTEGER }, |
||||
|
||||
// Smaller is higher
|
||||
priority: { type: Sequelize.INTEGER }, |
||||
|
||||
type: { |
||||
type: Sequelize.ENUM, |
||||
values: ['submission', 'custom-test'] |
||||
} |
||||
}, { |
||||
timestamps: false, |
||||
tableName: 'waiting_judge', |
||||
indexes: [ |
||||
{ |
||||
fields: ['judge_id'], |
||||
} |
||||
] |
||||
}); |
||||
|
||||
let Model = require('./common'); |
||||
class WaitingJudge extends Model { |
||||
static async create(val) { |
||||
return WaitingJudge.fromRecord(WaitingJudge.model.build(Object.assign({ |
||||
judge_id: 0, |
||||
priority: 0 |
||||
}, val))); |
||||
} |
||||
|
||||
async getCustomTest() { |
||||
return CustomTest.fromID(this.judge_id); |
||||
} |
||||
|
||||
async getJudgeState() { |
||||
return JudgeState.fromID(this.judge_id); |
||||
} |
||||
|
||||
getModel() { return model; } |
||||
} |
||||
|
||||
WaitingJudge.model = model; |
||||
|
||||
module.exports = WaitingJudge; |
@ -0,0 +1,11 @@
|
||||
{ |
||||
"compilerOptions": { |
||||
"module": "commonjs", |
||||
"preserveConstEnums": true, |
||||
"sourceMap": true, |
||||
"outDir": "./models-built", |
||||
"rootDir": "./models", |
||||
"emitDecoratorMetadata": true, |
||||
"experimentalDecorators": true |
||||
} |
||||
} |
Loading…
Reference in new issue