class GoalCategory(SurrogatePK, db.Model): __tablename__ = 'training_goal_categories' id = Column(db.Integer, primary_key=True, index=True) name = Column(db.String(32), nullable=False) unit = Column(db.String(16), nullable=True) workout_category_id = reference_col('workout_categories', nullable=True) workout_category = relationship('WorkoutCategory') __table_args__ = (UniqueConstraint('name', 'workout_category_id', name='_name_workout_cat_id_uc'), ) def __init__(self, name, unit=None, workout_category_id=None): db.Model.__init__(self, name=name, unit=unit, workout_category_id=workout_category_id) @property def workout_category_name(self): if self.workout_category is not None: return self.workout_category.name else: return "" def __repr__(self): return '<GoalCategory({name!r},{wcid!r})>'.format( name=self.name, wcid=self.workout_category_name)
class User(SurrogatePK, TimestampedModel): __tablename__ = 'users' username = Column(db.String(16), unique=True, nullable=False, index=True) email = Column(db.String(128), unique=True, nullable=False, index=True) password = Column(db.String(128), nullable=True) confirmed = Column(db.Boolean, nullable=False, default=False) access_token = '' refresh_token = '' def __init__(self, username, email, password=None, **kwargs): """Create instance.""" db.Model.__init__(self, username=username, email=email, **kwargs) if password: self.set_password(password) else: self.password = None @classmethod def find_by_username(cls, username): return cls.query.filter_by(username=username).first() @classmethod def find_by_email(cls, email): return cls.query.filter_by(email=email).first() def set_password(self, password): self.password = sha256.hash(password) def check_password(self, password): return sha256.verify(password, self.password) def __repr__(self): return '<User({username!r})>'.format(username=self.username)
class TokenRegistry(SurrogatePK, Model): __tablename__ = 'token_registry' jti = Column(db.String(64), nullable=False, index=True) token_type = Column(db.String(8), nullable=False) username = Column(db.String(16), nullable=False, index=True) revoked = Column(db.Boolean, nullable=False) expires = Column(db.DateTime, nullable=False) def __init__(self, jti, token_type, username, revoked, expires): db.Model.__init__(self, jti=jti, token_type=token_type, username=username, revoked=revoked, expires=expires) @classmethod def is_token_revoked(cls, jti): token = cls.query.filter_by(jti=jti).first() if token is None: return True return token.revoked @classmethod def add_token(cls, encoded_token): decoded_token = decode_token(encoded_token) jti = decoded_token['jti'] token_type = decoded_token['type'] username = decoded_token['identity'] expires = dt.datetime.fromtimestamp(decoded_token['exp']) revoked = False token = cls(jti, token_type, username, revoked, expires) token.save() @classmethod def find_by_username(cls, username): return cls.query.filter_by(username=username).order_by( cls.token_type.asc(), cls.expires.desc()).all() @classmethod def find_by_jti(cls, jti): return cls.query.filter_by(jti=jti).first() @classmethod def remove_expired_tokens(cls): now = dt.datetime.now() expired_list = cls.query.filter(TokenRegistry.expires < now).all() for token in expired_list: token.delete(False) # do not commit every delete for itself db.session.commit() return len(expired_list) def __repr__(self): return '<Token({username!r}:{jti!r})>'.format(username=self.username, jti=self.jti)
class PolarUser(SurrogatePK, db.Model): __tablename__ = 'polar_users' profile_id = reference_col('user_profiles', unique=True, nullable=False, index=True) member_id = Column(db.String(24), unique=True, nullable=False) polar_user_id = Column(db.Integer, nullable=False) state = Column(db.String(16), unique=False, nullable=True) access_token = Column(db.String(64), nullable=True) access_token_expires = Column(db.DateTime, nullable=True) updated_at = Column(db.DateTime, nullable=True) def __init__(self, profile_id, username): member_id = 'R4IT_{0}'.format(username) db.Model.__init__(self, profile_id=profile_id, member_id=member_id, polar_user_id=0) @classmethod def find_by_member_id(cls, id): return cls.query.filter_by(member_id=id).first() @classmethod def find_by_polar_user_id(cls, id): return cls.query.filter_by(polar_user_id=id).first() @classmethod def find_by_state_code(cls, code): polar_users = cls.query.filter_by(state=code).all() if (polar_users is None or len(polar_users) != 1): return None return polar_users[0] @property def auth_url(self): if self.has_valid_access_token() or self.state is None: return '' return POLAR_AUTHORIZATION_URL.format(current_app.config["POLAR_API_CLIENT_ID"], self.state) def generate_state_code(self): self.state = ''.join(choice(ascii_letters + digits) for _ in range(15)) def is_registered(self): return self.polar_user_id > 0 and self.access_token is not None and len(self.access_token) > 0 def has_valid_access_token(self): if self.is_registered(): now = dt.datetime.utcnow() return self.access_token_expires > now return False def __repr__(self): return '<PolarUser({id!r}:{member!r})>'.format( id=self.profile_id, member=self.member_id)
class Script(SurrogatePK, db.Model): __tablename__ = 'scripts' name = Column(db.String(64), unique=True, nullable=False) started_at = Column(db.DateTime, nullable=True) completed_at = Column(db.DateTime, nullable=True) return_code = Column(db.Integer, nullable=True) def __init__(self, name): db.Model.__init__(self, name=name) @classmethod def find_by_name(cls, name): return cls.query.filter_by(name=name).first() @property def execution_time(self): if self.started_at is not None and self.completed_at is not None: return self.completed_at - self.started_at return None def __repr__(self): return '<Script({name!r}:{last_run!r}:{ret_code!r})>'.format( name=self.name, last_run=self.completed_at.strftime("%Y-%m-%d %H:%M:%S"), ret_code=self.return_code)
class Discipline(SurrogatePK, db.Model): __tablename__ = 'disciplines' name = Column(db.String(128), unique=True, nullable=False, index=True) length = Column(db.Integer, nullable=False) username = Column(db.String(16), nullable=True) is_route = Column(db.Boolean, nullable=False) def __init__(self, name, length, username=None, is_route=False): db.Model.__init__(self, name=name, length=length, username=username, is_route=is_route) @classmethod def find_by_name(cls, name): return cls.query.filter_by(name=name).first() def __repr__(self): return '<Discipline({name!r})>'.format(name=self.name)
class UserConfirmation(SurrogatePK, Model): __tablename__ = 'user_confirmation_codes' username = Column(db.String(16), unique=True, nullable=False, index=True) code = Column(db.String(64), nullable=False) created_at = Column(db.DateTime, nullable=False, default=dt.datetime.utcnow) def __init__(self, username, confirmation_code=None, **kwargs): db.Model.__init__(self, username=username, **kwargs) if confirmation_code: self.code = confirmation_code else: self.generate_code(32) @classmethod def find_by_username(cls, username): return cls.query.filter_by(username=username).first() def generate_code(self, size): self.code = ''.join( choice(ascii_letters + digits) for _ in range(size)) def check_code(self, confirmation_code): return self.code == confirmation_code def check_expiration(self, seconds): now = dt.datetime.utcnow() is_expired = False try: datedelta = now - self.created_at is_expired = (datedelta.days > 0 or datedelta.seconds > seconds) except: # timedelta overflow is_expired = True return not is_expired def __repr__(self): return '<UserConfirmation({username!r}:{code!r})>'.format( username=self.username, code=self.code)
class PolarWebhookExercise(SurrogatePK, db.Model): __tablename__ = 'polar_webhook_exercises' polar_user_id = Column(db.Integer, nullable=False) entity_id = Column(db.String(32), unique=True, nullable=False) url = Column(db.String(255), nullable=True) timestamp = Column(db.DateTime, nullable=False) processed = Column(db.Boolean, nullable=False, index=True) def __init__(self, polar_user_id, entity_id, timestamp, url=None): db.Model.__init__(self, polar_user_id=polar_user_id, entity_id=entity_id, url=url, timestamp=timestamp, processed=False) @classmethod def find_by_polar_user_id(cls, id): return cls.query.filter_by(polar_user_id=id).first() @classmethod def get_not_processed(cls): return cls.query.filter_by(processed=False).order_by(cls.polar_user_id).all() def __repr__(self): return '<PolarWebhookExercise({user!r}:{entity!r})>'.format(user=self.polar_user_id,entity=self.entity_id)
class WorkoutCategory(SurrogatePK, db.Model): __tablename__ = 'workout_categories' id = Column(db.Integer, primary_key=True, index=True) name = Column(db.String(32), nullable=False, unique=True) supports_gps_data = Column(db.Boolean, nullable=False) def __init__(self, name, supports_gps_data): db.Model.__init__(self, name=name, supports_gps_data=supports_gps_data) @classmethod def find_by_name(cls, name): return cls.query.filter_by(name=name).first() def __repr__(self): return '<WorkoutCategory({name!r})>'.format(name=self.name)
class Workout(SurrogatePK, db.Model): __tablename__ = 'workouts' name = Column(db.String(128), nullable=False) start_at = Column(db.DateTime, nullable=False) distance = Column(db.Integer, nullable=False) duration = Column(db.Integer, nullable=False) climb = Column(db.Integer, nullable=False) resource_path = Column(db.String(255), unique=True, nullable=True) edited = Column(db.Boolean, nullable=False) category_id = reference_col('workout_categories', nullable=False) category = relationship('WorkoutCategory') profile_id = reference_col('user_profiles', nullable=False, index=True) def __init__(self, profile_id, category, name, start_at, distance, duration, climb, resource_path=None, edited=False): db.Model.__init__(self, profile_id=profile_id, category=category, name=name, start_at=start_at, distance=distance, duration=duration, climb=climb, resource_path=resource_path, edited=edited) def register_extended_data(self): self.extended_track_data = None self.extended_split_data = None self.extended_summary = None # parse file if it exists if is_filename_extension_of_type(self.resource_path, "gpx"): gpx = GpxParser(self.resource_path) if gpx.get_num_of_tracks() > 0: self.extended_track_data, self.extended_split_data, self.extended_summary = gpx.get_track_data(1) # we only expect one track per file elif is_filename_extension_of_type(self.resource_path, "tcx"): tcx = TcxParser(self.resource_path) if tcx.get_num_of_tracks() > 0: self.extended_track_data, self.extended_split_data, self.extended_summary = tcx.get_track_data() elif is_filename_extension_of_type(self.resource_path, "fit"): fit = FitParser(self.resource_path) if fit.get_num_of_tracks() > 0: self.extended_track_data, self.extended_split_data, self.extended_summary = fit.get_track_data() if not self.category.supports_gps_data: self.extended_track_data = None self.extended_split_data = None @property def resource_file(self): if self.resource_path is not None: return ntpath.basename(self.resource_path) else: return None @property def category_name(self): return self.category.name @property def average_speed(self): dist_km = self.distance / 1000.0 dur_h = self.duration / 3600.0 if dur_h > 0.0: return round(dist_km / dur_h, 2) else: return 0.0 @property def average_pace(self): dist_km = self.distance / 1000.0 dur_min = self.duration / 60.0 if dist_km > 0.0: avg_pace = dur_min / dist_km avg_pace_min = floor(avg_pace) avg_pace_sec = int((avg_pace - avg_pace_min) * 60) return "{0:02d}:{1:02d}".format(avg_pace_min, avg_pace_sec) else: return "" @classmethod def get_workouts_for_goal(cls, goal): if goal.category.workout_category is not None: goal_workout_id = goal.category.workout_category.id return cls.query.filter(and_( Workout.category_id==goal_workout_id, Workout.profile_id==goal.profile_id, Workout.start_at >= goal.start_at, Workout.start_at < goal.end_at) ).order_by(Workout.start_at.asc()).all() else: return [] def __repr__(self): return '<Workout({name!r},{distance!r}m)>'.format( name=self.name, distance=self.distance)