zoukankan      html  css  js  c++  java
  • CodingSouls团队项目冲刺(7)-个人概况

    团队冲刺第七天:

      题库后端继续:

      1 import * as TypeORM from "typeorm";
      2 import Model from "./common";
      3 
      4 declare var syzoj, ErrorMessage: any;
      5 
      6 import User from "./user";
      7 import File from "./file";
      8 import JudgeState from "./judge_state";
      9 import Contest from "./contest";
     10 import ProblemTag from "./problem_tag";
     11 import ProblemTagMap from "./problem_tag_map";
     12 import SubmissionStatistics, { StatisticsType } from "./submission_statistics";
     13 
     14 import * as fs from "fs-extra";
     15 import * as path from "path";
     16 import * as util from "util";
     17 import * as LRUCache from "lru-cache";
     18 import * as DeepCopy from "deepcopy";
     19 
     20 const problemTagCache = new LRUCache<number, number[]>({
     21   max: syzoj.config.db.cache_size
     22 });
     23 
     24 enum ProblemType {
     25   Traditional = "traditional",
     26   SubmitAnswer = "submit-answer",
     27   Interaction = "interaction"
     28 }
     29 
     30 const statisticsTypes = {
     31   fastest: ['total_time', 'ASC'],
     32   slowest: ['total_time', 'DESC'],
     33   shortest: ['code_length', 'ASC'],
     34   longest: ['code_length', 'DESC'],
     35   min: ['max_memory', 'ASC'],
     36   max: ['max_memory', 'DESC'],
     37   earliest: ['submit_time', 'ASC']
     38 };
     39 
     40 const statisticsCodeOnly = ["fastest", "slowest", "min", "max"];
     41 
     42 @TypeORM.Entity()
     43 export default class Problem extends Model {
     44   static cache = true;
     45 
     46   @TypeORM.PrimaryGeneratedColumn()
     47   id: number;
     48 
     49   @TypeORM.Column({ nullable: true, type: "varchar", length: 80 })
     50   title: string;
     51 
     52   @TypeORM.Index()
     53   @TypeORM.Column({ nullable: true, type: "integer" })
     54   user_id: number;
     55 
     56   @TypeORM.Column({ nullable: true, type: "integer" })
     57   publicizer_id: number;
     58 
     59   @TypeORM.Column({ nullable: true, type: "boolean" })
     60   is_anonymous: boolean;
     61 
     62   @TypeORM.Column({ nullable: true, type: "text" })
     63   description: string;
     64 
     65   @TypeORM.Column({ nullable: true, type: "text" })
     66   input_format: string;
     67 
     68   @TypeORM.Column({ nullable: true, type: "text" })
     69   output_format: string;
     70 
     71   @TypeORM.Column({ nullable: true, type: "text" })
     72   example: string;
     73 
     74   @TypeORM.Column({ nullable: true, type: "text" })
     75   limit_and_hint: string;
     76 
     77   @TypeORM.Column({ nullable: true, type: "integer" })
     78   time_limit: number;
     79 
     80   @TypeORM.Column({ nullable: true, type: "integer" })
     81   memory_limit: number;
     82 
     83   @TypeORM.Column({ nullable: true, type: "integer" })
     84   additional_file_id: number;
     85 
     86   @TypeORM.Column({ nullable: true, type: "integer" })
     87   ac_num: number;
     88 
     89   @TypeORM.Column({ nullable: true, type: "integer" })
     90   submit_num: number;
     91 
     92   @TypeORM.Index()
     93   @TypeORM.Column({ nullable: true, type: "boolean" })
     94   is_public: boolean;
     95 
     96   @TypeORM.Column({ nullable: true, type: "boolean" })
     97   file_io: boolean;
     98 
     99   @TypeORM.Column({ nullable: true, type: "text" })
    100   file_io_input_name: string;
    101 
    102   @TypeORM.Column({ nullable: true, type: "text" })
    103   file_io_output_name: string;
    104 
    105   @TypeORM.Index()
    106   @TypeORM.Column({ nullable: true, type: "datetime" })
    107   publicize_time: Date;
    108 
    109   @TypeORM.Column({ nullable: true,
    110       type: "enum",
    111       enum: ProblemType,
    112       default: ProblemType.Traditional
    113   })
    114   type: ProblemType;
    115 
    116   user?: User;
    117   publicizer?: User;
    118   additional_file?: File;
    119 
    120   async loadRelationships() {
    121     this.user = await User.findById(this.user_id);
    122     this.publicizer = await User.findById(this.publicizer_id);
    123     this.additional_file = await File.findById(this.additional_file_id);
    124   }
    125 
    126   async isAllowedEditBy(user) {
    127     if (!user) return false;
    128     if (await user.hasPrivilege('manage_problem')) return true;
    129     return this.user_id === user.id;
    130   }
    131 
    132   async isAllowedUseBy(user) {
    133     if (this.is_public) return true;
    134     if (!user) return false;
    135     if (await user.hasPrivilege('manage_problem')) return true;
    136     return this.user_id === user.id;
    137   }
    138 
    139   async isAllowedManageBy(user) {
    140     if (!user) return false;
    141     if (await user.hasPrivilege('manage_problem')) return true;
    142     return user.is_admin;
    143   }
    144 
    145   getTestdataPath() {
    146     return syzoj.utils.resolvePath(syzoj.config.upload_dir, 'testdata', this.id.toString());
    147   }
    148 
    149   getTestdataArchivePath() {
    150     return syzoj.utils.resolvePath(syzoj.config.upload_dir, 'testdata-archive', this.id.toString() + '.zip');
    151   }
    152 
    153   async updateTestdata(path, noLimit) {
    154     await syzoj.utils.lock(['Problem::Testdata', this.id], async () => {
    155       let unzipSize = 0, unzipCount = 0;
    156       let p7zip = new (require('node-7z'));
    157       await p7zip.list(path).progress(files => {
    158         unzipCount += files.length;
    159         for (let file of files) unzipSize += file.size;
    160       });
    161       if (!noLimit && unzipCount > syzoj.config.limit.testdata_filecount) throw new ErrorMessage('数据包中的文件太多。');
    162       if (!noLimit && unzipSize > syzoj.config.limit.testdata) throw new ErrorMessage('数据包太大。');
    163 
    164       let dir = this.getTestdataPath();
    165       await fs.remove(dir);
    166       await fs.ensureDir(dir);
    167 
    168       let execFileAsync = util.promisify(require('child_process').execFile);
    169       await execFileAsync(__dirname + '/../bin/unzip', ['-j', '-o', '-d', dir, path]);
    170       await fs.move(path, this.getTestdataArchivePath(), { overwrite: true });
    171     });
    172   }
    173 
    174   async uploadTestdataSingleFile(filename, filepath, size, noLimit) {
    175     await syzoj.utils.lock(['Promise::Testdata', this.id], async () => {
    176       let dir = this.getTestdataPath();
    177       await fs.ensureDir(dir);
    178 
    179       let oldSize = 0, list = await this.listTestdata(), replace = false, oldCount = 0;
    180       if (list) {
    181         oldCount = list.files.length;
    182         for (let file of list.files) {
    183           if (file.filename !== filename) oldSize += file.size;
    184           else replace = true;
    185         }
    186       }
    187 
    188       if (!noLimit && oldSize + size > syzoj.config.limit.testdata) throw new ErrorMessage('数据包太大。');
    189       if (!noLimit && oldCount + (!replace as any as number) > syzoj.config.limit.testdata_filecount) throw new ErrorMessage('数据包中的文件太多。');
    190 
    191       await fs.move(filepath, path.join(dir, filename), { overwrite: true });
    192 
    193       let execFileAsync = util.promisify(require('child_process').execFile);
    194       try { await execFileAsync('dos2unix', [path.join(dir, filename)]); } catch (e) {}
    195 
    196       await fs.remove(this.getTestdataArchivePath());
    197     });
    198   }
    199 
    200   async deleteTestdataSingleFile(filename) {
    201     await syzoj.utils.lock(['Promise::Testdata', this.id], async () => {
    202       await fs.remove(path.join(this.getTestdataPath(), filename));
    203       await fs.remove(this.getTestdataArchivePath());
    204     });
    205   }
    206 
    207   async makeTestdataZip() {
    208     await syzoj.utils.lock(['Promise::Testdata', this.id], async () => {
    209       let dir = this.getTestdataPath();
    210       if (!await syzoj.utils.isDir(dir)) throw new ErrorMessage('无测试数据。');
    211 
    212       let p7zip = new (require('node-7z'));
    213 
    214       let list = await this.listTestdata(), pathlist = list.files.map(file => path.join(dir, file.filename));
    215       if (!pathlist.length) throw new ErrorMessage('无测试数据。');
    216       await fs.ensureDir(path.resolve(this.getTestdataArchivePath(), '..'));
    217       await p7zip.add(this.getTestdataArchivePath(), pathlist);
    218     });
    219   }
    220 
    221   async hasSpecialJudge() {
    222     try {
    223       let dir = this.getTestdataPath();
    224       let list = await fs.readdir(dir);
    225       return list.includes('spj.js') || list.find(x => x.startsWith('spj_')) !== undefined;
    226     } catch (e) {
    227       return false;
    228     }
    229   }
    230 
    231   async listTestdata() {
    232     try {
    233       let dir = this.getTestdataPath();
    234       let filenameList = await fs.readdir(dir);
    235       let list = await Promise.all(filenameList.map(async x => {
    236         let stat = await fs.stat(path.join(dir, x));
    237         if (!stat.isFile()) return undefined;
    238         return {
    239           filename: x,
    240           size: stat.size
    241         };
    242       }));
    243 
    244       list = list.filter(x => x);
    245 
    246       let res = {
    247         files: list,
    248         zip: null
    249       };
    250 
    251       try {
    252         let stat = await fs.stat(this.getTestdataArchivePath());
    253         if (stat.isFile()) {
    254           res.zip = {
    255             size: stat.size
    256           };
    257         }
    258       } catch (e) {
    259         if (list) {
    260           res.zip = {
    261             size: null
    262           };
    263         }
    264       }
    265 
    266       return res;
    267     } catch (e) {
    268       return null;
    269     }
    270   }
    271 
    272   async updateFile(path, type, noLimit) {
    273     let file = await File.upload(path, type, noLimit);
    274 
    275     if (type === 'additional_file') {
    276       this.additional_file_id = file.id;
    277     }
    278 
    279     await this.save();
    280   }
    281 
    282   async validate() {
    283     if (this.time_limit <= 0) return 'Invalid time limit';
    284     if (this.time_limit > syzoj.config.limit.time_limit) return 'Time limit too large';
    285     if (this.memory_limit <= 0) return 'Invalid memory limit';
    286     if (this.memory_limit > syzoj.config.limit.memory_limit) return 'Memory limit too large';
    287     if (!['traditional', 'submit-answer', 'interaction'].includes(this.type)) return 'Invalid problem type';
    288 
    289     if (this.type === 'traditional') {
    290       let filenameRE = /^[w -+.]*$/;
    291       if (this.file_io_input_name && !filenameRE.test(this.file_io_input_name)) return 'Invalid input file name';
    292       if (this.file_io_output_name && !filenameRE.test(this.file_io_output_name)) return 'Invalid output file name';
    293 
    294       if (this.file_io) {
    295         if (!this.file_io_input_name) return 'No input file name';
    296         if (!this.file_io_output_name) return 'No output file name';
    297       }
    298     }
    299 
    300     return null;
    301   }
    302 async setTags(newTagIDs) {
    303     let oldTagIDs = (await this.getTags()).map(x => x.id);
    304 
    305     let delTagIDs = oldTagIDs.filter(x => !newTagIDs.includes(x));
    306     let addTagIDs = newTagIDs.filter(x => !oldTagIDs.includes(x));
    307 
    308     for (let tagID of delTagIDs) {
    309       let map = await ProblemTagMap.findOne({
    310         where: {
    311           problem_id: this.id,
    312           tag_id: tagID
    313         }
    314       });
    315 
    316       await map.destroy();
    317     }
    318 
    319     for (let tagID of addTagIDs) {
    320       let map = await ProblemTagMap.create({
    321         problem_id: this.id,
    322         tag_id: tagID
    323       });
    324 
    325       await map.save();
    326     }
    327 
    328     problemTagCache.set(this.id, newTagIDs);
    329   }
    330 
    331   async changeID(id) {
    332     const entityManager = TypeORM.getManager();
    333 
    334     id = parseInt(id);
    335     await entityManager.query('UPDATE `problem`               SET `id`         = ' + id + ' WHERE `id`         = ' + this.id);
    336     await entityManager.query('UPDATE `judge_state`           SET `problem_id` = ' + id + ' WHERE `problem_id` = ' + this.id);
    337     await entityManager.query('UPDATE `problem_tag_map`       SET `problem_id` = ' + id + ' WHERE `problem_id` = ' + this.id);
    338     await entityManager.query('UPDATE `article`               SET `problem_id` = ' + id + ' WHERE `problem_id` = ' + this.id);
    339     await entityManager.query('UPDATE `submission_statistics` SET `problem_id` = ' + id + ' WHERE `problem_id` = ' + this.id);
    340 
    341     let contests = await Contest.find();
    342     for (let contest of contests) {
    343       let problemIDs = await contest.getProblems();
    344 
    345       let flag = false;
    346       for (let i in problemIDs) {
    347         if (problemIDs[i] === this.id) {
    348           problemIDs[i] = id;
    349           flag = true;
    350         }
    351       }
    352 
    353       if (flag) {
    354         await contest.setProblemsNoCheck(problemIDs);
    355         await contest.save();
    356       }
    357     }
    358 
    359     let oldTestdataDir = this.getTestdataPath(), oldTestdataZip = this.getTestdataArchivePath();
    360 
    361     const oldID = this.id;
    362     this.id = id;
    363 
    364     // Move testdata
    365     let newTestdataDir = this.getTestdataPath(), newTestdataZip = this.getTestdataArchivePath();
    366     if (await syzoj.utils.isDir(oldTestdataDir)) {
    367       await fs.move(oldTestdataDir, newTestdataDir);
    368     }
    369 
    370     if (await syzoj.utils.isFile(oldTestdataZip)) {
    371       await fs.move(oldTestdataZip, newTestdataZip);
    372     }
    373 
    374     await this.save();
    375 
    376     await Problem.deleteFromCache(oldID);
    377     await problemTagCache.del(oldID);
    378   }
    379 
    380   async delete() {
    381     const entityManager = TypeORM.getManager();
    382 
    383     let oldTestdataDir = this.getTestdataPath(), oldTestdataZip = this.getTestdataPath();
    384     await fs.remove(oldTestdataDir);
    385     await fs.remove(oldTestdataZip);
    386 
    387     let submissions = await JudgeState.find({
    388       where: {
    389         problem_id: this.id
    390       }
    391     }), submitCnt = {}, acUsers = new Set();
    392     for (let sm of submissions) {
    393       if (sm.status === 'Accepted') acUsers.add(sm.user_id);
    394       if (!submitCnt[sm.user_id]) {
    395         submitCnt[sm.user_id] = 1;
    396       } else {
    397         submitCnt[sm.user_id]++;
    398       }
    399     }
    400 
    401     for (let u in submitCnt) {
    402       let user = await User.findById(parseInt(u));
    403       user.submit_num -= submitCnt[u];
    404       if (acUsers.has(parseInt(u))) user.ac_num--;
    405       await user.save();
    406     }
    407 
    408     problemTagCache.del(this.id);
    409 
    410     await entityManager.query('DELETE FROM `judge_state`           WHERE `problem_id` = ' + this.id);
    411     await entityManager.query('DELETE FROM `problem_tag_map`       WHERE `problem_id` = ' + this.id);
    412     await entityManager.query('DELETE FROM `article`               WHERE `problem_id` = ' + this.id);
    413     await entityManager.query('DELETE FROM `submission_statistics` WHERE `problem_id` = ' + this.id);
    414 
    415     await this.destroy();
    416   }
    417 }
    继续
  • 相关阅读:
    关于自发电的一些想法
    折腾了一个晚上的业余时间,终于把一块廉价的tftlcd连到了我的树莓派上
    关于飞行器从外太空返回地球时候的减速器的思考与假象
    echart漏斗图背景多出一个方框的问题解决,浪费了两个小时的教训
    linux加密文件系统 fsck 无法修复一例
    天津
    [转]Bitmap图片缩放
    python spark 配置
    screen
    tar 命令
  • 原文地址:https://www.cnblogs.com/125418a/p/12781745.html
Copyright © 2011-2022 走看看