class FileUrl(BaseModel): project_js_id = db.Column(UUID(), db.ForeignKey('project.id', ondelete='CASCADE')) project_js = relationship(Project, foreign_keys=project_js_id, backref=db.backref("js_files", lazy="dynamic")) project_css_id = db.Column(UUID(), db.ForeignKey('project.id', ondelete='CASCADE')) project_css = relationship(Project, foreign_keys=project_css_id, backref=db.backref("css_files", lazy="dynamic")) name = db.Column(db.String) @classmethod def get(cls, name): file = FileUrl.query.filter_by(name=name).first() if not file: file = FileUrl(name=name) db.session.add(file) return file def __repr__(self): return self.name
class Team(TournamentBaseModel): tournament_id = db.Column( UUID(), db.ForeignKey('tournament.id', ondelete='CASCADE')) @classmethod def loadByName(name): return Team.query.filter_by(name=name).first()
class Rock1500Song(BaseModel): title = db.Column(db.String(100), index=True) artist_id = db.Column(UUID(), db.ForeignKey('rock1500_artist.id'), nullable=False) artist = db.relationship(Rock1500Artist, backref="songs", lazy="joined") album_id = db.Column(UUID(), db.ForeignKey('rock1500_album.id'), nullable=False) album = db.relationship(Rock1500Album, backref="songs", lazy="joined") # The important rank, once it is known. rankThisYear = db.Column(db.Integer(), index=True) # Previous ranks if known. rank2021 = db.Column(db.Integer(), index=True, unique=True) rank2020 = db.Column(db.Integer(), index=True, unique=True) rank2019 = db.Column(db.Integer(), index=True, unique=True) rank2018 = db.Column(db.Integer(), index=True, unique=True) rank2017 = db.Column(db.Integer(), index=True, unique=True) rank2016 = db.Column(db.Integer(), index=True, unique=True) rank2015 = db.Column(db.Integer(), index=True, unique=True) rank2014 = db.Column(db.Integer(), index=True, unique=True) @classmethod def find_by_name(cls, title, artist): if not artist.id: # If the artist doesn't exist yet, then there won't be any songs by them. return None song = Rock1500Song.query.filter_by(title=title, artist=artist).first() return song def __repr__(self): return self.title def set2017Rank(self, value): self.rank2017 = int(value) def set2016Rank(self, value): self.rank2016 = int(value) def set2015Rank(self, value): self.rank2015 = int(value)
class Tournament(TournamentBaseModel): teams = relationship("Team", order_by='Team.date_created', lazy="dynamic", backref=db.backref("tournament")) creator_id = db.Column(UUID(), db.ForeignKey('user.id'), nullable=False) creator = db.relationship(User) pass
class Rock1500Pick(BaseModel): song_id = db.Column(UUID(), db.ForeignKey('rock1500_song.id'), nullable=False) song = db.relationship(Rock1500Song, backref="picks") position = db.Column(db.Integer(), nullable=False) # TODO delete orphans? # If a user removes a pick, it should be deleted user_id = db.Column(UUID(), db.ForeignKey('user.id'), nullable=False) user = db.relationship(User, backref=backref( "rock_picks", order_by=position, cascade='all, delete-orphan', )) def __repr__(self): return "%s: %s" % (self.position, self.song)
class Match(BaseModel): round_id = db.Column(UUID(), db.ForeignKey('round.id', ondelete='CASCADE')) round = relationship("Round", backref="matches") homeTeam_id = db.Column(UUID(), db.ForeignKey('team.id')) homeTeam = relationship("Team", foreign_keys=homeTeam_id) awayTeam_id = db.Column(UUID(), db.ForeignKey('team.id')) awayTeam = relationship("Team", foreign_keys=awayTeam_id) result = relationship( MatchResult, back_populates='match', # Cascade will delete result when a match is deleted. # delete-orphan will delete results if they are removed from a match cascade="all, delete-orphan", # Can only have a single result per match. uselist=False) def __repr__(self): return "%s - %s" % (self.homeTeam, self.awayTeam)
class AuditEvent(BaseModel): who_id = db.Column(UUID(), db.ForeignKey('user.id', ondelete='CASCADE')) who = relationship("User") section = db.Column(db.String(255)) action = db.Column(db.String(255)) object_type = db.Column(db.Unicode(255)) # This is used to point to the primary key of the linked row. object_id = db.Column(UUID) # A pointer to a row in the database somewhere. object = generic_relationship(object_type, object_id)
class MatchResult(BaseModel): homeScore = db.Column(db.Integer, nullable=False) awayScore = db.Column(db.Integer, nullable=False) match_id = db.Column(UUID(), db.ForeignKey('match.id', ondelete='CASCADE'), nullable=False) match = relationship( 'Match', # uselist=False, back_populates='result', single_parent=True) def __repr__(self): return "%s - %s" % (self.homeScore, self.awayScore)
class TrailProgress(BaseModel): trail_profile_id = db.Column(UUID(), db.ForeignKey('trail_profile.id', ondelete='CASCADE'), nullable=False) trail_profile = relationship(TrailProfile, backref=db.backref( 'progress', lazy='dynamic', cascade='all, delete-orphan')) date = db.Column(db.Date()) distance = db.Column(db.Float, nullable=False) def __repr__(self): return "<TrailProgress: %sm, %s>" % (self.distance, self.date)
class Rock1500Album(BaseModel): name = db.Column(db.String(100), unique=True, index=True) year = db.Column(db.Integer()) # the cover art image url cover_art_url = db.Column(db.String(255)) artist_id = db.Column(UUID(), db.ForeignKey('rock1500_artist.id')) artist = db.relationship(Rock1500Artist, backref="albums") @classmethod def find_by_name(cls, name): album = Rock1500Album.query.filter_by(name=name).first() return album def __repr__(self): return self.name
class User(BaseModel): profile_id = db.Column(UUID(), db.ForeignKey('profile.id', ondelete='CASCADE'), nullable=False) profile = relationship("Profile", backref=backref("user", uselist=False)) email = db.Column(db.String, unique=True) password_hash = db.Column(db.String) is_active = db.Column(db.Boolean, default=False) admin = db.Column(db.Boolean, default=False) last_login = db.Column(db.DateTime(timezone=True), server_default=func.now()) friends = relationship( "User", secondary=friendships, primaryjoin="User.id == friendships.c.user_id", secondaryjoin="User.id == friendships.c.friend_id", backref="friended_you") def __repr__(self): return self.email @classmethod def create(cls, email, password=None): user = User.query.filter_by(email=email).first() if user: raise Exception('duplicate user create') username = email.split('@')[0] profile = Profile( username=username ) user = User( email=email, profile=profile, ) user.set_password(password) db.session.add(user) db.session.commit() return user def set_password(self, password): if not password: # Generate a random password for social users. print("Generating random password for %s" % self.email) password = bcrypt.gensalt().decode('utf-8') print('Password length', len(password)) hashed = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()) # This needs to be stored as a string, not bytes. self.password_hash = hashed.decode('utf-8') def check_password(self, password_attempt): match = bcrypt.hashpw(password_attempt.encode('utf-8'), self.password_hash.encode('utf-8')) == self.password_hash.encode('utf-8') if match: return True print('Password check failed') return False # For flask login @property def is_authenticated(self): return True def get_id(self): return str(self.id) def get_preferred_timezone(self): # TODO should get from profile. # local_tz = pytz.timezone(current_user.profile.timezone) return pytz.timezone('Pacific/Auckland')
import bcrypt import pytz from shared.database import db, BaseModel, UUID from sqlalchemy.orm import relationship, backref from sqlalchemy.sql import func from werkzeug.security import gen_salt api_client_scope = db.Table( 'api_client_scope', BaseModel.metadata, db.Column('client_id', UUID(), db.ForeignKey('client.id', ondelete="CASCADE")), db.Column('scope_id', UUID(), db.ForeignKey('scope.id', ondelete="CASCADE")), db.UniqueConstraint('client_id', 'scope_id', name='client_scope_no_dups') ) class Client(BaseModel): client_id = db.Column(db.String(40), unique=True, index=True, nullable=False) client_secret = db.Column(db.String(55), unique=True, index=True, nullable=False) name = db.Column(db.String(255)) scopes = db.relationship("Scope", secondary=api_client_scope, back_populates="clients") def __init__(self, name=None, scopes=[]): self.name = name self.scopes = scopes self.client_id = gen_salt(40) self.client_secret = gen_salt(50)
class TrailProfile(BaseModel): ACTIVITY_WALK = u'walk' ACTIVITY_BIKE = u'bike' ACTIVITY_MAP = { ACTIVITY_WALK: { 'code': ACTIVITY_WALK, 'profile': 'walker', 'action': 'walking', 'plural': 'walks', 'value': 'Walking Trail', }, ACTIVITY_BIKE: { 'code': ACTIVITY_BIKE, 'profile': 'biker', 'action': 'biking', 'plural': 'bikes', 'value': 'Biking Trail', } } ACTIVITIES = [(k, v['value']) for (k, v) in ACTIVITY_MAP.items()] activity = db.Column(ChoiceType(ACTIVITIES), nullable=False, server_default=u'walk', default=u'walk') user_id = db.Column(UUID(), db.ForeignKey('user.id'), nullable=False) user = relationship( User, backref=db.backref('trail_profiles', lazy='dynamic'), ) name = db.Column(db.String) trail_id = db.Column(UUID(), db.ForeignKey('trail.id', ondelete='CASCADE'), nullable=False) trail = relationship(Trail, backref=db.backref('trail_profiles', lazy='dynamic')) color = db.Column(db.Integer, nullable=False) def __repr__(self): return "<TrailProfile: %s on %s>" % (self.user.email, self.trail.name) # deprecated @classmethod def get_or_create(cls, user, trail, color=None, activity=None): newWalker = None if activity is None: # Default to walking a trail. # Allow changing this? activity = TrailProfile.ACTIVITY_WALK if user.id and trail.id: newWalker = TrailProfile.query.filter_by( user=user, trail=trail, activity=activity).first() if not newWalker: if not color: color = random.randint(0, 16777215) newWalker = TrailProfile(user=user, trail=trail, color=color, activity=activity, name=str(user.profile)) db.session.add(newWalker) return newWalker
class Round(TournamentBaseModel): tournament_id = db.Column( UUID(), db.ForeignKey('tournament.id', ondelete='CASCADE')) tournament = relationship("Tournament", backref=db.backref("rounds", lazy="dynamic"))