class File(db.Model): """ Usually an image, but could be anything. The purpose of this application is to tag the appearances of things in files, so this is the primary model. """ __tablename__ = 'file' id = db.Column(db.Integer, primary_key=True) sha256 = db.Column(db.String, nullable=False, unique=True) ext = db.Column(db.String, nullable=False) @property def filename(self): return "{}{}".format(self.sha256, self.ext) @property def tags(self): return [item.tag for item in self.tag_relationships] @property def tag_names(self): return [item.name for item in self.tags] @property def artist_names(self): return [item.thing.name for item in self.thing_roles if item.role.name == 'artist'] @property def recipient_names(self): return [item.thing.name for item in self.thing_roles if item.role.name == 'recipient']
class AppearanceTag(db.Model): """ This associates tags with Appearances of Things. Since a Thing already has tags, this also allows you to 'anti-tag' those things in specific appearances to prevent their usual tags from cascading in. """ __tablename__ = 'appearance_tag' appearance_id = db.Column('appearance_id', db.ForeignKey('appearance.id', ondelete='cascade'), primary_key=True) appearance = db.relationship('Appearance', backref=db.backref("taggings", passive_deletes='all')) tag_id = db.Column('tag_id', db.ForeignKey('tag.id', ondelete='cascade'), primary_key=True) tag = db.relationship('Tag', backref=db.backref("file_thing_relationships", passive_deletes='all')) negative = db.Column(db.Boolean, nullable=False, server_default='f')
class Thing(db.Model): """ Could be a person, character, or object. It's like a tag that can have its own tags attached to it. You associate it with a file using an Appearance. """ __tablename__ = 'thing' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String, unique=True, nullable=False) @property def files(self): return [item.file for item in self.file_relationships] @property def tags(self): return [item.tag for item in self.tag_relationships] @property def tag_name_list(self): return [item.name for item in self.tags] @property def tag_names(self): return ",".join(self.tag_name_list)
class FileTag(db.Model): __tablename__ = 'file_tag' file_id = db.Column('file_id', db.ForeignKey('file.id', ondelete='cascade'), primary_key=True) file = db.relationship('File', backref=db.backref("tag_relationships", passive_deletes='all')) tag_id = db.Column('tag_id', db.ForeignKey('tag.id', ondelete='cascade'), primary_key=True) tag = db.relationship('Tag', backref=db.backref("file_relationships", passive_deletes='all'))
class ThingRole(db.Model): __tablename__ = 'thing_role' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String, nullable=False, unique=True) @classmethod def by_name(cls): return {item.name: item for item in db.session.query(cls).all()}
class FileThingRole(db.Model): __tablename__ = 'file_thing_role' file_id = db.Column('file_id', db.ForeignKey('file.id', ondelete='cascade'), primary_key=True) file = db.relationship('File', backref=db.backref("thing_roles", passive_deletes='all')) thing_id = db.Column('thing_id', db.ForeignKey('thing.id', ondelete='cascade'), primary_key=True) thing = db.relationship('Thing', backref=db.backref('file_roles', passive_deletes='all')) role_id = db.Column(db.ForeignKey('thing_role.id'), primary_key=True) role = db.relationship('ThingRole')
class Comment(db.Model): __tablename__ = 'comment' id = db.Column(db.Integer, primary_key=True) text = db.Column(db.String, nullable=False) parent_id = db.Column(db.ForeignKey('comment.id', ondelete='cascade')) # in reply to parent = db.relationship('Comment', remote_side=[id], backref=db.backref('replies', passive_deletes='all')) # what it's related to. Only one may be populated file_id = db.Column(db.ForeignKey('file.id', ondelete='cascade'), nullable=False) # nullability will change created_at = db.Column(ArrowType, nullable=False, server_default=db.text("timezone('utc',now())")) file = db.relationship('File', backref=db.backref('comments', passive_deletes='all', order_by=created_at.desc()))
class ThingTag(db.Model): __tablename__ = 'thing_tag' tag_id = db.Column('tag_id', db.ForeignKey('tag.id', ondelete='cascade'), primary_key=True) tag = db.relationship('Tag', backref=db.backref('thing_relationships', passive_deletes='all')) thing_id = db.Column('thing_id', db.ForeignKey('thing.id', ondelete='cascade'), primary_key=True) thing = db.relationship('Thing', backref=db.backref('tag_relationships', passive_deletes='all'))
class Tag(db.Model): """ This is mostly what you'll be searching for. """ __tablename__ = 'tag' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String, unique=True, nullable=False) @property def things(self): return [item.thing for item in self.thing_relationships] @property def files(self): return [item.file for item in self.file_relationships] @property def thing_files(self): from tagger.models import File, FileThing, Thing, ThingTag return db.session.query(File).join(FileThing).join(Thing).join(ThingTag).filter( ThingTag.tag == self ).all()
class Appearance(db.Model): """ An appearance of a Thing in a File. The Thing doesn't have to be specified though. You can tag an appearance on its own, before the Thing it concerns is identified. """ __tablename__ = 'appearance' id = db.Column(db.Integer, primary_key=True) # required file_id = db.Column('file_id', db.ForeignKey('file.id', ondelete='cascade'), nullable=False) file = db.relationship('File', backref=db.backref('appearances', passive_deletes='all')) # optional thing_id = db.Column('thing_id', db.ForeignKey('thing.id', ondelete='cascade')) thing = db.relationship('Thing', backref=db.backref('appearances', passive_deletes='all')) # where is it position_x = db.Column(db.Integer, nullable=False) position_y = db.Column(db.Integer, nullable=False) size_x = db.Column(db.Integer, nullable=False) size_y = db.Column(db.Integer, nullable=False) @property def tag_names(self): return set([ tagging.tag.name for tagging in self.taggings if not tagging.negative ]) @property def negative_tag_names(self): return set([ tagging.tag.name for tagging in self.taggings if tagging.negative ]) @property def thing_name(self): if self.thing: return self.thing.name @property def position(self): return dict( x=self.position_x, y=self.position_y, ) @position.setter def position(self, value): self.position_x = value['x'] self.position_y = value['y'] @property def size(self): return dict( x=self.size_x, y=self.size_y, ) @size.setter def size(self, value): self.size_x = value['x'] self.size_y = value['y'] @property def dimensions(self): return dict( position=self.position, size=self.size, ) @dimensions.setter def dimensions(self, value): self.position = value['position'] self.size = value['size']
class Source(db.Model): id = db.Column(db.Integer, primary_key=True) url = db.Column(db.String, nullable=False) file_id = db.Column(db.ForeignKey('file.id'), nullable=False) file = db.relationship('File', backref='sources')