class TrustedReview(db.Model): __tablename__ = 'trusted_reviews' id = db.Column(db.Integer, primary_key=True) reviewer_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) app_id = db.Column(db.Integer, db.ForeignKey('trusted_applications.id'), nullable=False) created_time = db.Column(db.DateTime(timezone=False), default=datetime.utcnow) comment = db.Column(db.String(length=4000), nullable=False) recommendation = db.Column(ChoiceType(TrustedRecommendation, impl=db.Integer()), nullable=False) reviewer = db.relationship('User', uselist=False, lazy='joined', foreign_keys=[reviewer_id]) application = db.relationship('TrustedApplication', uselist=False, lazy='joined', foreign_keys=[app_id])
class Ban(db.Model): __tablename__ = 'bans' id = db.Column(db.Integer, primary_key=True) created_time = db.Column(db.DateTime(timezone=False), default=datetime.utcnow) admin_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True) user_ip = db.Column(db.Binary(length=16), nullable=True) reason = db.Column(db.String(length=2048), nullable=False) admin = db.relationship('User', uselist=False, lazy='joined', foreign_keys=[admin_id]) user = db.relationship('User', uselist=False, lazy='joined', foreign_keys=[user_id]) __table_args__ = ( Index('user_ip_4', 'user_ip', mysql_length=4, unique=True), Index('user_ip_16', 'user_ip', mysql_length=16, unique=True), ) def __repr__(self): return '<Ban %r>' % self.id @property def ip_string(self): if self.user_ip: return str(ip_address(self.user_ip)) @classmethod def all_bans(cls): return cls.query @classmethod def by_id(cls, id): return cls.query.get(id) @classmethod def banned(cls, user_id, user_ip): if user_id: if user_ip: return cls.query.filter((cls.user_id == user_id) | (cls.user_ip == user_ip)) return cls.query.filter(cls.user_id == user_id) if user_ip: return cls.query.filter(cls.user_ip == user_ip) return None
class TrustedApplication(db.Model): __tablename__ = 'trusted_applications' id = db.Column(db.Integer, primary_key=True) submitter_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, index=True) created_time = db.Column(db.DateTime(timezone=False), default=datetime.utcnow) closed_time = db.Column(db.DateTime(timezone=False)) why_want = db.Column(db.String(length=4000), nullable=False) why_give = db.Column(db.String(length=4000), nullable=False) status = db.Column(ChoiceType(TrustedApplicationStatus, impl=db.Integer()), nullable=False, default=TrustedApplicationStatus.NEW) reviews = db.relationship('TrustedReview', backref='trusted_applications') submitter = db.relationship('User', uselist=False, lazy='joined', foreign_keys=[submitter_id]) @hybrid_property def is_closed(self): # We can't use the attribute names from TrustedApplicationStatus in an or here because of # SQLAlchemy jank. It'll generate the wrong query. return self.status > 1 @hybrid_property def is_new(self): return self.status == TrustedApplicationStatus.NEW @hybrid_property def is_reviewed(self): return self.status == TrustedApplicationStatus.REVIEWED @hybrid_property def is_rejected(self): return self.status == TrustedApplicationStatus.REJECTED @property def created_utc_timestamp(self): ''' Returns a UTC POSIX timestamp, as seconds ''' return (self.created_time - UTC_EPOCH).total_seconds() @classmethod def by_id(cls, id): return cls.query.get(id)
def trackers(cls): return db.relationship( cls._flavor_prefix('TorrentTrackers'), uselist=True, cascade="all, delete-orphan", lazy='select', order_by=cls._flavor_prefix('TorrentTrackers.order'))
def sub_category(cls): join_sql = ( "and_({0}SubCategory.id == foreign({0}Torrent.sub_category_id), " "{0}SubCategory.main_category_id == {0}Torrent.main_category_id)") return db.relationship(cls._flavor_prefix('SubCategory'), uselist=False, backref='torrents', lazy="joined", primaryjoin=join_sql.format(cls.__flavor__))
class UserPreferences(db.Model): __tablename__ = 'user_preferences' user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), primary_key=True) def __init__(self, user_id): self.user_id = user_id def __repr__(self): return '<UserPreferences %r>' % self.user_id user = db.relationship('User', back_populates='preferences') hide_comments = db.Column(db.Boolean, nullable=False, default=False)
def torrent(cls): return db.relationship(cls._flavor_prefix('Torrent'), uselist=False, lazy="joined")
def torrent(cls): return db.relationship(cls._flavor_prefix('Torrent'), uselist=False, back_populates='stats')
class User(db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(length=32, collation=COL_ASCII_GENERAL_CI), unique=True, nullable=False) email = db.Column(EmailType(length=255, collation=COL_ASCII_GENERAL_CI), unique=True, nullable=True) password_hash = db.Column(PasswordType(max_length=255, schemes=['argon2']), nullable=False) status = db.Column(ChoiceType(UserStatusType, impl=db.Integer()), nullable=False) level = db.Column(ChoiceType(UserLevelType, impl=db.Integer()), nullable=False) created_time = db.Column(db.DateTime(timezone=False), default=datetime.utcnow) last_login_date = db.Column(db.DateTime(timezone=False), default=None, nullable=True) last_login_ip = db.Column(db.Binary(length=16), default=None, nullable=True) registration_ip = db.Column(db.Binary(length=16), default=None, nullable=True) nyaa_torrents = db.relationship('NyaaTorrent', back_populates='user', lazy='dynamic') nyaa_comments = db.relationship('NyaaComment', back_populates='user', lazy='dynamic') sukebei_torrents = db.relationship('SukebeiTorrent', back_populates='user', lazy='dynamic') sukebei_comments = db.relationship('SukebeiComment', back_populates='user', lazy='dynamic') bans = db.relationship('Ban', uselist=True, foreign_keys='Ban.user_id') def __init__(self, username, email, password): self.username = username self.email = email self.password_hash = password self.status = UserStatusType.INACTIVE self.level = UserLevelType.REGULAR def __repr__(self): return '<User %r>' % self.username def validate_authorization(self, password): ''' Returns a boolean for whether the user can be logged in ''' checks = [ # Password must match password == self.password_hash, # Reject inactive and banned users self.status == UserStatusType.ACTIVE ] return all(checks) def gravatar_url(self): if 'DEFAULT_GRAVATAR_URL' in app.config: default_url = app.config['DEFAULT_GRAVATAR_URL'] else: default_url = flask.url_for('static', filename='img/avatar/default.png', _external=True) if app.config['ENABLE_GRAVATAR']: # from http://en.gravatar.com/site/implement/images/python/ params = { # Image size (https://en.gravatar.com/site/implement/images/#size) 's': 120, # Default image (https://en.gravatar.com/site/implement/images/#default-image) 'd': default_url, # Image rating (https://en.gravatar.com/site/implement/images/#rating) # Nyaa: PG-rated, Sukebei: X-rated 'r': 'pg' if app.config['SITE_FLAVOR'] == 'nyaa' else 'x', } # construct the url return 'https://www.gravatar.com/avatar/{}?{}'.format( md5(self.email.encode('utf-8').lower()).hexdigest(), urlencode(params)) else: return default_url @property def userlevel_str(self): level = '' if self.level == UserLevelType.REGULAR: level = 'User' elif self.level == UserLevelType.TRUSTED: level = 'Trusted' elif self.level == UserLevelType.MODERATOR: level = 'Moderator' elif self.level >= UserLevelType.SUPERADMIN: level = 'Administrator' if self.is_banned: level = 'BANNED ' + level return level @property def userstatus_str(self): if self.status == UserStatusType.INACTIVE: return 'Inactive' elif self.status == UserStatusType.ACTIVE: return 'Active' elif self.status == UserStatusType.BANNED: return 'Banned' @property def userlevel_color(self): color = '' if self.level == UserLevelType.REGULAR: color = 'default' elif self.level == UserLevelType.TRUSTED: color = 'success' elif self.level >= UserLevelType.MODERATOR: color = 'purple' if self.is_banned: color += ' strike' return color @property def ip_string(self): if self.last_login_ip: return str(ip_address(self.last_login_ip)) @property def reg_ip_string(self): if self.registration_ip: return str(ip_address(self.registration_ip)) @classmethod def by_id(cls, id): return cls.query.get(id) @classmethod def by_username(cls, username): def isascii(s): return len(s) == len(s.encode()) if not isascii(username): return None user = cls.query.filter_by(username=username).first() return user @classmethod def by_email(cls, email): user = cls.query.filter_by(email=email).first() return user @classmethod def by_username_or_email(cls, username_or_email): return cls.by_username(username_or_email) or cls.by_email( username_or_email) @property def is_moderator(self): return self.level >= UserLevelType.MODERATOR @property def is_superadmin(self): return self.level == UserLevelType.SUPERADMIN @property def is_trusted(self): return self.level >= UserLevelType.TRUSTED @property def is_banned(self): return self.status == UserStatusType.BANNED @property def is_active(self): return self.status != UserStatusType.INACTIVE @property def age(self): '''Account age in seconds''' return (datetime.utcnow() - self.created_time).total_seconds() @property def created_utc_timestamp(self): ''' Returns a UTC POSIX timestamp, as seconds ''' return (self.created_time - UTC_EPOCH).total_seconds()
def user(cls): return db.relationship('User', uselist=False, lazy="joined")
def user(cls): return db.relationship('User', uselist=False, back_populates=cls._table_prefix('comments'), lazy="joined")
def torrent(cls): return db.relationship(cls._flavor_prefix('Torrent'), uselist=False, back_populates='comments', lazy="joined")
def torrents(cls): return db.relationship(cls._flavor_prefix('Torrent'), back_populates='main_category')
def main_category(cls): return db.relationship(cls._flavor_prefix('MainCategory'), uselist=False, back_populates='sub_categories')
def tracker(cls): return db.relationship('Trackers', uselist=False, lazy='joined')
def sub_categories(cls): return db.relationship(cls._flavor_prefix('SubCategory'), back_populates='main_category')
def user(cls): return db.relationship('User', uselist=False, back_populates=cls._table_prefix('torrents'))
def comments(cls): return db.relationship(cls._flavor_prefix('Comment'), uselist=True, cascade="all, delete-orphan")
def main_category(cls): return db.relationship(cls._flavor_prefix('MainCategory'), uselist=False, back_populates='torrents', lazy="joined")
def stats(cls): return db.relationship(cls._flavor_prefix('Statistic'), uselist=False, cascade="all, delete-orphan", back_populates='torrent', lazy='joined')
def filelist(cls): return db.relationship(cls._flavor_prefix('TorrentFilelist'), uselist=False, cascade="all, delete-orphan", back_populates='torrent')