API - add user sport preferences
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
import datetime
|
||||
import os
|
||||
import re
|
||||
from typing import Dict, Tuple, Union
|
||||
|
||||
import jwt
|
||||
@ -10,6 +11,7 @@ from werkzeug.utils import secure_filename
|
||||
|
||||
from fittrackee import appLog, bcrypt, db
|
||||
from fittrackee.responses import (
|
||||
DataNotFoundErrorResponse,
|
||||
ForbiddenErrorResponse,
|
||||
HttpResponse,
|
||||
InvalidPayloadErrorResponse,
|
||||
@ -19,15 +21,18 @@ from fittrackee.responses import (
|
||||
)
|
||||
from fittrackee.tasks import reset_password_email
|
||||
from fittrackee.utils import get_readable_duration, verify_extension_and_size
|
||||
from fittrackee.workouts.models import Sport
|
||||
from fittrackee.workouts.utils_files import get_absolute_file_path
|
||||
|
||||
from .decorators import authenticate
|
||||
from .models import User
|
||||
from .models import User, UserSportPreference
|
||||
from .utils import check_passwords, register_controls
|
||||
from .utils_token import decode_user_token
|
||||
|
||||
auth_blueprint = Blueprint('auth', __name__)
|
||||
|
||||
HEX_COLOR_REGEX = regex = "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"
|
||||
|
||||
|
||||
@auth_blueprint.route('/auth/register', methods=['POST'])
|
||||
def register_user() -> Union[Tuple[Dict, int], HttpResponse]:
|
||||
@ -677,6 +682,108 @@ def edit_user_preferences(auth_user_id: int) -> Union[Dict, HttpResponse]:
|
||||
return handle_error_and_return_response(e, db=db)
|
||||
|
||||
|
||||
@auth_blueprint.route('/auth/profile/edit/sports', methods=['POST'])
|
||||
@authenticate
|
||||
def edit_user_sport_preferences(
|
||||
auth_user_id: int,
|
||||
) -> Union[Dict, HttpResponse]:
|
||||
"""
|
||||
edit authenticated user sport preferences
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /api/auth/profile/edit/sports HTTP/1.1
|
||||
Content-Type: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"color": "#000000",
|
||||
"is_active": true,
|
||||
"sport_id": 1,
|
||||
"stopped_speed_threshold": 1,
|
||||
"user_id": 1
|
||||
},
|
||||
"message": "user sport preferences updated",
|
||||
"status": "success"
|
||||
}
|
||||
|
||||
:<json string color: valid hexadecimal color
|
||||
:<json boolean is_active: is sport available when adding a workout
|
||||
:<json float stopped_speed_threshold: stopped speed threshold used by gpxpy
|
||||
|
||||
:reqheader Authorization: OAuth 2.0 Bearer Token
|
||||
|
||||
:statuscode 200: user preferences updated
|
||||
:statuscode 400:
|
||||
- invalid payload
|
||||
- invalid hexadecimal color
|
||||
:statuscode 401:
|
||||
- provide a valid auth token
|
||||
- signature expired, please log in again
|
||||
- invalid token, please log in again
|
||||
:statuscode 500: error, please try again or contact the administrator
|
||||
|
||||
"""
|
||||
post_data = request.get_json()
|
||||
if (
|
||||
not post_data
|
||||
or 'sport_id' not in post_data
|
||||
or len(post_data.keys()) == 1
|
||||
):
|
||||
return InvalidPayloadErrorResponse()
|
||||
|
||||
sport_id = post_data.get('sport_id')
|
||||
sport = Sport.query.filter_by(id=sport_id).first()
|
||||
if not sport:
|
||||
return DataNotFoundErrorResponse('sports')
|
||||
|
||||
color = post_data.get('color')
|
||||
is_active = post_data.get('is_active')
|
||||
stopped_speed_threshold = post_data.get('stopped_speed_threshold')
|
||||
|
||||
try:
|
||||
user_sport = UserSportPreference.query.filter_by(
|
||||
user_id=auth_user_id,
|
||||
sport_id=sport_id,
|
||||
).first()
|
||||
if not user_sport:
|
||||
user_sport = UserSportPreference(
|
||||
user_id=auth_user_id,
|
||||
sport_id=sport_id,
|
||||
stopped_speed_threshold=sport.stopped_speed_threshold,
|
||||
)
|
||||
db.session.add(user_sport)
|
||||
db.session.flush()
|
||||
if color:
|
||||
if re.match(HEX_COLOR_REGEX, color) is None:
|
||||
return InvalidPayloadErrorResponse('invalid hexadecimal color')
|
||||
user_sport.color = color
|
||||
if is_active is not None:
|
||||
user_sport.is_active = is_active
|
||||
if stopped_speed_threshold:
|
||||
user_sport.stopped_speed_threshold = stopped_speed_threshold
|
||||
db.session.commit()
|
||||
|
||||
return {
|
||||
'status': 'success',
|
||||
'message': 'user sport preferences updated',
|
||||
'data': user_sport.serialize(),
|
||||
}
|
||||
|
||||
# handler errors
|
||||
except (exc.IntegrityError, exc.OperationalError, ValueError) as e:
|
||||
return handle_error_and_return_response(e, db=db)
|
||||
|
||||
|
||||
@auth_blueprint.route('/auth/picture', methods=['POST'])
|
||||
@authenticate
|
||||
def edit_picture(auth_user_id: int) -> Union[Dict, HttpResponse]:
|
||||
|
@ -40,6 +40,11 @@ class User(BaseModel):
|
||||
'Record', lazy=True, backref=db.backref('user', lazy='joined')
|
||||
)
|
||||
language = db.Column(db.String(50), nullable=True)
|
||||
sport_preferences = db.relationship(
|
||||
'UserSportPreference',
|
||||
lazy=True,
|
||||
backref=db.backref('user', lazy='joined'),
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<User {self.username!r}>'
|
||||
@ -143,3 +148,41 @@ class User(BaseModel):
|
||||
'total_distance': float(total[0]),
|
||||
'total_duration': str(total[1]),
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
}
|
||||
|
Reference in New Issue
Block a user