def _init_model(): global _field_list_clean global Events for field in field_list: if isinstance(field, tuple): params = field[1] field = field[0] else: params = {} params['null'] = True my_field = TextField(**params) my_field.add_to_class(Events, field) _field_list_clean.append(field)
class Arrival(BaseModel): amount = IntegerField() description = TextField() date = DateField() user = ForeignKeyField(model=User, on_delete='CASCADE')
class GovernanceBlock(BaseModel, GovernanceClass): governance_object = ForeignKeyField(GovernanceObject, related_name='governanceblocks', on_delete='CASCADE', on_update='CASCADE') event_block_height = IntegerField() payment_addresses = TextField() payment_amounts = TextField() proposal_hashes = TextField(default='') sb_hash = CharField() object_hash = CharField(max_length=64) govobj_type = GENESISD_GOVOBJ_TYPES['governanceblock'] only_masternode_can_submit = True class Meta: db_table = 'governanceblocks' def is_valid(self): import genesislib import decimal printdbg("In GovernanceBlock#is_valid, for SB: %s" % self.__dict__) # it's a string from the DB... addresses = self.payment_addresses.split('|') for addr in addresses: if not genesislib.is_valid_genesis_address(addr, config.network): printdbg("\tInvalid address [%s], returning False" % addr) return False amounts = self.payment_amounts.split('|') for amt in amounts: if not misc.is_numeric(amt): printdbg("\tAmount [%s] is not numeric, returning False" % amt) return False # no negative or zero amounts allowed damt = decimal.Decimal(amt) if not damt > 0: printdbg("\tAmount [%s] is zero or negative, returning False" % damt) return False # verify proposal hashes correctly formatted... if len(self.proposal_hashes) > 0: hashes = self.proposal_hashes.split('|') for object_hash in hashes: if not misc.is_hash(object_hash): printdbg("\tInvalid proposal hash [%s], returning False" % object_hash) return False # ensure number of payment addresses matches number of payments if len(addresses) != len(amounts): printdbg("\tNumber of payment addresses [%s] != number of payment amounts [%s], returning False" % (len(addresses), len(amounts))) return False printdbg("Leaving GovernanceBlock#is_valid, Valid = True") return True def hash(self): import genesislib return genesislib.hashit(self.serialise()) def hex_hash(self): return "%x" % self.hash() # workaround for now, b/c we must uniquely ID a governanceblock with the hash, # in case of differing governanceblocks # # this prevents sb_hash from being added to the serialised fields @classmethod def serialisable_fields(self): return [ 'event_block_height', 'payment_addresses', 'payment_amounts', 'proposal_hashes' ] # has this masternode voted to fund *any* governanceblocks at the given # event_block_height? @classmethod def is_voted_funding(self, ebh): count = (self.select() .where(self.event_block_height == ebh) .join(GovernanceObject) .join(Vote) .join(Signal) .switch(Vote) # switch join query context back to Vote .join(Outcome) .where(Vote.signal == VoteSignals.funding) .where(Vote.outcome == VoteOutcomes.yes) .count()) return count @classmethod def latest(self): try: obj = self.select().order_by(self.event_block_height).desc().limit(1)[0] except IndexError as e: obj = None return obj @classmethod def at_height(self, ebh): query = (self.select().where(self.event_block_height == ebh)) return query @classmethod def find_highest_deterministic(self, sb_hash): # highest block hash wins query = (self.select() .where(self.sb_hash == sb_hash) .order_by(self.object_hash.desc())) try: obj = query.limit(1)[0] except IndexError as e: obj = None return obj
class User(BaseModel): user_id = BigIntegerField(primary_key=True) username = TextField() discriminator = SmallIntegerField() avatar = TextField(null=True) bot = BooleanField() created_at = DateTimeField(default=datetime.utcnow) admin = BooleanField(default=False) SQL = ''' CREATE INDEX IF NOT EXISTS users_username_trgm ON users USING gin(username gin_trgm_ops); ''' class Meta: db_table = 'users' indexes = ((('user_id', 'username', 'discriminator'), True), ) @property def id(self): return self.user_id @classmethod def ensure(cls, user, should_update=True): return cls.from_disco_user(user) @classmethod def with_id(cls, uid): try: return User.get(user_id=uid) except User.DoesNotExist: return @classmethod def from_disco_user(cls, user, should_update=True): # DEPRECATED obj, _ = cls.get_or_create(user_id=user.id, defaults={ 'username': user.username, 'discriminator': user.discriminator, 'avatar': user.avatar, 'bot': user.bot }) if should_update: updates = {} if obj.username != user.username: updates['username'] = user.username if obj.discriminator != user.discriminator: updates['discriminator'] = user.discriminator if obj.avatar != user.avatar: updates['avatar'] = user.avatar if updates: cls.update(**updates).where(User.user_id == user.id).execute() return obj def get_avatar_url(self, fmt='webp', size=1024): if not self.avatar: return None return 'https://cdn.discordapp.com/avatars/{}/{}.{}?size={}'.format( self.user_id, self.avatar, fmt, size) def __unicode__(self): return u'{}#{}'.format(self.username, str(self.discriminator).zfill(4))
class Grupo(BaseModel): nome = TextField() dona = ForeignKeyField(Pessoa, backref='grupos')
class Article(BaseModel): """ 文章 """ class Meta: db_table = 'article' order_by = ("-created", "-id") MARK_DELETE = -1 DRAFT = 1 PUBLISHED = 2 STATE = ( (MARK_DELETE, "删除"), (DRAFT, "草稿"), (PUBLISHED, "已发布") ) team_id = IntegerField(verbose_name="俱乐部", help_text="俱乐部 Team.id", default=0) cover_key = CharField(default="", max_length=128) title = CharField(max_length=255, verbose_name="标题") summary = TextField(verbose_name="摘要") text = TextField(verbose_name="内容") approved = BooleanField(default=False, verbose_name="是否通过审核") recommend = BooleanField(default=False, verbose_name="是否推荐") state = IntegerField(default=1, verbose_name="状态") category = IntegerField(default=0, verbose_name="分类") views_count = IntegerField(default=0, verbose_name="浏览量") created_by = IntegerField(default=0) last_updated_by = IntegerField(default=0) created = DateTimeField(default=datetime.now) updated = DateTimeField(default=datetime.now) @property def info(self): if not hasattr(self, "_info"): self._info = self.to_dict(exclude=[Article.cover_key]) cover_url = app.settings["attach_url"] self._info["cover"] = Article.get_cover_urls(self.cover_key, cover_url=cover_url) return self._info @property def public_info(self): if not hasattr(self, "_public_info"): self._public_info = self.to_dict( exclude=[Article.cover_key] ) cover_url = app.settings["attach_url"] self._public_info["cover"] = \ Article.get_cover_urls(self.cover_key, cover_url=cover_url) return self._public_info @property def list_info(self): if not hasattr(self, "_list_info"): self._list_info = self.to_dict( exclude=[Article.cover_key, Article.text] ) cover_url = app.settings["attach_url"] self._list_info["cover"] = \ Article.get_cover_urls(self.cover_key, cover_url=cover_url) return self._list_info
class Verb(ProxyModel): ''' A lemmatized verb that found in programming documentation. ''' verb = TextField(index=True)
class SnippetPattern(ProxyModel): ''' A regular expression pattern describing a rule for detecting a snippet. ''' pattern = TextField(index=True)
class Message(BaseModel): message = TextField()
class WalletGroupAddressMerge(BaseModel): wallet = UUIDField(index=True) address = TextField(index=True, unique=True)
class Text(BaseModel): # http://dublincore.org/usage/meetings/2002/05/citdcsv.html corpus = TextField(index=True) identifier = TextField(index=True) url = TextField(null=True) # Book + article: title = TextField() surname = TextField() authors = ArrayField(TextField) publisher = TextField(null=True) date = TextField(null=True) # Article: journal_title = TextField(null=True) journal_identifier = TextField(null=True) issue_volume = TextField(null=True) issue_number = TextField(null=True) issue_chronology = TextField(null=True) pagination = TextField(null=True) # Validation: valid = BooleanField(null=True, index=True) display = BooleanField(null=True, index=True) class Meta: database = config.get_table_db('text') indexes = ((('corpus', 'identifier'), True),) @classmethod def ingest_hlom(cls): """ Ingest HLOM MARC records. """ corpus = HLOM_Corpus.from_env() for i, text in enumerate(corpus.texts()): try: cls.create(**text) except Exception as e: print(e) sys.stdout.write('\r'+str(i)) sys.stdout.flush() @classmethod def ingest_jstor(cls): """ Ingest JSTOR records. """ corpus = JSTOR_Corpus.from_env() for i, text in enumerate(corpus.texts()): try: cls.create(**text) except Exception as e: print(e) sys.stdout.write('\r'+str(i)) sys.stdout.flush() @classmethod def select_cited(cls): """ Select texts with at least one citation. Returns: peewee.SelectQuery """ from . import Citation return ( cls .select() .join(Citation) .order_by(cls.id) .group_by(cls.id) ) @classmethod def deduplicate(cls): """ Deduplicate cited texts. """ for text in query_bar(cls.select_cited()): # Has the hash been seen? seen = config.redis.sismember( redis_keys.OSP_DEDUP, text.hash, ) # If so, don't display this text. if seen: text.display = False else: # If not, display this text. text.display = True # And reserve the hash. config.redis.sadd( redis_keys.OSP_DEDUP, text.hash, ) text.save() @classmethod def validate(cls, *args, **kwargs): """ Validate all cited texts. """ config = Validate_Config(*args, **kwargs) for text in query_bar(cls.select_cited()): text.valid = not ( # Title text.title_and_author_overlap or text.title_blacklisted(config.blacklisted_titles) or text.title_is_toponym or # Surname text.surname_blacklisted(config.blacklisted_surnames) or text.surname_is_toponym or # Focus text.unfocused(config.max_fuzz, config.whitelist) ) text.save() @property def title_tokens(self): """ Tokenize the title. Returns: list """ return tokenize_field(self.title) @property def first_author_tokens(self): """ Tokenize the first author. Returns: list """ return tokenize_field(self.authors[0]) @property def surname_tokens(self): """ Tokenize the surname. Returns: list """ return tokenize_field(self.surname) @property def hash_tokens(self): """ Generate a sequence of tokens from the surname and title that forms a "signature" for the text. Returns: list: The hashing tokens. """ # Sort the surname tokens. return sorted(self.surname_tokens) + self.title_tokens @property def hash(self): """ SHA1 the hash tokens. Returns: str: The deduping hash. """ # Hash the tokens. sha1 = hashlib.sha1() sha1.update(' '.join(self.hash_tokens).encode('ascii', 'ignore')) return sha1.hexdigest() @property def queries(self): """ Build a set of Elasticsearch query strings. Returns: list: The set of queries. """ return [ # <author> <title> self.surname_tokens + self.title_tokens, # <title> <author> self.title_tokens + self.surname_tokens, ] def pretty(self, field): """ Prettify a field. Args: field (str) Returns: str|list """ value = getattr(self, field) if not value: return None elif type(value) is list: return [prettify(v) for v in value] else: return prettify(value) @property def fuzz(self): """ Compute an arbitrarily-scaled "fuzziness" score for the query tokens, where low is focused and high is fuzzy. Returns: float """ freqs = [ word_frequency(t, 'en', minimum=1e-6) for t in self.hash_tokens ] return reduce(lambda x, y: x*y, freqs)*1e10 @property def title_and_author_overlap(self): """ Do the title and first author have any tokens in common? Returns: bool """ title = set(self.title_tokens) author = set(self.first_author_tokens) return bool(title.intersection(author)) def title_blacklisted(self, blacklist=[]): """ Is the title blacklisted? Args: blacklist (list) Returns: bool """ return self.title_tokens in blacklist def surname_blacklisted(self, blacklist=[]): """ Is the surname blacklisted? Args: blacklist (list) Returns: bool """ return self.surname_tokens in blacklist @property def title_is_toponym(self): """ Is the title the name of a country or US state? Returns: bool """ title = ' '.join(self.title_tokens) return is_toponym(title) @property def surname_is_toponym(self): """ Is the surname the name of a country or US state? Returns: bool """ surname = ' '.join(self.surname_tokens) return is_toponym(surname) def unfocused(self, max_fuzz=float('inf'), whitelist=[]): """ Are the title / surname tokens too "fuzzy" for inclusion? Args: max_fuzz (float) whitelisted_ids (list) Returns: bool """ return self.fuzz > max_fuzz and self.id not in whitelist
class ScraperGenreMovies(TableBase): """Library Base Genre Movies""" id = IntegerField(primary_key=True) name = TextField()
class Location(BaseModel): room = ForeignKeyField(Room, backref="locations", on_delete="CASCADE") name = TextField() options = ForeignKeyField(LocationOptions, on_delete="CASCADE", null=True) index = IntegerField() archived = BooleanField(default=False) def __repr__(self): return f"<Location {self.get_path()}>" def get_path(self): return f"{self.room.get_path()}/{self.name}" def as_dict(self): data = model_to_dict( self, backrefs=False, recurse=False, exclude=[Location.room, Location.index, Location.options], ) if self.options is not None: data["options"] = self.options.as_dict() else: data["options"] = {} return data def create_floor(self, name="ground"): if Floor.select().where(Floor.location == self).count() > 0: index = ( Floor.select(fn.Max(Floor.index)).where(Floor.location == self).scalar() + 1 ) else: index = 0 floor = Floor.create(location=self, name=name, index=index) Layer.create( location=self, name="map", type_="normal", player_visible=True, index=0, floor=floor, ) Layer.create( location=self, name="grid", type_="grid", selectable=False, player_visible=True, index=1, floor=floor, ) Layer.create( location=self, name="tokens", type_="normal", player_visible=True, player_editable=True, index=2, floor=floor, ) Layer.create(location=self, type_="normal", name="dm", index=3, floor=floor) Layer.create( location=self, type_="fow", name="fow", player_visible=True, index=4, floor=floor, ) Layer.create( location=self, name="fow-players", type_="fow-players", selectable=False, player_visible=True, index=5, floor=floor, ) Layer.create( location=self, name="draw", type_="normal", selectable=False, player_visible=True, player_editable=True, index=6, floor=floor, ) return floor
class Order_info(Base): sn = TextField(unique=True) date = DateTimeField() user = ForeignKeyField(User, backref='orders') final_money = IntegerField()
class Guild(BaseModel): WhitelistFlags = Enum('MUSIC', 'MODLOG_CUSTOM_FORMAT', bitmask=False) guild_id = BigIntegerField(primary_key=True) owner_id = BigIntegerField(null=True) name = TextField(null=True) icon = TextField(null=True) splash = TextField(null=True) region = TextField(null=True) last_ban_sync = DateTimeField(null=True) # Rowboat specific data config = BinaryJSONField(null=True) config_raw = BlobField(null=True) enabled = BooleanField(default=True) whitelist = BinaryJSONField(default=[]) added_at = DateTimeField(default=datetime.utcnow) # SQL = ''' # CREATE OR REPLACE FUNCTION shard (int, bigint) # RETURNS bigint AS $$ # SELECT ($2 >> 22) % $1 # $$ LANGUAGE SQL; # ''' class Meta: db_table = 'guilds' @classmethod def with_id(cls, guild_id): return cls.get(guild_id=guild_id) @classmethod def setup(cls, guild): return cls.create(guild_id=guild.id, owner_id=guild.owner_id, name=guild.name, icon=guild.icon, splash=guild.splash, region=guild.region, config={'web': { guild.owner_id: 'admin' }}, config_raw='') def is_whitelisted(self, flag): return int(flag) in self.whitelist def update_config(self, actor_id, raw): from rowboat.types.guild import GuildConfig parsed = yaml.load(raw) GuildConfig(parsed).validate() GuildConfigChange.create(user_id=actor_id, guild_id=self.guild_id, before_raw=self.config_raw, after_raw=raw) self.update( config=parsed, config_raw=raw).where(Guild.guild_id == self.guild_id).execute() self.emit('GUILD_UPDATE') def emit(self, action, **kwargs): emit(action, id=self.guild_id, **kwargs) def sync(self, guild): updates = {} for key in ['owner_id', 'name', 'icon', 'splash', 'region']: if getattr(guild, key) != getattr(self, key): updates[key] = getattr(guild, key) if updates: Guild.update(**updates).where( Guild.guild_id == self.guild_id).execute() def get_config(self, refresh=False): from rowboat.types.guild import GuildConfig if refresh: self.config = Guild.select(Guild.config).where( Guild.guild_id == self.guild_id).get().config if refresh or not hasattr(self, '_cached_config'): try: self._cached_config = GuildConfig(self.config) except: log.exception('Failed to load config for Guild %s, invalid: ', self.guild_id) return None return self._cached_config def sync_bans(self, guild): # Update last synced time Guild.update(last_ban_sync=datetime.utcnow()).where( Guild.guild_id == self.guild_id).execute() try: bans = guild.get_bans() except: log.exception('sync_bans failed:') return log.info('Syncing %s bans for guild %s', len(bans), guild.id) GuildBan.delete().where((~(GuildBan.user_id << list(bans.keys()))) & (GuildBan.guild_id == guild.id)).execute() for ban in bans.values(): GuildBan.ensure(guild, ban.user, ban.reason) def serialize(self): base = { 'id': str(self.guild_id), 'owner_id': str(self.owner_id), 'name': self.name, 'icon': self.icon, 'splash': self.splash, 'region': self.region, 'enabled': self.enabled, 'whitelist': self.whitelist } if hasattr(self, 'role'): base['role'] = self.role return base
class WebPageContent(ProxyModel): ''' The contents at a web URL at a point in time. ''' date = DateTimeField(index=True, default=datetime.datetime.now) url = TextField(index=True) content = TextField()
class AddressChanges(BaseModel): address = TextField(index=True) balance_change = BigIntegerField() sent_change = BigIntegerField() received_change = BigIntegerField()
class PostNpmInstallPackage(ProxyModel): ''' A package referenced in an 'npm install' command in a Stack Overflow post. ''' compute_index = IntegerField(index=True) date = DateTimeField(index=True, default=datetime.datetime.now) post = ForeignKeyField(Post) package = TextField()
class Medic(Model): academic = TextField() city = TextField() name = TextField() specialty = TextField() title = TextField()
class Noun(ProxyModel): ''' A lemmatized noun that found in programming documentation. ''' noun = TextField(index=True)
class Reviews(BaseModel): id = AutoField() album_id = IntegerField() reader_id = IntegerField() rating = IntegerField() details = TextField()
class Pessoa(BaseModel): nome = TextField() email = TextField(unique=True) senha = TextField() idade = IntegerField()
class usuario(basemodel): nome = TextField(unique=True) vitoria = IntegerField() derrota = IntegerField()
class Message(BaseModel): id = IntegerField(primary_key=True, sequence=True) content = TextField() time = TextField()
class criapalavra(basemodel): jogador = ForeignKeyField(usuario, backref='usuarios') palavrasecreta = TextField() dica = TextField() datacriacao = DateTimeField(default='datetime.now()')
class Infraction(BaseModel): Types = Enum( 'MUTE', 'KICK', 'TEMPBAN', 'SOFTBAN', 'BAN', 'TEMPMUTE', 'UNBAN', bitmask=False, ) guild_id = BigIntegerField() user_id = BigIntegerField() actor_id = BigIntegerField(null=True) type_ = IntegerField(db_column='type') reason = TextField(null=True) metadata = BinaryJSONField(default={}) expires_at = DateTimeField(null=True) created_at = DateTimeField(default=datetime.utcnow) active = BooleanField(default=True) class Meta: db_table = 'infractions' indexes = ((('guild_id', 'user_id'), False), ) @staticmethod def admin_config(event): return getattr(event.base_config.plugins, 'admin', None) @classmethod def kick(cls, plugin, event, member, reason): User.from_disco_user(member.user) plugin.bot.plugins.get('ModLogPlugin').create_debounce( event, member.user.id, 'kick', actor=unicode(event.author) if event.author.id != member.id else 'Automatic', reason=reason or 'no reason') member.kick() cls.create(guild_id=member.guild_id, user_id=member.user.id, actor_id=event.author.id, type_=cls.Types.KICK, reason=reason) @classmethod def tempban(cls, plugin, event, member, reason, expires_at): User.from_disco_user(member.user) plugin.bot.plugins.get('ModLogPlugin').create_debounce( event, member.user.id, 'ban_reason', actor=unicode(event.author) if event.author.id != member.id else 'Automatic', temp=True, expires=expires_at, reason=reason or 'no reason') member.ban() cls.create(guild_id=member.guild_id, user_id=member.user.id, actor_id=event.author.id, type_=cls.Types.TEMPBAN, reason=reason, expires_at=expires_at) @classmethod def softban(cls, plugin, event, member, reason): User.from_disco_user(member.user) plugin.bot.plugins.get('ModLogPlugin').create_debounce( event, member.user.id, 'ban_reason', actor=unicode(event.author) if event.author.id != member.id else 'Automatic', temp=True, expires=None, reason=reason or 'no reason') member.ban(delete_message_days=7) member.unban() cls.create(guild_id=member.guild_id, user_id=member.user.id, actor_id=event.author.id, type_=cls.Types.SOFTBAN, reason=reason) @classmethod def ban(cls, plugin, event, member, reason, guild): if isinstance(member, (int, long)): user_id = member else: User.from_disco_user(member.user) user_id = member.user.id plugin.bot.plugins.get('ModLogPlugin').create_debounce( event, user_id, 'ban_reason', actor=unicode(event.author) if event.author.id != user_id else 'Automatic', temp=False, expires=None, reason=reason or 'no reason') guild.create_ban(user_id) cls.create(guild_id=guild.id, user_id=user_id, actor_id=event.author.id, type_=cls.Types.BAN, reason=reason) @classmethod def mute(cls, plugin, event, member, reason): admin_config = cls.admin_config(event) plugin.bot.plugins.get('ModLogPlugin').create_debounce( event, member.user.id, 'muted', reason=reason, expires_at=None, actor=unicode(event.author) if event.author.id != member.id else 'Automatic', role=admin_config.mute_role) member.add_role(admin_config.mute_role) cls.create(guild_id=event.guild.id, user_id=member.user.id, actor_id=event.author.id, type_=cls.Types.MUTE, reason=reason, metadata={'role': admin_config.mute_role}) @classmethod def tempmute(cls, plugin, event, member, reason, expires_at): admin_config = cls.admin_config(event) plugin.bot.plugins.get('ModLogPlugin').create_debounce( event, member.user.id, 'muted', reason=reason, expires_at=expires_at, actor=unicode(event.author) if event.author.id != member.id else 'Automatic', role=admin_config.mute_role) member.add_role(admin_config.mute_role) cls.create(guild_id=event.guild.id, user_id=member.user.id, actor_id=event.author.id, type_=cls.Types.TEMPMUTE, reason=reason, expires_at=expires_at, metadata={'role': admin_config.mute_role}) @classmethod def clear_active(cls, event, user_id, types): """ Marks a previously active tempmute as inactive for the given event/user. This should be used in all locations where we either think this is no longer active (e.g. the mute role was removed) _or_ when we don't want to unmute the user any longer, e.g. they've been remuted by another command. """ return cls.update(active=False).where( (cls.guild_id == event.guild.id) & (cls.user_id == user_id) & (cls.type_ << types) & (cls.active == 1)).execute() >= 1
class chutejog(basemodel): jogador02 = ForeignKeyField(usuario, backref='usuarios') letracitada = TextField() tentativa = IntegerField()
class User(BaseModel): uid = IntegerField(primary_key=True) admin = BooleanField(default=False) bboxes = TextField(null=True)
class Linux_RpmPackages(RpmPackages): pid_with_namespace = IntegerField(help_text="Pids that contain a namespace") # {'additional': True, 'hidden': True} mount_namespace_id = TextField(help_text="Mount namespace id") # {'hidden': True} class Meta: table_name = "rpm_packages"
class Constants(BaseModel): save_version = IntegerField() secret_token = BlobField() api_token = TextField()
class Solution(BaseModel): STATES = SolutionState MAX_CHECK_TIME_SECONDS = 60 * 10 exercise = ForeignKeyField(Exercise, backref='solutions') solver = ForeignKeyField(User, backref='solutions') checker = ForeignKeyField(User, null=True, backref='solutions') state = CharField( choices=STATES.to_choices(), default=STATES.CREATED.name, index=True, ) grade = IntegerField( default=0, constraints=[Check('grade <= 100'), Check('grade >= 0')], ) submission_timestamp = DateTimeField(index=True) hashed = TextField() @property def solution_files( self, ) -> Union[Iterable['SolutionFile'], 'SolutionFile']: return SolutionFile.filter(SolutionFile.solution == self) @property def is_checked(self): return self.state == self.STATES.DONE.name @staticmethod def create_hash(content: Union[str, bytes], *args, **kwargs) -> str: return hashing.by_content(content, *args, **kwargs) @classmethod def is_duplicate( cls, content: Union[str, bytes], user: User, *, already_hashed: bool = False, ) -> bool: hash_ = cls.create_hash(content) if not already_hashed else content return cls.select().where( cls.hashed == hash_, cls.solver == user, ).exists() def start_checking(self) -> bool: return self.set_state(Solution.STATES.IN_CHECKING) def set_state(self, new_state: SolutionState, **kwargs) -> bool: # Optional: filter the old state of the object # to make sure that no two processes set the state together requested_solution = (Solution.id == self.id) changes = Solution.update( **{ Solution.state.name: new_state.name }, **kwargs, ).where(requested_solution) updated = changes.execute() == 1 return updated def ordered_versions(self) -> Iterable['Solution']: return Solution.select().where( Solution.exercise == self.exercise, Solution.solver == self.solver, ).order_by(Solution.submission_timestamp.asc()) def test_results(self) -> Iterable[dict]: return SolutionExerciseTestExecution.by_solution(self) @classmethod def of_user( cls, user_id: int, with_archived: bool = False, ) -> Iterable[Dict[str, Any]]: db_exercises = Exercise.get_objects(fetch_archived=with_archived) exercises = Exercise.as_dicts(db_exercises) solutions = (cls.select(cls.exercise, cls.id, cls.state, cls.checker).where( cls.exercise.in_(db_exercises), cls.solver == user_id).order_by( cls.submission_timestamp.desc())) for solution in solutions: exercise = exercises[solution.exercise_id] if exercise.get('solution_id') is None: exercise['solution_id'] = solution.id exercise['is_checked'] = solution.is_checked exercise['comments_num'] = len(solution.comments) if solution.is_checked and solution.checker: exercise['checker'] = solution.checker.fullname return tuple(exercises.values()) @property def comments(self): return Comment.select().join( SolutionFile, ).where(SolutionFile.solution == self) @property def comments_per_file(self): return Counter(c.file.id for c in self.comments) @classmethod def create_solution( cls, exercise: Exercise, solver: User, files: List['File'], hash_: Optional[str] = None, ) -> 'Solution': if len(files) == 1: hash_ = cls.create_hash(files[0].code) if hash_ and cls.is_duplicate(hash_, solver, already_hashed=True): raise AlreadyExists('This solution already exists.') instance = cls.create( **{ cls.exercise.name: exercise, cls.solver.name: solver, cls.submission_timestamp.name: datetime.now(), cls.hashed.name: hash_, }) files_details = [{ SolutionFile.path.name: f.path, SolutionFile.solution_id.name: instance.id, SolutionFile.code.name: f.code, SolutionFile.file_hash.name: SolutionFile.create_hash(f.code), } for f in files] SolutionFile.insert_many(files_details).execute() # update old solutions for this exercise other_solutions: Iterable[Solution] = cls.select().where( cls.exercise == exercise, cls.solver == solver, cls.id != instance.id, ) for old_solution in other_solutions: old_solution.set_state(Solution.STATES.OLD_SOLUTION) return instance @classmethod def _base_next_unchecked(cls): comments_count = fn.Count(Comment.id).alias('comments_count') fails = fn.Count(SolutionExerciseTestExecution.id).alias('failures') return cls.select( cls.id, cls.state, cls.exercise, comments_count, fails, ).join( SolutionFile, join_type=JOIN.LEFT_OUTER, on=(SolutionFile.solution == cls.id), ).join( Comment, join_type=JOIN.LEFT_OUTER, on=(Comment.file == SolutionFile.id), ).join( SolutionExerciseTestExecution, join_type=JOIN.LEFT_OUTER, on=(SolutionExerciseTestExecution.solution == cls.id), ).where(cls.state == Solution.STATES.CREATED.name, ).group_by( cls.id, ).order_by( comments_count, fails, cls.submission_timestamp.asc(), ) def mark_as_checked( self, by: Optional[Union[User, int]] = None, ) -> bool: return self.set_state( Solution.STATES.DONE, checker=by, ) @classmethod def next_unchecked(cls) -> Optional['Solution']: try: return cls._base_next_unchecked().get() except cls.DoesNotExist: return None @classmethod def next_unchecked_of(cls, exercise_id) -> Optional['Solution']: try: return cls._base_next_unchecked().where( cls.exercise == exercise_id, ).get() except cls.DoesNotExist: return None @classmethod def status(cls): one_if_is_checked = Case( Solution.state, ((Solution.STATES.DONE.name, 1), ), 0, ) fields = [ Exercise.id, Exercise.subject.alias('name'), Exercise.is_archived.alias('is_archived'), fn.Count(Solution.id).alias('submitted'), fn.Sum(one_if_is_checked).alias('checked'), ] join_by_exercise = (Solution.exercise == Exercise.id) active_solutions = Solution.state.in_( Solution.STATES.active_solutions(), ) return (Exercise.select(*fields).join( Solution, JOIN.LEFT_OUTER, on=join_by_exercise).where(active_solutions).group_by( Exercise.subject, Exercise.id).order_by(Exercise.id)) @classmethod def left_in_exercise(cls, exercise: Exercise) -> int: one_if_is_checked = Case(Solution.state, ((Solution.STATES.DONE.name, 1), ), 0) active_solutions = cls.state.in_(Solution.STATES.active_solutions()) response = cls.filter( cls.exercise == exercise, active_solutions, ).select( fn.Count(cls.id).alias('submitted'), fn.Sum(one_if_is_checked).alias('checked'), ).dicts().get() return int(response['checked'] * 100 / response['submitted'])