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

    团队冲刺第六天:

      题库数据库设计与交互:

      

      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  type: ProblemType;
     74 
     75   user?: User;
     76   publicizer?: User;
     77   additional_file?: File;
     78 
     79   async loadRelationships() {
     80     this.user = await User.findById(this.user_id);
     81     this.publicizer = await User.findById(this.publicizer_id);
     82     this.additional_file = await File.findById(this.additional_file_id);
     83   }
     84 
     85   async isAllowedEditBy(user) {
     86     if (!user) return false;
     87     if (await user.hasPrivilege('manage_problem')) return true;
     88     return this.user_id === user.id;
     89   }
     90 
     91   async isAllowedUseBy(user) {
     92     if (this.is_public) return true;
     93     if (!user) return false;
     94     if (await user.hasPrivilege('manage_problem')) return true;
     95     return this.user_id === user.id;
     96   }
     97 
     98   async isAllowedManageBy(user) {
     99     if (!user) return false;
    100     if (await user.hasPrivilege('manage_problem')) return true;
    101     return user.is_admin;
    102   }
    103 
    104   getTestdataPath() {
    105     return syzoj.utils.resolvePath(syzoj.config.upload_dir, 'testdata', this.id.toString());
    106   }
    107 
    108   getTestdataArchivePath() {
    109     return syzoj.utils.resolvePath(syzoj.config.upload_dir, 'testdata-archive', this.id.toString() + '.zip');
    110   }
    111  async deleteTestdataSingleFile(filename) {
    112     await syzoj.utils.lock(['Promise::Testdata', this.id], async () => {
    113       await fs.remove(path.join(this.getTestdataPath(), filename));
    114       await fs.remove(this.getTestdataArchivePath());
    115     });
    116   }
    117 
    118   async makeTestdataZip() {
    119     await syzoj.utils.lock(['Promise::Testdata', this.id], async () => {
    120       let dir = this.getTestdataPath();
    121       if (!await syzoj.utils.isDir(dir)) throw new ErrorMessage('无测试数据。');
    122 
    123       let p7zip = new (require('node-7z'));
    124 
    125       let list = await this.listTestdata(), pathlist = list.files.map(file => path.join(dir, file.filename));
    126       if (!pathlist.length) throw new ErrorMessage('无测试数据。');
    127       await fs.ensureDir(path.resolve(this.getTestdataArchivePath(), '..'));
    128       await p7zip.add(this.getTestdataArchivePath(), pathlist);
    129     });
    130   }
    131 
    132   async hasSpecialJudge() {
    133     try {
    134       let dir = this.getTestdataPath();
    135       let list = await fs.readdir(dir);
    136       return list.includes('spj.js') || list.find(x => x.startsWith('spj_')) !== undefined;
    137     } catch (e) {
    138       return false;
    139     }
    140   }
    141 
    142   async listTestdata() {
    143     try {
    144       let dir = this.getTestdataPath();
    145       let filenameList = await fs.readdir(dir);
    146       let list = await Promise.all(filenameList.map(async x => {
    147         let stat = await fs.stat(path.join(dir, x));
    148         if (!stat.isFile()) return undefined;
    149         return {
    150           filename: x,
    151           size: stat.size
    152         };
    153       }));
    154 
    155       list = list.filter(x => x);
    156 
    157       let res = {
    158         files: list,
    159         zip: null
    160       };
    161 
    162       try {
    163         let stat = await fs.stat(this.getTestdataArchivePath());
    164         if (stat.isFile()) {
    165           res.zip = {
    166             size: stat.size
    167           };
    168         }
    169       } catch (e) {
    170         if (list) {
    171           res.zip = {
    172             size: null
    173           };
    174         }
    175       }
    176 
    177       return res;
    178     } catch (e) {
    179       return null;
    180     }
    181   }
    182 
    183   async updateFile(path, type, noLimit) {
    184     let file = await File.upload(path, type, noLimit);
    185 
    186     if (type === 'additional_file') {
    187       this.additional_file_id = file.id;
    188     }
    189 
    190     await this.save();
    191   }
    192 
    193   async validate() {
    194     if (this.time_limit <= 0) return 'Invalid time limit';
    195     if (this.time_limit > syzoj.config.limit.time_limit) return 'Time limit too large';
    196     if (this.memory_limit <= 0) return 'Invalid memory limit';
    197     if (this.memory_limit > syzoj.config.limit.memory_limit) return 'Memory limit too large';
    198     if (!['traditional', 'submit-answer', 'interaction'].includes(this.type)) return 'Invalid problem type';
    199 
    200     if (this.type === 'traditional') {
    201       let filenameRE = /^[w -+.]*$/;
    202       if (this.file_io_input_name && !filenameRE.test(this.file_io_input_name)) return 'Invalid input file name';
    203       if (this.file_io_output_name && !filenameRE.test(this.file_io_output_name)) return 'Invalid output file name';
    204 
    205       if (this.file_io) {
    206         if (!this.file_io_input_name) return 'No input file name';
    207         if (!this.file_io_output_name) return 'No output file name';
    208       }
    209     }
    210 
    211     return null;
    212   }
    213   async setTags(newTagIDs) {
    214     let oldTagIDs = (await this.getTags()).map(x => x.id);
    215 
    216     let delTagIDs = oldTagIDs.filter(x => !newTagIDs.includes(x));
    217     let addTagIDs = newTagIDs.filter(x => !oldTagIDs.includes(x));
    218 
    219     for (let tagID of delTagIDs) {
    220       let map = await ProblemTagMap.findOne({
    221         where: {
    222           problem_id: this.id,
    223           tag_id: tagID
    224         }
    225       });
    226 
    227       await map.destroy();
    228     }
    229 
    230     for (let tagID of addTagIDs) {
    231       let map = await ProblemTagMap.create({
    232         problem_id: this.id,
    233         tag_id: tagID
    234       });
    235 
    236       await map.save();
    237     }
    238 
    239     problemTagCache.set(this.id, newTagIDs);
    240   }
    241 
    242   async changeID(id) {
    243     const entityManager = TypeORM.getManager();
    244 
    245     id = parseInt(id);
    246     await entityManager.query('UPDATE `problem`               SET `id`         = ' + id + ' WHERE `id`         = ' + this.id);
    247     await entityManager.query('UPDATE `judge_state`           SET `problem_id` = ' + id + ' WHERE `problem_id` = ' + this.id);
    248     await entityManager.query('UPDATE `problem_tag_map`       SET `problem_id` = ' + id + ' WHERE `problem_id` = ' + this.id);
    249     await entityManager.query('UPDATE `article`               SET `problem_id` = ' + id + ' WHERE `problem_id` = ' + this.id);
    250     await entityManager.query('UPDATE `submission_statistics` SET `problem_id` = ' + id + ' WHERE `problem_id` = ' + this.id);
    251 
    252     let contests = await Contest.find();
    253     for (let contest of contests) {
    254       let problemIDs = await contest.getProblems();
    255 
    256       let flag = false;
    257       for (let i in problemIDs) {
    258         if (problemIDs[i] === this.id) {
    259           problemIDs[i] = id;
    260           flag = true;
    261         }
    262       }
    263 
    264       if (flag) {
    265         await contest.setProblemsNoCheck(problemIDs);
    266         await contest.save();
    267       }
    268     }
    269 
    270     let oldTestdataDir = this.getTestdataPath(), oldTestdataZip = this.getTestdataArchivePath();
    271 
    272     const oldID = this.id;
    273     this.id = id;
    274 
    275     // Move testdata
    276     let newTestdataDir = this.getTestdataPath(), newTestdataZip = this.getTestdataArchivePath();
    277     if (await syzoj.utils.isDir(oldTestdataDir)) {
    278       await fs.move(oldTestdataDir, newTestdataDir);
    279     }
    280 
    281     if (await syzoj.utils.isFile(oldTestdataZip)) {
    282       await fs.move(oldTestdataZip, newTestdataZip);
    283     }
    284 
    285     await this.save();
    286 
    287     await Problem.deleteFromCache(oldID);
    288     await problemTagCache.del(oldID);
    289   }
    290 
    291   async delete() {
    292     const entityManager = TypeORM.getManager();
    293 
    294     let oldTestdataDir = this.getTestdataPath(), oldTestdataZip = this.getTestdataPath();
    295     await fs.remove(oldTestdataDir);
    296     await fs.remove(oldTestdataZip);
    297 
    298     let submissions = await JudgeState.find({
    299       where: {
    300         problem_id: this.id
    301       }
    302     }), submitCnt = {}, acUsers = new Set();
    303     for (let sm of submissions) {
    304       if (sm.status === 'Accepted') acUsers.add(sm.user_id);
    305       if (!submitCnt[sm.user_id]) {
    306         submitCnt[sm.user_id] = 1;
    307       } else {
    308         submitCnt[sm.user_id]++;
    309       }
    310     }
    311 
    312     for (let u in submitCnt) {
    313       let user = await User.findById(parseInt(u));
    314       user.submit_num -= submitCnt[u];
    315       if (acUsers.has(parseInt(u))) user.ac_num--;
    316       await user.save();
    317     }
    318 
    319     problemTagCache.del(this.id);
    320 
    321     await entityManager.query('DELETE FROM `judge_state`           WHERE `problem_id` = ' + this.id);
    322     await entityManager.query('DELETE FROM `problem_tag_map`       WHERE `problem_id` = ' + this.id);
    323     await entityManager.query('DELETE FROM `article`               WHERE `problem_id` = ' + this.id);
    324     await entityManager.query('DELETE FROM `submission_statistics` WHERE `problem_id` = ' + this.id);
    325 
    326     await this.destroy();
    327   }
    328 }
    题库后端

      没写完,太难了吧。

  • 相关阅读:
    linux服务篇
    降智比赛题解
    CF 1437 题解
    ZR 2020普转提七连测day5
    20联赛集训day11 题解
    contest5 题解
    20联赛集训day9 题解
    20联赛集训day8 题解
    contest4 题解
    20联赛集训day7 题解
  • 原文地址:https://www.cnblogs.com/125418a/p/12781737.html
Copyright © 2011-2022 走看看