FitTrackee/fittrackee/users/models.py

228 lines
7.4 KiB
Python
Raw Normal View History

from datetime import datetime
2021-01-02 19:28:03 +01:00
from typing import Dict, Optional, Union
2017-12-16 21:00:46 +01:00
import jwt
from flask import current_app
from sqlalchemy import func
2021-01-02 19:28:03 +01:00
from sqlalchemy.ext.declarative import DeclarativeMeta
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.sql.expression import select
2021-01-20 16:47:00 +01:00
from fittrackee import bcrypt, db
from fittrackee.workouts.models import Workout
from .exceptions import UserNotFoundException
from .roles import UserRole
2022-02-16 18:07:05 +01:00
from .utils.token import decode_user_token, get_user_token
2017-12-16 21:00:46 +01:00
2021-01-02 19:28:03 +01:00
BaseModel: DeclarativeMeta = db.Model
2017-12-16 21:00:46 +01:00
2021-01-02 19:28:03 +01:00
class User(BaseModel):
__tablename__ = 'users'
2017-12-16 21:00:46 +01:00
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(255), unique=True, nullable=False)
2022-03-13 08:52:09 +01:00
email = db.Column(db.String(255), unique=True, nullable=False)
2017-12-16 21:00:46 +01:00
password = db.Column(db.String(255), nullable=False)
created_at = db.Column(db.DateTime, nullable=False)
2017-12-17 09:16:08 +01:00
admin = db.Column(db.Boolean, default=False, nullable=False)
2018-01-01 16:59:46 +01:00
first_name = db.Column(db.String(80), nullable=True)
last_name = db.Column(db.String(80), nullable=True)
birth_date = db.Column(db.DateTime, nullable=True)
location = db.Column(db.String(80), nullable=True)
bio = db.Column(db.String(200), nullable=True)
2018-01-01 21:54:03 +01:00
picture = db.Column(db.String(255), nullable=True)
timezone = db.Column(db.String(50), nullable=True)
# does the week start Monday?
weekm = db.Column(db.Boolean, default=False, nullable=False)
workouts = db.relationship(
'Workout',
lazy=True,
backref=db.backref('user', lazy='joined', single_parent=True),
2019-08-28 13:25:39 +02:00
)
records = db.relationship(
'Record',
lazy=True,
backref=db.backref('user', lazy='joined', single_parent=True),
2019-08-28 13:25:39 +02:00
)
language = db.Column(db.String(50), nullable=True)
imperial_units = db.Column(db.Boolean, default=False, nullable=False)
2022-03-19 22:02:06 +01:00
is_active = db.Column(db.Boolean, default=False, nullable=False)
2022-03-13 08:52:09 +01:00
email_to_confirm = db.Column(db.String(255), nullable=True)
confirmation_token = db.Column(db.String(255), nullable=True)
2017-12-16 21:00:46 +01:00
2021-01-02 19:28:03 +01:00
def __repr__(self) -> str:
2018-05-23 17:30:22 +02:00
return f'<User {self.username!r}>'
2017-12-16 21:00:46 +01:00
def __init__(
2021-01-02 19:28:03 +01:00
self,
username: str,
email: str,
password: str,
created_at: Optional[datetime] = None,
2021-01-02 19:28:03 +01:00
) -> None:
2017-12-16 21:00:46 +01:00
self.username = username
self.email = email
self.password = bcrypt.generate_password_hash(
password, current_app.config.get('BCRYPT_LOG_ROUNDS')
).decode()
self.created_at = (
datetime.utcnow() if created_at is None else created_at
)
2017-12-16 21:00:46 +01:00
2017-12-17 12:07:25 +01:00
@staticmethod
2021-01-02 19:28:03 +01:00
def encode_auth_token(user_id: int) -> str:
2018-01-01 16:59:46 +01:00
"""
Generates the auth token
:param user_id: -
:return: JWToken
"""
2021-01-02 19:28:03 +01:00
return get_user_token(user_id)
@staticmethod
2021-01-02 19:28:03 +01:00
def encode_password_reset_token(user_id: int) -> str:
"""
Generates the auth token
:param user_id: -
:return: JWToken
"""
2021-01-02 19:28:03 +01:00
return get_user_token(user_id, password_reset=True)
2017-12-16 21:00:46 +01:00
@staticmethod
2021-01-02 19:28:03 +01:00
def decode_auth_token(auth_token: str) -> Union[int, str]:
2017-12-16 21:00:46 +01:00
"""
Decodes the auth token
:param auth_token: -
:return: integer|string
"""
try:
return decode_user_token(auth_token)
2017-12-16 21:00:46 +01:00
except jwt.ExpiredSignatureError:
return 'signature expired, please log in again'
2017-12-16 21:00:46 +01:00
except jwt.InvalidTokenError:
return 'invalid token, please log in again'
2018-05-09 15:52:27 +02:00
2022-05-23 13:04:01 +02:00
def check_password(self, password: str) -> bool:
return bcrypt.check_password_hash(self.password, password)
@staticmethod
def generate_password_hash(new_password: str) -> str:
return bcrypt.generate_password_hash(
new_password, current_app.config.get('BCRYPT_LOG_ROUNDS')
).decode()
@hybrid_property
def workouts_count(self) -> int:
return Workout.query.filter(Workout.user_id == self.id).count()
@workouts_count.expression # type: ignore
def workouts_count(self) -> int:
return (
select([func.count(Workout.id)])
.where(Workout.user_id == self.id)
.label('workouts_count')
)
def serialize(self, current_user: 'User') -> Dict:
role = (
UserRole.AUTH_USER
if current_user.id == self.id
else UserRole.ADMIN
if current_user.admin
else UserRole.USER
)
if role == UserRole.USER:
raise UserNotFoundException()
2018-06-07 22:44:52 +02:00
sports = []
2021-01-02 19:28:03 +01:00
total = (0, '0:00:00')
if self.workouts_count > 0: # type: ignore
2019-08-28 13:25:39 +02:00
sports = (
db.session.query(Workout.sport_id)
.filter(Workout.user_id == self.id)
.group_by(Workout.sport_id)
.order_by(Workout.sport_id)
2019-08-28 13:25:39 +02:00
.all()
)
2019-09-23 14:43:56 +02:00
total = (
db.session.query(
func.sum(Workout.distance), func.sum(Workout.duration)
2019-09-23 14:43:56 +02:00
)
.filter(Workout.user_id == self.id)
2019-09-23 14:43:56 +02:00
.first()
)
serialized_user = {
2018-05-09 15:52:27 +02:00
'admin': self.admin,
'bio': self.bio,
'birth_date': self.birth_date,
'created_at': self.created_at,
'email': self.email,
'email_to_confirm': self.email_to_confirm,
2018-05-09 15:52:27 +02:00
'first_name': self.first_name,
2022-03-19 22:02:06 +01:00
'is_active': self.is_active,
2018-05-09 15:52:27 +02:00
'last_name': self.last_name,
'location': self.location,
'nb_sports': len(sports),
'nb_workouts': self.workouts_count,
'picture': self.picture is not None,
2021-09-21 18:10:27 +02:00
'records': [record.serialize() for record in self.records],
'sports_list': [
sport for sportslist in sports for sport in sportslist
],
2021-01-02 19:28:03 +01:00
'total_distance': float(total[0]),
'total_duration': str(total[1]),
'username': self.username,
2018-05-09 15:52:27 +02:00
}
if role == UserRole.AUTH_USER:
serialized_user = {
**serialized_user,
**{
'imperial_units': self.imperial_units,
'language': self.language,
'timezone': self.timezone,
'weekm': self.weekm,
},
}
return serialized_user
2021-11-12 12:22:07 +01:00
class UserSportPreference(BaseModel):
__tablename__ = 'users_sports_preferences'
user_id = db.Column(
db.Integer,
db.ForeignKey('users.id'),
primary_key=True,
)
sport_id = db.Column(
db.Integer,
db.ForeignKey('sports.id'),
primary_key=True,
)
color = db.Column(db.String(50), nullable=True)
is_active = db.Column(db.Boolean, default=True, nullable=False)
stopped_speed_threshold = db.Column(db.Float, default=1.0, nullable=False)
def __init__(
self,
user_id: int,
sport_id: int,
stopped_speed_threshold: float,
) -> None:
self.user_id = user_id
self.sport_id = sport_id
self.is_active = True
self.stopped_speed_threshold = stopped_speed_threshold
def serialize(self) -> Dict:
return {
'user_id': self.user_id,
'sport_id': self.sport_id,
'color': self.color,
'is_active': self.is_active,
'stopped_speed_threshold': self.stopped_speed_threshold,
}