class Problem(db.Model): __table_args__ = {'schema':'moodle'} __tablename__ = 'mdl_problems' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.Unicode(255)) content = db.Column(db.Text) review = db.Column(db.Text) hidden = db.Column(db.Boolean) timelimit = db.Column(db.Float) memorylimit = db.Column(db.Integer) description = db.Column(db.Text) analysis = db.Column(db.Text) sample_tests = db.Column(db.Unicode(255)) sample_tests_html = db.Column(db.Text) sample_tests_json = db.Column(JsonType) show_limits = db.Column(db.Boolean) output_only = db.Column(db.Boolean) pr_id = db.Column(db.Integer, db.ForeignKey('moodle.mdl_ejudge_problem.id')) ejudge_problem = relationship('EjudgeProblem', lazy='select') def __init__(self, *args, **kwargs): super(Problem, self).__init__(*args, **kwargs) self.hidden = 1 self.show_limits = True
class Resource(CourseModuleInstance, db.Model): """ Модуль курса, описывающий ссылку """ __table_args__ = {'schema': 'moodle'} __tablename__ = 'mdl_resource' __mapper_args__ = { 'polymorphic_identity': 'resource', 'concrete': True, } MODULE = 13 id = db.Column(db.Integer, primary_key=True) course_id = db.Column('course', db.Integer) name = db.Column(db.Unicode(255)) type_ = db.Column('type', db.Unicode(30)) reference = db.Column(db.Unicode(255)) summary = db.Column(db.UnicodeText()) def serialize(self): serialized = attrs_to_dict( self, 'id', 'course_id', 'name', 'type', 'reference', 'summary', ) serialized['type'] = self.type_ return serialized
class SimpleUser(db.Model): RESET_PASSWORD_LENGTH = 20 __table_args__ = (db.Index('ej_id', 'ej_id'), {'schema': 'moodle'}) __tablename__ = 'mdl_user' id = db.Column(db.Integer, primary_key=True) firstname = db.Column(db.Unicode(100)) lastname = db.Column(db.Unicode(100)) login = db.Column('ej_login', db.Unicode(50)) password = db.Column('ej_password', db.Unicode(50)) deleted = db.Column('deleted', db.Boolean) ejudge_id = db.Column('ej_id', db.Integer) problems_solved = db.Column(db.Integer) password_md5 = db.Column('password', db.Unicode(32)) statement = db.relationship( 'Statement', secondary=StatementUser.__table__, backref='StatementUsers1', lazy='dynamic', ) statements = association_proxy('StatementUsers2', 'statement') def reset_password(self): """ Генерирует случайный пароль для пользователя и возвращает его """ new_password = random_password(self.RESET_PASSWORD_LENGTH) self.password_md5 = hash_password(new_password) return new_password
class Course(db.Model): __table_args__ = {'schema': 'moodle'} __tablename__ = 'mdl_course' id = db.Column(db.Integer, primary_key=True) full_name = db.Column('fullname', db.Unicode(254)) short_name = db.Column('shortname', db.Unicode(100)) category = db.Column(db.Integer) password = db.Column(db.Unicode(50)) visible = db.Column(db.Boolean) def require_password(self): return bool(self.password) @deprecated('view.serializers.course.CourseSchema') def serialize(self): serialized = attrs_to_dict( self, 'id', 'full_name', 'short_name', ) serialized['require_password'] = self.require_password() if not self.require_password(): serialized['sections'] = [ section.serialize() for section in self.sections.all() if section.visible ] return serialized
class Ideal(db.Model): __table_args__ = {'schema': 'moodle'} __tablename__ = 'mdl_ideal_solution' id = db.Column(db.Integer, primary_key=True) problem_id = db.Column(db.Integer, db.ForeignKey('moodle.mdl_problems.id')) run_id = db.Column(db.Integer, db.ForeignKey('ejudge.runs.run_id')) contest_id = db.Column(db.Integer, db.ForeignKey('ejudge.runs.contest_id')) lang_id = db.Column(db.Integer) lang = db.Column(db.Unicode(100)) code = db.Column(db.UnicodeText) author_id = db.Column(db.Integer, db.ForeignKey('moodle.mdl_user.id')) author_name = db.Column(db.Unicode(200)) comment = db.Column(db.UnicodeText) status = db.Column(db.Integer) def __init__(self, problem_id, run_id, contest_id, author_id, comment, status=0): self.problem_id = problem_id self.run_id = run_id self.contest_id = contest_id run = db.session.query(EjudgeRun) \ .filter_by(contest_id=contest_id) \ .filter_by(run_id=run_id) \ .one() user = db.session.query(User) \ .filter_by(id=author_id) \ .one() self.lang_id = run.lang_id self.lang = LANG[self.lang_id] self.code = get_run_code(run_id, contest_id) self.author_id = author_id self.author_name = user.firstname + ' ' + user.lastname self.comment = comment self.status = status def __json__(self, request): return { 'id': self.id, 'problem_id': self.problem_id, 'lang': self.lang, 'code': self.code, 'author_name': self.author_name, 'author_id': self.author_id, 'comment': self.comment, }
class Group(db.Model): __table_args__ = (db.ForeignKeyConstraint(['owner_id'], ['moodle.mdl_user.id']), { 'schema': 'moodle' }) __tablename__ = 'mdl_ejudge_group' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.Unicode(100)) description = db.Column(db.Text) owner_id = db.Column(db.Integer) visible = db.Column(db.Integer) owner = db.relationship('SimpleUser', backref=db.backref('groups', lazy='select'), lazy='joined') def serialize(self, attributes=None): if not attributes: attributes = ( 'name', 'description', 'owner_id', 'visible', ) serialized = attrs_to_dict(self, *attributes) return serialized
class User(SimpleUser): __mapper_args__ = {'polymorphic_identity': 'user'} username = db.Column(db.Unicode(100)) email = db.Column(db.Unicode(100)) city = db.Column(db.Unicode(20)) school = db.Column(db.Unicode(255)) problems_week_solved = db.Column(db.Integer) @classmethod def search(cls, filter_func: Callable[[Query], Query], filter_deleted=True): if filter_deleted: users_query = filter_func( db.session.query(cls).filter(cls.deleted == False)) else: users_query = filter_func(db.session.query(cls)) return users_query @classmethod def search_by_string(cls, search_string): def filter_func(query: Query): if search_string.count(' '): str1, str2 = search_string.split(' ', 1) query = query.filter( or_( and_(cls.firstname.like("%{}%".format(str1)), cls.lastname.like("%{}%".format(str2))), and_(cls.lastname.like("%{}%".format(str1)), cls.firstname.like("%{}%".format(str2))), )) else: query = query.filter( or_( cls.email.like("%{}%".format(search_string)), cls.username.like("%{}%".format(search_string)), cls.firstname.like("%{}%".format(search_string)), cls.lastname.like("%{}%".format(search_string)), )) return query return cls.search(filter_func) @lazy def _get_current_olymp(self): return None
class EjudgeContest(db.Model): __table_args__ = {'schema': 'moodle'} __tablename__ = 'mdl_ejudge_contest' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.Unicode(255)) ejudge_id = db.Column(db.Unicode(10)) ejudge_int_id = db.Column(db.Integer, nullable=False, primary_key=True, autoincrement=False) load_time = db.Column(db.DateTime) cloned = db.Column(db.Boolean, nullable=False) def __init__(self, name='', ejudge_int_id=0): self.name = name self.ejudge_id = get_contest_str_id(ejudge_int_id) self.ejudge_int_id = ejudge_int_id self.load_time = datetime.datetime.now() self.cloned = False
class MonitorCourseModule(CourseModuleInstance, db.Model): """ Модуль курса, описывающий монитор """ __table_args__ = {'schema': 'moodle'} __tablename__ = 'mdl_monitor' __mapper_args__ = { 'polymorphic_identity': 'monitor', 'concrete': True, } MODULE = 28 id = db.Column(db.Integer, primary_key=True) course_id = db.Column('course', db.Integer) name = db.Column(db.Unicode(255), nullable=False, default='') monitor_id = db.Column(db.Integer, db.ForeignKey('moodle.mdl_monitors.id')) group_id = db.Column(db.Integer, nullable=False, default=0)
class Label(CourseModuleInstance, db.Model): __table_args__ = {'schema': 'moodle'} __tablename__ = 'mdl_label' __mapper_args__ = { 'polymorphic_identity': 'label', 'concrete': True, } MODULE = 9 id = db.Column(db.Integer, primary_key=True) course = db.Column(db.Integer) name = db.Column(db.Unicode(255)) content = db.Column(db.UnicodeText) def serialize(self): return attrs_to_dict( self, 'id', 'name', 'content', )
class EjudgeProblem(Problem): """ Модель задачи из ejudge ejudge_prid -- primary key, на который ссылается Problem.pr_id. После инициализации, соответствтующему объекту Problem проставляется корректный pr_id contest_id -- ejudge_contest_id -- соответствует contest_id из ejudge secondary_ejudge_contest_id -- problem_id -- соответствует problem_id из ejudge short_id -- короткий id (обычно буква) Здесь используется наследование и polymorphic_identity Это значит, что можно написать problem_id = Problem.id query(EjudgeProblem).filter(EjudgeProblem.id == problem_id) # вернет Problem При этом неявно приджойнится Problem.pr_id == EjudgeProblem.id Ещё это даёт нам интересные эффекты, например query(EjudgeProblem).all() вернёт как EjudgeProblem, так и Problem, a query(EjudgeProblem).filter(EjudgeProblem.id == 6).all() -- только Problem А вот такое query(EjudgeProblem).get(6) -- вообще вернёт None, потому что join не отработает Ещё в Runs у нас ссылка на обе таблицы, и это тоже работает нормально """ __table_args__ = ( db.Index('ejudge_contest_id_problem_id', 'ejudge_contest_id', 'problem_id'), {'schema':'moodle', 'extend_existing': True} ) __tablename__ = 'mdl_ejudge_problem' __mapper_args__ = {'polymorphic_identity': 'ejudgeproblem'} ejudge_prid = db.Column('id', db.Integer, primary_key=True) #global id in ejudge contest_id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=False) ejudge_contest_id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=False) secondary_ejudge_contest_id = db.Column(db.Integer, nullable=True) problem_id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=False) #id in contest short_id = db.Column(db.Unicode(50)) ejudge_name = db.Column('name', db.Unicode(255)) @staticmethod def create(**kwargs): """ При создании EjudgeProblem сначала в базу пишет Problem потом EjudgeProblem, из-за чего pr_id не проставляется """ instance = EjudgeProblem(**kwargs) db.session.add(instance) db.session.flush([instance]) problem_id = instance.id ejudge_problem_id = instance.pr_id db.session.commit() problem_instance = db.session.query(Problem).filter_by(id=problem_id).one() problem_instance.pr_id = ejudge_problem_id db.session.commit() return db.session.query(EjudgeProblem).filter_by(id=problem_id).one() @deprecated('view.serializers.ProblemSchema') def serialize(self): if self.sample_tests: self.generateSamplesJson(force_update=True) attrs = [ 'id', 'name', 'content', 'timelimit', 'memorylimit', 'show_limits', 'sample_tests_json', 'output_only', ] problem_dict = { attr: getattr(self, attr, 'undefined') for attr in attrs } problem_dict['content'] = problem_dict['content'].replace("\\)", "$").replace("\\(", "$") # problem_dict['languages'] = context.get_allowed_languages() return problem_dict def get_test(self, test_num, size=255): conf = EjudgeContestCfg(number=self.ejudge_contest_id) prob = conf.getProblem(self.problem_id) test_file_name = (prob.tests_dir + prob.test_pat) % int(test_num) error_str = None if os.path.exists(test_file_name): res = read_file_unknown_encoding(test_file_name, size) else: res = test_file_name return res def get_test_size(self, test_num): conf = EjudgeContestCfg(number=self.ejudge_contest_id) prob = conf.getProblem(self.problem_id) test_file_name = (prob.tests_dir + prob.test_pat) % int(test_num) return os.stat(test_file_name).st_size def get_corr(self, test_num, size=255): conf = EjudgeContestCfg(number = self.ejudge_contest_id) prob = conf.getProblem(self.problem_id) corr_file_name = (prob.tests_dir + prob.corr_pat) % int(test_num) error_str = None if os.path.exists(corr_file_name): res = read_file_unknown_encoding(corr_file_name, size) else: res = corr_file_name return res def get_test_full(self, test_num, size=255): """ Возвращает словарь с полной информацией о тесте """ test = {} if self.get_test_size(int(test_num)) <= 255: test["input"] = self.get_test(int(test_num), size=size) test["big_input"] = False else: test["input"] = self.get_test(int(test_num), size=size) + "...\n" test["big_input"] = True if self.get_corr_size(int(test_num)) <= 255: test["corr"] = self.get_corr(int(test_num), size=size) test["big_corr"] = False else: test["corr"] = self.get_corr(int(test_num), size=size) + "...\n" test["big_corr"] = True return test def get_corr_size(self, test_num): conf = EjudgeContestCfg(number = self.ejudge_contest_id) prob = conf.getProblem(self.problem_id) corr_file_name = (prob.tests_dir + prob.corr_pat) % int(test_num) return os.stat(corr_file_name).st_size def get_checker(self): conf = EjudgeContestCfg(number = self.ejudge_contest_id) prob = conf.getProblem(self.problem_id) #generate dir with checker checker_dir = None if conf.advanced_layout: checker_dir = os.path.join(conf.contest_path, "problems", prob.internal_name) else: checker_dir = os.path.join(conf.contest_path, "checkers") #trying to find checker find_res = glob.glob(os.path.join(checker_dir, "check_{0}.*".format(prob.internal_name))) check_src = None checker_ext = None if find_res: check_src = open(find_res[0], "r").read() checker_ext = os.path.splitext(find_res[0])[1] #if checker not found then try polygon package downloads_dir = os.path.join(conf.contest_path, "download") if check_src is None and os.path.exists(downloads_dir): download_archive_mask = "{0}-*$linux.zip".format(prob.internal_name) find_archive_result = glob.glob(os.path.join(downloads_dir, download_archive_mask)) download_archive_path = find_archive_result[0] if find_archive_result else None archive = None if download_archive_path is not None: archive = ZipFile(download_archive_path) if archive is not None: member_path = None for file in archive.namelist(): if file.startswith("check."): member_path = file break try: check_src = archive.open(member_path).read() checker_ext = os.path.splitext(member_path)[1] except KeyError: check_src = None if check_src is None: check_src = "checker not found" return check_src, checker_ext def generateSamples(self): res = "" if self.sample_tests != '': res = "<div class='problem-statement'><div class='sample-tests'><div class='section-title'>Примеры</div>" for i in self.sample_tests.split(","): inp = self.get_test(i, 4096) if inp[-1] == '\n': inp = inp[:-1] corr = self.get_corr(i, 4096) if corr[-1] == '\n': corr = corr[:-1] res += "<div class='sample-test'>" res += "<div class='input'><div class='title'>Входные данные</div><pre class='content'>" res += inp res += "</pre></div><div class='output'><div class='title'>Выходные данные</div><pre class='content'>" res += corr res += "</pre></div></div>" res += "</div></div>" self.sample_tests_html = res return self.sample_tests def generateSamplesJson(self, force_update=False): if self.sample_tests != '': if not self.sample_tests_json: self.sample_tests_json = {} for test in self.sample_tests.split(','): if not force_update and test in self.sample_tests_json: continue test_input = self.get_test(test, 4096) test_correct = self.get_corr(test, 4096) self.sample_tests_json[test] = { 'input': test_input, 'correct': test_correct, }
)) return query return cls.search(filter_func) @lazy def _get_current_olymp(self): return None class PynformaticsUser(User): __table_args__ = {'schema': 'moodle'} __tablename__ = 'mdl_user_settings' __mapper_args__ = {'polymorphic_identity': 'pynformaticsuser'} id = db.Column(db.Integer, db.ForeignKey('moodle.mdl_user.id'), primary_key=True) main_page_settings = db.Column(db.Text) # SQLAlchemy Core table to avoiding class instance creation LightWeightUser = Table('mdl_user', MetaData(), db.Column('id', db.Integer, primary_key=True), db.Column('firstname', db.Unicode(100)), db.Column('lastname', db.Unicode(100)), db.Column('email', db.Unicode(100)), db.Column('username', db.Unicode(100)), schema='moodle')
cls.firstname.like("%{}%".format(search_string)), cls.lastname.like("%{}%".format(search_string)), )) return query return cls.search(filter_func) @lazy def _get_current_olymp(self): return None class PynformaticsUser(User): __table_args__ = {'schema': 'moodle'} __tablename__ = 'mdl_user_settings' __mapper_args__ = {'polymorphic_identity': 'pynformaticsuser'} id = db.Column(db.Integer, db.ForeignKey('moodle.mdl_user.id'), primary_key=True) main_page_settings = db.Column(db.Text) # SQLAlchemy Core table to avoiding class instance creation LightWeightUser = Table('mdl_user', MetaData(), db.Column('id', db.Integer, primary_key=True), db.Column('firstname', db.Unicode(100)), db.Column('lastname', db.Unicode(100)), schema='moodle')
def name(cls): return db.Column(db.Unicode(255))
class Statement(CourseModuleInstance, db.Model): __table_args__ = {'schema': 'moodle'} __tablename__ = 'mdl_statements' __mapper_args__ = { 'polymorphic_identity': 'statement', 'concrete': True, } MODULE = 19 id = db.Column(db.Integer, primary_key=True) course_id = db.Column('course', db.Integer, db.ForeignKey('moodle.mdl_course.id')) name = db.Column(db.Unicode(255)) summary = db.Column(MEDIUMTEXT) numbering = db.Column(db.Integer) disable_printing = db.Column('disableprinting', db.Boolean) custom_titles = db.Column('customtitles', db.Boolean) time_created = db.Column('timecreated', db.Integer) time_modified = db.Column('timemodified', db.Integer) contest_id = db.Column(db.Integer) time_start = db.Column('timestart', db.Integer) time_stop = db.Column('timestop', db.Integer) olympiad = db.Column(db.Boolean) virtual_olympiad = db.Column(db.Boolean) virtual_duration = db.Column(db.Integer) settings = db.Column(JsonType) course = db.relationship('Course', backref=db.backref('statements', lazy='dynamic')) problems = association_proxy('StatementProblems', 'problem') user = association_proxy('StatementUsers1', 'user') SETTINGS_SCHEMA = { 'type': 'object', 'properties': { 'allowed_languages': { 'type': 'array', 'uniqueItems': True, 'items': { 'type': 'integer', 'enum': list(LANG_NAME_BY_ID.keys()), } }, 'type': { 'oneOf': [{ 'type': 'null', }, { 'type': 'string', 'enum': [ 'olympiad', 'virtual', ], }], }, 'group': { 'type': 'integer', }, 'team': { 'type': 'boolean', }, 'time_start': { 'type': 'integer', }, 'time_stop': { 'type': 'integer', }, 'freeze_time': { 'type': 'integer', }, 'standings': { 'type': 'boolean', }, 'test_only_samples': { 'type': 'boolean', }, 'reset_submits_on_start': { 'type': 'boolean', }, 'test_until_fail': { 'type': 'boolean', }, 'start_from_scratch': { 'type': 'boolean', }, 'restrict_view': { 'type': 'boolean', } }, 'additionalProperties': False, } SETTINGS_SCHEMA_VALIDATOR = Draft4Validator(SETTINGS_SCHEMA) def get_allowed_languages(self): if not (self.settings and 'allowed_languages' in self.settings): return None return self.settings['allowed_languages'] def set_settings(self, settings): validation_error = next( self.SETTINGS_SCHEMA_VALIDATOR.iter_errors(settings), None) if validation_error: raise ValueError(validation_error.message) self.settings = settings if settings.get('time_start'): self.time_start = settings['time_start'] if settings.get('time_stop'): self.time_stop = settings['time_stop'] if 'type' in settings: type_ = settings['type'] if type_ == None: self.olympiad = False self.virtual_olympiad = False elif type_ == 'olympiad': self.olympiad = True self.virtual_olympiad = False else: self.olympiad = False self.virtual_olympiad = True self.time_modified = int(time.time()) def start_participant( self, user, duration, password=None, ): if self.course \ and self.course.require_password() \ and password != self.course.password: raise ValueError() if self.participants.filter(Participant.user_id == user.id).count(): raise ValueError() if user.get_active_participant(): raise ValueError() new_participant = Participant( user_id=user.id, statement_id=self.id, start=int(time.time()), duration=duration, ) db.session.add(new_participant) return new_participant def finish_participant(self, user): active_participant = user.get_active_participant() if not active_participant or active_participant.statement_id != self.id: raise ValueError() active_participant.duration = int(time.time() - active_participant.start) return active_participant def start( self, user, password=None, ): if not self.olympiad: raise ValueError() now = time.time() if now < self.time_start: raise ValueError() if now >= self.time_stop: raise ValueError() return self.start_participant( user=user, duration=self.time_stop - int(time.time()), password=password, ) def finish(self, user): if not self.olympiad: raise ValueError() return self.finish_participant(user) def serialize(self, attributes=None): if not attributes: attributes = ( 'id', 'name', 'olympiad', 'settings', 'time_start', 'time_stop', 'virtual_olympiad', 'virtual_duration', 'course_module_id', 'course', 'require_password', ) serialized = attrs_to_dict(self, *attributes) if 'course' in attributes and self.course: serialized['course'] = self.course.serialize() serialized['course_module_id'] = getattr(self.course_module, 'id', None) if 'require_password' in attributes: if self.course: serialized['require_password'] = self.course.require_password() else: serialized['require_password'] = False user = getattr(g, 'user', None) if self.olympiad or self.virtual_olympiad: if not user: return serialized try: participant = self.participants.filter_by( user_id=user.id).one() except NoResultFound: return serialized serialized['participant'] = participant.serialize() serialized['problems'] = { rank: { 'id': statement_problem.problem.id, 'name': statement_problem.problem.name, } for rank, statement_problem in self.StatementProblems.items() if statement_problem.problem and not statement_problem.hidden } return serialized