class Comment(Model): id = IntField(pk=True) dataset = ForeignKeyField('models.Dataset', related_name='comments') author = TextField() time_added = IntField() # ms since epoch text = TextField()
class Post(Model): id = IntField(pk=True) created_at = DatetimeField(auto_now_add=True) modified_at = DatetimeField(auto_now=True) title = CharField(max_length=32) content = TextField(null=True) image_url = TextField() author: ForeignKeyRelation[User] = ForeignKeyField("models.User", "posts") comments = ReverseRelation["Comment"] class Meta: table = "posts" ordering = ["created_at"] class PydanticMeta: exclude = ("author", "comments") def __repr__(self): return ( f"Post({self.id=}, {self.created_at=}, {self.modified_at=}, " f"{self.title=}, {self.content=}, {self.image_url}, {self.author=})" ) def __str__(self): return self.__repr__()
class SubExclude(Model): """ Exclusion for a trigger in a specific channel. Attributes: trigger (.SubTrigger): Containing trigger instance. network (str): Network identifier that the channel belongs to. user (str): Channel's own identifier. """ trigger = ForeignKeyField("db.SubTrigger", "excludes") network = TextField() channel = TextField() @classmethod def select_related(cls): return cls.all().prefetch_related("trigger") def __repr__(self): if isinstance(self.trigger, SubTrigger): trigger = repr(self.trigger) else: trigger = "<{}: #{}>".format(SubTrigger.__name__, self.trigger_id) return "<{}: #{} {} @ {} {}>".format(self.__class__.__name__, self.id, repr(self.network), repr(self.channel), trigger)
class Answer(Model): id: UUIDField = UUIDField(pk=True) text: TextField = TextField() formatted_text: TextField = TextField() questions: ReverseRelation["Question"] def __str__(self) -> str: return f"{self.id} ({self.text[:120]}{'...' if len(self.text) > 120 else ''})"
class Question(Model): id: UUIDField = UUIDField(pk=True) text: TextField = TextField() formatted_text: TextField = TextField() answer: ForeignKeyNullableRelation["Answer"] = ForeignKeyField( model_name="models.Answer", related_name="questions") def __str__(self) -> str: return f"{self.id} ({self.text[:120]}{'...' if len(self.text) > 120 else ''})"
class Article(Model): uid = IntField(pk=True) title = CharField(max_length=120) slug = CharField(max_length=150, unique=True) intro = TextField() content = TextField() html = TextField() class Meta: table = "articles"
def make_listing_model(name, filter_specs): models = [] listing_name = f'l_{name}' listing_model_name = underscore_to_camelCase(listing_name) def generate_relation(fname): rel_name = f'{listing_name}_{fname}' rel_model_name = underscore_to_camelCase(rel_name) assert '_' not in rel_model_name, rel_model_name model = type( rel_model_name, (Model, ), { 'Meta': type('Meta', (), {'table': rel_name}), 'id': IntField(pk=True), 'value': CharField(max_length=255, index=True, unique=True), '__repr__': lambda self: f'<{type(self).__name__} {self.id} {self.value!r}>', }) models.append(model) return ManyToManyField(f'models.{rel_model_name}', related_name='listing') coltype_factories = { # All dates and times in ms (since epoch) 'datetime': lambda name: IntField(null=True), 'filesize': lambda name: IntField(null=True), 'float': lambda name: FloatField(null=True), 'int': lambda name: IntField(null=True), 'string': lambda name: TextField(null=True), 'string[]': generate_relation, 'subset': generate_relation, 'timedelta': lambda name: IntField(null=True), 'words': lambda name: TextField(null=True), } dct = { 'Meta': type('Meta', (), {'table': listing_name}), 'id': IntField(pk=True), 'row': TextField(null=True), '__repr__': lambda self: f'<{type(self).__name__} {self.dataset_id}>', } dct.update( (fspec.name, coltype_factories[fspec.value_type](fspec.name)) for fspec in filter_specs.values() if fspec.name not in ('row', 'f_comments', 'f_status', 'f_tags')) models.append(type(listing_model_name, (Model, ), dct)) return models
class Journal(Model): id = IntField(pk=True, index=True) user: ForeignKeyRelation[User] = ForeignKeyField("models.User", related_name="journals", on_delete=CASCADE) name = TextField(null=False) name_lower = TextField(null=False) # YYYY-MM-DD or None deleted_on = DateField(null=True) entries: ReverseRelation["Entry"]
class Entry(Model): id = IntField(pk=True, index=True) journal: ForeignKeyRelation[Journal] = ForeignKeyField( "models.Journal", related_name="entries", on_delete=CASCADE) short = TextField(null=False) long = TextField(null=False) # YYYY-MM-DD HH:MM format in UTC timezone date = DatetimeField(null=False) # YYYY-MM-DD or None deleted_on = DateField(null=True) keywords: ReverseRelation["Keyword"]
class Dataset(Model): id = IntField(pk=True) collection = ForeignKeyField('models.Collection', related_name='datasets') discarded = BooleanField(default=False) name = TextField() status = IntField(default=0) time_added = IntField() # ms since epoch timestamp = IntField() # ms since epoch setid = custom.SetIDField(unique=True) tags = ManyToManyField('models.Tag', related_name='datasets') acn = ForeignKeyField('models.Acn', related_name='datasets', on_delete=RESTRICT) # Populated by backreferences: comments, files error = make_status_property(1) missing = make_status_property(2) outdated = make_status_property(4) pending = make_status_property(8) def __repr__(self): return f'<{type(self).__name__} {self.setid} {self.name}>'
class IdentityGroup(Model): """ Representation of a single identity. Attributes: instance (int): :class:`IdentityHook` instance code. name (str): Unique display name. pwd (str): Hashed password, used by the user to authenticate when linking identities. links (.IdentityLink iterable): All links contained by this group. """ instance = IntField() name = TextField() pwd = TextField() class Meta: # Uniqueness constraint for each name in each identity instance. unique_together = (("instance", "name"),) @classmethod def hash(cls, pwd): return sha256(pwd.encode("utf-8")).hexdigest() @classmethod def select_related(cls): return cls.all().prefetch_related("links", "roles") async def to_identity(self, host, provider): tasks = [] plugs = {plug.network_id: plug for plug in host.plugs.values() if plug.state == immp.OpenState.active} for link in self.links: if link.network in plugs: tasks.append(plugs[link.network].user_from_id(link.user)) else: log.debug("Ignoring identity link for unavailable plug: %r", link) users = await Identity.gather(*tasks) roles = [role.role for role in self.roles] return Identity(self.name, provider, users, roles) def __repr__(self): return "<{}: #{} {}>".format(self.__class__.__name__, self.id, repr(self.name))
class Moderator(Model): id = IntField(pk=True) user: ForeignKeyRelation[User] = ForeignKeyField( "models.User", related_name="moderator_on") guild: ForeignKeyRelation[Guild] = ForeignKeyField( "models.Guild", related_name="moderators") title = TextField(null=True) class Meta: table = "moderators"
class Log(BaseModel): email_id: ManyToManyRelation['Email'] = ManyToManyField( "models.Email", related_name='emails') date = DateTimeField(index=True, default=datetime.datetime.now()) exception_type = CharField(null=True) message = TextField() status = IntField(index=True, null=True) class Meta: table = 'data_log'
class SatelliteDatasets(Model): id = IntField(pk=True) satellite_name = CharField(max_length=50) dataset_path = TextField() dataset_type = CharField(max_length=50) # class PydanticMeta: # exclude = ['dataset_path'] # user: ForeignKeyRelation[Users] = ForeignKeyField('models.Users', related_name='satellite_dataset') data_visualization: ReverseRelation['DataVisualization']
class File(Model): id = IntField(pk=True) dataset = ForeignKeyField('models.Dataset', related_name='files') idx = IntField() # files are counted per dataset missing = BooleanField(default=False) mtime = IntField() # ms since epoch path = TextField() size = IntField() def __repr__(self): return f"<{type(self).__name__} '{self.path}'>"
class SubTrigger(Model): """ Individual subscription trigger phrase for an individual user. Attributes: network (str): Network identifier that the user belongs to. user (str): User identifier as given by the plug. text (str): Subscription text that they wish to be notified on. """ network = TextField() user = TextField() text = TextField() def __repr__(self): return "<{}: #{} {} ({} @ {})>".format(self.__class__.__name__, self.id, repr(self.text), repr(self.user), repr(self.network))
class User(Model): id = UUIDField(pk=True, index=True) # 0 == Free, 10 == Premium tier = IntField(default=0) username = CharField(max_length=255, unique=True, index=True) hashed_password = TextField(null=False) encrypted = BooleanField(default=False) admin = BooleanField(default=False) journals: ReverseRelation["Journal"]
class ApiJob(Model): """API job model.""" requestor: ForeignKeyRelation[ApiUser] = ForeignKeyField( "models.ApiUser", related_name="jobs") request_time = DatetimeField(auto_now=True) complete_time = DatetimeField(null=True) in_progress = BooleanField(default=False) detail = TextField(null=True) class Meta: """Tortoise ORM Config.""" table = "api_jobs"
class Guild(Model): id = BigIntField(pk=True) prefix = TextField(null=True) mute_role = BigIntField(null=True) level_up_messages = BooleanField(default=True) moderators: ReverseRelation["Moderator"] mutes: ReverseRelation["Mute"] warns: ReverseRelation["Warn"] class Meta: table = "guilds" def __str__(self): return f"<Guild id:{self.id} prefix:{self.prefix} muterole:{self.mute_role}>"
class IdentityLink(Model): """ Single link between an identity and a user. Attributes: group (.IdentityGroup): Containing group instance. network (str): Network identifier that the user belongs to. user (str): User identifier as given by the plug. """ group = ForeignKeyField("db.IdentityGroup", "links") network = TextField() user = TextField() def __repr__(self): if isinstance(self.group, IdentityGroup): group = repr(self.group) else: group = "<{}: #{}>".format(IdentityGroup.__name__, self.group_id) return "<{}: #{} {} @ {} {}>".format(self.__class__.__name__, self.id, repr(self.user), repr(self.network), group)
class Warn(Model): id = IntField(pk=True) moderator = BigIntField() reason = TextField(null=True) when = DatetimeField(auto_now_add=True) user: ForeignKeyRelation[User] = ForeignKeyField("models.User", related_name="warns") guild: ForeignKeyRelation[Guild] = ForeignKeyField("models.Guild", related_name="warns") class Meta: table = "warns" def __str__(self): return (f"<Warn id:{self.id} moderator:{self.moderator} " f"reason:'{self.reason}' datetime:{self.when} " f"user:{self.user.id} guild:{self.guild.id}>")
class User(Model): id = IntField(pk=True) name = CharField(max_length=255, unique=True) password = TextField(null=True) given_name = TextField(null=True) family_name = TextField(null=True) email = TextField(null=True) realm = TextField() realmuid = TextField(null=True) time_created = DatetimeField(auto_now_add=True) time_updated = DatetimeField(auto_now=True) groups = ManyToManyField('models.Group', related_name='users')
class User(Model): id = IntField(pk=True) created_at = DatetimeField(auto_now_add=True) modified_at = DatetimeField(auto_now=True) username = CharField(max_length=32, unique=True) mail = CharField(max_length=64, null=True) password = CharField(max_length=64) bio = TextField(null=True) posts = ReverseRelation["Post"] comments = ReverseRelation["Comment"] class Meta: table = "users" class PydanticMeta: exclude = ("password", "access_token", "posts", "comments") def __repr__(self): return ( f"User({self.id=}, {self.created_at=}, {self.modified_at=}, " f"{self.username=}, {self.mail=}, {self.password=},{self.bio=})" ) def __str__(self): return self.__repr__() def verify_password(self, password: str) -> bool: return bcrypt.verify(password, self.password) def access_token(self) -> str: data = { "iat": datetime.utcnow(), "exp": datetime.utcnow() + timedelta(minutes=60), "sub": str(self.id), } token: str = jwt.encode( data, getenv("JWT_SECRET"), algorithm="HS256", ) return token
class Mute(Model): id = IntField(pk=True) moderator = BigIntField() reason = TextField(null=True) start = DatetimeField(auto_now_add=True) end = DatetimeField() active = BooleanField(default=True) user: ForeignKeyRelation[User] = ForeignKeyField("models.User", related_name="mutes") guild: ForeignKeyRelation[Guild] = ForeignKeyField("models.Guild", related_name="mutes") class Meta: table = "mutes" def __str__(self): return ( f"<Mute id:{self.id} moderator:{self.moderator} " f"reason:'{self.reason}' start:{self.start} end:{self.end} " f"active:{self.active} user:{self.user.id} guild:{self.guild.id}>")
class IdentityRole(Model): """ Assignment of a role to an identity. Attributes: group (.IdentityGroup): Containing group instance. role (str): Plain role identifier. """ group = ForeignKeyField("db.IdentityGroup", "roles") role = TextField() def __repr__(self): if isinstance(self.group, IdentityGroup): group = repr(self.group) else: group = "<{}: #{}>".format(IdentityGroup.__name__, self.group_id) return "<{}: #{} {} {}>".format(self.__class__.__name__, self.id, repr(self.role), group)
class Comment(Model): id = IntField(pk=True) created_at = DatetimeField(auto_now_add=True) modified_at = DatetimeField(auto_now=True) content = TextField() author: ForeignKeyRelation[User] = ForeignKeyField("models.User", "comments") post: ForeignKeyRelation[Post] = ForeignKeyField("models.Post", "comments") class Meta: table = "comments" ordering = ["created_at"] class PydanticMeta: exclude = ("author", "post") def __repr__(self): return ( f"Comment({self.id=}, {self.created_at=}, {self.modified_at=}, " f"{self.content=}, {self.author=}, {self.post=})" ) def __str__(self): return self.__repr__()
class Email(BaseModel): id = IntField(pk=True) from_email = CharField(255) to_email = TextField() bcc = TextField() cc = TextField() subject = CharField(255) context = TextField(null=True) created = DateTimeField(index=True, auto_now_add=True) headers = JSONField(null=False) html_message = TextField(null=True) last_updated = DateTimeField(index=True, default=datetime.datetime.now()) message = TextField() priority = IntField(null=True) scheduled_time = DateTimeField(index=True, null=True) status = IntField(index=True, null=True) class Meta: table = 'data_email'
class Note(Model): """ Representation of a single note. Attributes: timestamp (int): Creation time of the note. network (str): Network identifier for the channel's plug. channel (str): Channel identifier where the note was created. user (str): User identifier of the note's author. text (str): Note content. """ timestamp = IntField(default=lambda: int(time.time())) network = TextField() channel = TextField() user = TextField(null=True) text = TextField() @classmethod def select_channel(cls, channel): return (cls.filter(network=channel.plug.network_id, channel=channel.source).order_by("timestamp")) @classmethod async def select_position(cls, channel, num): if num < 1: raise ValueError note = await cls.select_channel(channel).offset(num - 1).first() if note: return note else: raise DoesNotExist @classmethod async def select_position_multi(cls, channel, *nums): if any(num < 1 for num in nums): raise ValueError notes = await cls.select_channel(channel) try: return [notes[num - 1] for num in nums] except IndexError: raise DoesNotExist from None @property def ago(self): diff = int(time.time()) - self.timestamp for step, unit in ((60, "s"), (60, "m"), (24, "h")): if diff < step: return "{}{}".format(diff, unit) diff //= step return "{}d".format(diff) def __repr__(self): return "<{}: #{} {} @ {}: {}>".format(self.__class__.__name__, self.id, self.ago, repr(self.channel), repr(self.text))
def test_pk_deprecated(self): with self.assertWarnsRegex( DeprecationWarning, "TextField as a PrimaryKey is Deprecated, use CharField"): TextField(pk=True)
def test_index_fail(self): with self.assertRaisesRegex(ConfigurationError, "can't be indexed, consider CharField"): TextField(index=True)