class SavingThrowProficiency(ResourceMixin, db.Model): id = db.Column(db.Integer, primary_key=True) ability_score_id = db.Column(db.Integer, db.ForeignKey('ability_score.id'), unique=True) ability_score = db.relationship('AbilityScore') proficiency_multiplier = db.Column(db.Integer, default=0, nullable=False)
class CoinType(ResourceMixin, db.Model): id = db.Column(db.Integer, db.ForeignKey('item.id'), primary_key=True) item = db.relationship('Item') abbreviation = db.Column(db.String(8), nullable=False, unique=True, index=True)
class Ability(ResourceMixin, db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(32), nullable=False, unique=True, index=True) abbreviation = db.Column(db.String(8), nullable=False, unique=True, index=True)
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 ResourceMixin: """ Adds date created and date updated columns to the sources """ db_created_on = db.Column( AwareDateTime(), default=tz_aware_now, ) db_updated_on = db.Column( AwareDateTime(), default=tz_aware_now, ) def __setattr__(self, key, value): if hasattr(self, key): if getattr(self, key) != value: super().__setattr__(key, value) super().__setattr__('db_updated_on', tz_aware_now()) else: super().__setattr__(key, value) def save(self): """ Save a model instance :return: model instance """ db.session.add(self) db.session.flush() db.session.commit() return self def delete(self): """ Delete a model instance :return: the result of the commit """ db.session.delete(self) return db.session.commit() def __str__(self): """ create a human readable version of the class instance :return: string """ obj_id = hex(id(self)) if hasattr(self, '__table__'): columns = self.__table__.c.keys() else: columns = [] values = ', '.join('{!s}={!r}'.format(n, getattr(self, n)) for n in columns) return '<{!s} {!s}({!s})>'.format(obj_id, self.__class__.__name__, values)
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 AbilityScore(ResourceMixin, db.Model): __table_args__ = (db.UniqueConstraint('ability_id', 'stat_block_id'), ) id = db.Column(db.Integer, primary_key=True) ability_id = db.Column(db.Integer, db.ForeignKey('ability.id'), nullable=False) ability = db.relationship('Ability') stat_block_id = db.Column(db.Integer, db.ForeignKey('stat_block.id'), nullable=False) stat_block = db.relationship('StatBlock', back_populates='ability_scores') base_value = db.Column(db.Integer, default=10)
class SpeedScore(ResourceMixin, db.Model): __table_args__ = (db.UniqueConstraint('speed_type_id', 'stat_block_id'), ) id = db.Column(db.Integer, primary_key=True) speed_type_id = db.Column(db.Integer, db.ForeignKey('speed_type.id'), nullable=False) speed_type = db.relationship('SpeedType') stat_block_id = db.Column(db.Integer, db.ForeignKey('stat_block.id'), nullable=False) stat_block = db.relationship('StatBlock', back_populates='speed_scores') base_value = db.Column(db.Integer, default=0, nullable=False, index=True)
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 Attack(ResourceMixin, db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), nullable=False, unique=True, index=True) display_name = db.Column(db.String(64), nullable=False) required_number_of_hands = db.Column(db.Integer, nullable=False) ability_id = db.Column(db.Integer, db.ForeignKey('ability.id')) ability = db.relationship('Ability') melee_range = db.Column(db.Integer) short_range = db.Column(db.Integer) long_range = db.Column(db.Integer) uses_proficiency = db.Column(db.Boolean(), default=True)
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 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 Item(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) weight = db.Column(db.Float(), default=0) value = db.Column(db.Integer, index=True)
class DamageType(ResourceMixin, db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(32), nullable=False, unique=True, index=True)
class Condition(ResourceMixin, db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(32), nullable=False, unique=True, index=True) description = db.Column(db.String)
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 CreatureClass(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(32), unique=True, index=True, nullable=False)
class Skill(ResourceMixin, db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(32), nullable=False, unique=True) default_ability_id = db.Column(db.Integer, db.ForeignKey('ability.id'), nullable=False)
class WeaponProperty(ResourceMixin, db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(16), nullable=False, unique=True, index=True) description = db.Column(db.String())
class Spell(ResourceMixin, db.Model): id = db.Column(db.Integer, primary_key=True) unique_name = db.Column(db.String(256), nullable=False, unique=True, index=True) name = db.Column(db.String(256), nullable=False, index=True) ritual = db.Column(db.Boolean, nullable=False, index=True) level = db.Column(db.Integer, nullable=False, index=True) school_id = db.Column(db.Integer, db.ForeignKey('school_of_magic.id'), nullable=False, index=True) school = db.relationship('SchoolOfMagic', back_populates='spells') casting_time = db.Column(db.Integer, nullable=False, index=True) range = db.Column(db.String(64), nullable=False, index=True) components = db.relationship('SpellComponent', back_populates='spell') material_components = db.Column(db.String(1024), nullable=True) description = db.Column(db.String(), nullable=True) higher_levels = db.Column(db.String()) classes = db.relationship('CreatureClass', secondary=creature_class_spell_map, backref=db.backref('spells', lazy='dynamic')) damage_types = db.relationship('DamageType', secondary=damage_type_spell_map, backref=db.backref('spells', lazy='dynamic'))
class CreatureType(ResourceMixin, db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), unique=True, nullable=False, index=True) description = db.Column(db.String)
class Race(ResourceMixin, db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), unique=True, nullable=False, index=True) description = db.Column(db.String) creature_type_id = db.Column(db.Integer, db.ForeignKey('creature_type.id')) creature_type = db.relationship('CreatureType')
import enum from utils.sql import ResourceMixin from world_manager.extensions import db from world_manager.model import account class SchoolOfMagic(ResourceMixin, db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), nullable=False, unique=True, index=True) spells = db.relationship('Spell', back_populates='school') creature_class_spell_map = db.Table( 'creature_class_spell_map', db.Column('spell_id', db.Integer, db.ForeignKey('spell.id')), db.Column('creature_class_id', db.Integer, db.ForeignKey('creature_class.id')), db.PrimaryKeyConstraint('spell_id', 'creature_class_id')) damage_type_spell_map = db.Table( 'damage_type_spell_map', db.Column('damage_type_id', db.Integer, db.ForeignKey('damage_type.id')), db.Column('spell_id', db.Integer, db.ForeignKey('spell.id')), db.PrimaryKeyConstraint('damage_type_id', 'spell_id')) class DieType(enum.Enum): d4 = 4 d6 = 6 d8 = 8
class SchoolOfMagic(ResourceMixin, db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), nullable=False, unique=True, index=True) spells = db.relationship('Spell', back_populates='school')
class SkillProficiency(ResourceMixin, db.Model): id = db.Column(db.Integer, primary_key=True) skill_id = db.Column(db.Integer, db.ForeignKey('skill.id')) skill = db.relationship('Skill') stat_block_id = db.Column(db.Integer, db.ForeignKey('stat_block.id')) proficiency_multiplier = db.Column(db.Integer, default=0, nullable=False)
class Shield(ResourceMixin, db.Model): id = db.Column(db.Integer, db.ForeignKey('item.id'), primary_key=True) item = db.relationship('Item')
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()