class Weapon(ResourceMixin, db.Model): id = db.Column(db.Integer, db.ForeignKey('item.id'), primary_key=True) item = db.relationship('Item') category = db.Column(db.Enum(WeaponCategory, native_enum=False), nullable=False) classification = db.Column(db.Enum(WeaponClass, native_enum=False)) properties = db.relationship('WeaponProperty', secondary=weapon_weapon_property_map)
class Feature(ResourceMixin, db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128), nullable=False, unique=True, index=True) description = db.Column( db.String(), nullable=False, ) category = db.Column(db.Enum(FeatureCategory, native_enum=False), nullable=False, index=True) maximum_uses = db.Column(db.Integer) current_uses = db.Column(db.Integer) replenishes_on = db.Column(db.Enum(Rest, native_enum=False))
class StatBlock(ResourceMixin, db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(256), nullable=False, index=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True) user = db.relationship(account.User) classes = db.relationship('StatBlockClass') background_id = db.Column(db.Integer, db.ForeignKey('background.id'), nullable=True, index=True) background = db.relationship('Background') race_id = db.Column(db.Integer, db.ForeignKey('race.id'), nullable=False, index=True) race = db.relationship('Race') alignment = db.Column(db.Enum(Alignment, native_enum=False), index=True) experience_points = db.Column(db.Integer, index=True, default=0) proficiency_bonus = db.Column(db.Integer) ability_scores = db.relationship('AbilityScore', back_populates='stat_block') base_hit_point_max = db.Column(db.Integer) current_hit_points = db.Column(db.Integer) temporary_hit_points = db.Column(db.Integer) speed_scores = db.relationship('SpeedScore', back_populates='stat_block') personality_traits = db.Column(db.String) ideals = db.Column(db.String) bonds = db.Column(db.String) flaws = db.Column(db.String)
class Armor(ResourceMixin, db.Model): id = db.Column(db.Integer, db.ForeignKey('item.id'), primary_key=True) item = db.relationship('Item') category = db.Column(db.Enum(ArmorCategories, native_enum=False), nullable=False) base_armor_class = db.Column(db.Integer, nullable=False, index=True) strength_requirement = db.Column(db.Integer, nullable=False, index=True, default=0) stealth_effect = db.Column(db.String, index=True)
class StatBlockClass(ResourceMixin, db.Model): __table_args__ = (db.UniqueConstraint('stat_block_id', 'creature_class_id'), ) id = db.Column(db.Integer, primary_key=True) stat_block_id = db.Column(db.Integer, db.ForeignKey('stat_block.id')) creature_class_id = db.Column(db.Integer, db.ForeignKey('creature_class.id')) level = db.Column(db.Integer, nullable=False, index=True) hit_die = db.Column(db.Enum(DieType)) creature_class = db.relationship('CreatureClass') stat_block = db.relationship('StatBlock', back_populates='classes') current_hit_dice = db.Column(db.Integer, nullable=False, default=0)
class SpellComponent(db.Model): __table_args__ = (db.UniqueConstraint('type', 'spell_id'), ) id = db.Column(db.Integer, primary_key=True) type_ = db.Column('type', db.Enum('V', 'S', 'M', native_enum=False), nullable=False, index=True) spell_id = db.Column(db.Integer, db.ForeignKey('spell.id'), nullable=False, index=True) spell = db.relationship('Spell', back_populates='components')
class User(ResourceMixin, db.Model): id = db.Column(db.BigInteger, primary_key=True) # Authentication role = db.Column(db.Enum(UserRole, native_enum=False, name='user_role_type'), index=True, nullable=False, server_default='member') is_active = db.Column(db.Boolean(), nullable=False, server_default='1') username = db.Column(db.String(24), nullable=False, unique=True, index=True) email_address = db.Column(db.String(255), nullable=False, unique=True, index=True) password = db.Column(db.String(128), nullable=False, server_default='') # Activity Tracking login_count = db.Column(db.Integer, nullable=False, default=0) current_login_time = db.Column(AwareDateTime()) current_login_ip_address = db.Column(db.String(45)) last_login_time = db.Column(AwareDateTime()) last_login_ip_address = db.Column(db.String(45)) def __init__(self, **kwargs): super.__init__(**kwargs) self.password = User.encrypt_password(kwargs.get('password', '')) @staticmethod def find_by_identity(identity: str) -> 'User': """ Find a user by their e-mail or username. :param identity: Email or username :type identity: str :return: User instance """ return User.query.filter( (User.email_address == identity) | (User.username == identity)).first() @staticmethod def encrypt_password(plaintext_password: str) -> Optional[str]: """ Hash a plaintext string using PBKDF2. This is good enough according to the NIST (National Institute of Standards and Technology). In other words while bcrypt might be superior in practice, if you use PBKDF2 properly (which we are), then your passwords are safe. :param plaintext_password: Password in plain text :type plaintext_password: str :return: str """ if plaintext_password: return generate_password_hash(plaintext_password) return None @staticmethod def deserialize_token(token: str) -> Optional['User']: """ Obtain a user from de-serializing a signed token. :param token: Signed token. :type token: str :return: User instance or None """ private_key = TimedJSONWebSignatureSerializer( current_app.config['SECRET_KEY']) try: decoded_payload = private_key.loads(token) return User.find_by_identity(decoded_payload.get('user_email')) except Exception: return None def authenticate(self, password: str='') -> bool: """ Ensure a user is authenticated, and optionally check their password. :param password: Optionally verify this as their password """ if password: return check_password_hash(self.password, password) return True def register_login(self, ip_address): self.sign_in_count += 1 self.last_login_ip_address = self.current_login_ip_address self.last_login_time = self.current_login_time self.current_login_ip_address = ip_address self.current_login_time = tz_aware_now() return self.save()