예제 #1
0
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)
예제 #2
0
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)
예제 #3
0
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)
예제 #4
0
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)
예제 #5
0
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)
예제 #6
0
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)
예제 #7
0
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)
예제 #8
0
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)
예제 #9
0
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)
예제 #10
0
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)