Merge branch 'api-refactor'
This commit is contained in:
commit
7a1f44ccc5
@ -9,7 +9,7 @@ from flask_dramatiq import Dramatiq
|
||||
from flask_migrate import Migrate
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
from .email.email import Email
|
||||
from fittrackee.emails.email import Email
|
||||
|
||||
db = SQLAlchemy()
|
||||
bcrypt = Bcrypt()
|
||||
|
@ -5,15 +5,16 @@ import shutil
|
||||
from typing import Dict, Optional
|
||||
|
||||
import gunicorn.app.base
|
||||
from flask import Flask
|
||||
from flask_dramatiq import worker
|
||||
from flask_migrate import upgrade
|
||||
from tqdm import tqdm
|
||||
|
||||
from fittrackee import create_app, db
|
||||
from fittrackee.application.utils import init_config
|
||||
from fittrackee.database_utils import init_database
|
||||
from fittrackee.workouts.models import Workout
|
||||
from fittrackee.workouts.utils import update_workout
|
||||
from flask import Flask
|
||||
from flask_dramatiq import worker
|
||||
from flask_migrate import upgrade
|
||||
from tqdm import tqdm
|
||||
|
||||
HOST = os.getenv('HOST', '0.0.0.0')
|
||||
PORT = os.getenv('PORT', '5000')
|
||||
|
@ -1,15 +1,16 @@
|
||||
from typing import Dict, Union
|
||||
|
||||
from flask import Blueprint, current_app, request
|
||||
from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound
|
||||
|
||||
from fittrackee import db
|
||||
from fittrackee.responses import (
|
||||
HttpResponse,
|
||||
InvalidPayloadErrorResponse,
|
||||
handle_error_and_return_response,
|
||||
)
|
||||
from flask import Blueprint, current_app, request
|
||||
from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound
|
||||
from fittrackee.users.decorators import authenticate_as_admin
|
||||
|
||||
from ..users.utils import authenticate_as_admin
|
||||
from .models import AppConfig
|
||||
from .utils import update_app_config_from_database
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
from typing import Dict
|
||||
|
||||
from fittrackee import db
|
||||
from flask import current_app
|
||||
from sqlalchemy.engine.base import Connection
|
||||
from sqlalchemy.event import listens_for
|
||||
@ -8,7 +7,8 @@ from sqlalchemy.ext.declarative import DeclarativeMeta
|
||||
from sqlalchemy.orm.mapper import Mapper
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from ..users.models import User
|
||||
from fittrackee import db
|
||||
from fittrackee.users.models import User
|
||||
|
||||
BaseModel: DeclarativeMeta = db.Model
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
import os
|
||||
from typing import Tuple
|
||||
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee import db
|
||||
from fittrackee.users.models import User
|
||||
from flask import Flask
|
||||
|
||||
from .models import AppConfig
|
||||
|
||||
|
@ -26,7 +26,7 @@ class BaseConfig:
|
||||
)
|
||||
PICTURE_ALLOWED_EXTENSIONS = {'jpg', 'png', 'gif'}
|
||||
WORKOUT_ALLOWED_EXTENSIONS = {'gpx', 'zip'}
|
||||
TEMPLATES_FOLDER = os.path.join(current_app.root_path, 'email/templates')
|
||||
TEMPLATES_FOLDER = os.path.join(current_app.root_path, 'emails/templates')
|
||||
UI_URL = os.environ.get('UI_URL')
|
||||
EMAIL_URL = os.environ.get('EMAIL_URL')
|
||||
SENDER_EMAIL = os.environ.get('SENDER_EMAIL')
|
||||
|
@ -1,3 +1,5 @@
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee import db
|
||||
from fittrackee.application.utils import (
|
||||
init_config,
|
||||
@ -5,7 +7,6 @@ from fittrackee.application.utils import (
|
||||
)
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Sport
|
||||
from flask import Flask
|
||||
|
||||
|
||||
def init_database(app: Flask) -> None:
|
||||
|
2
fittrackee/emails/exceptions.py
Normal file
2
fittrackee/emails/exceptions.py
Normal file
@ -0,0 +1,2 @@
|
||||
class InvalidEmailUrlScheme(Exception):
|
||||
...
|
@ -2,9 +2,7 @@ from typing import Dict
|
||||
|
||||
from urllib3.util import parse_url
|
||||
|
||||
|
||||
class InvalidEmailUrlScheme(Exception):
|
||||
...
|
||||
from .exceptions import InvalidEmailUrlScheme
|
||||
|
||||
|
||||
def parse_email_url(email_url: str) -> Dict:
|
11
fittrackee/exceptions.py
Normal file
11
fittrackee/exceptions.py
Normal file
@ -0,0 +1,11 @@
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class GenericException(Exception):
|
||||
def __init__(
|
||||
self, status: str, message: str, e: Optional[Exception] = None
|
||||
) -> None:
|
||||
super().__init__(message)
|
||||
self.status = status
|
||||
self.message = message
|
||||
self.e = e
|
@ -1,10 +1,11 @@
|
||||
from json import dumps
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
from fittrackee import appLog
|
||||
from flask import Response
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
from fittrackee import appLog
|
||||
|
||||
|
||||
def get_empty_data_for_datatype(data_type: str) -> Union[str, List]:
|
||||
return '' if data_type in ['gpx', 'chart_data'] else []
|
||||
|
@ -1,8 +1,9 @@
|
||||
import json
|
||||
|
||||
from fittrackee.users.models import User
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.users.models import User
|
||||
|
||||
|
||||
class TestGetConfig:
|
||||
def test_it_gets_application_config(
|
||||
|
@ -1,6 +1,7 @@
|
||||
from fittrackee.application.models import AppConfig
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.application.models import AppConfig
|
||||
|
||||
|
||||
class TestConfigModel:
|
||||
def test_application_config(self, app: Flask) -> None:
|
||||
|
@ -3,6 +3,7 @@ import os
|
||||
from typing import Generator, Optional
|
||||
|
||||
import pytest
|
||||
|
||||
from fittrackee import create_app, db
|
||||
from fittrackee.application.models import AppConfig
|
||||
from fittrackee.application.utils import update_app_config_from_database
|
||||
|
@ -1,11 +1,12 @@
|
||||
from typing import Any
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from fittrackee import email_service
|
||||
from fittrackee.email.email import EmailMessage
|
||||
from flask import Flask
|
||||
|
||||
from ..template_results.password_reset_request import expected_en_text_body
|
||||
from fittrackee import email_service
|
||||
from fittrackee.emails.email import EmailMessage
|
||||
|
||||
from .template_results.password_reset_request import expected_en_text_body
|
||||
|
||||
|
||||
class TestEmailMessage:
|
@ -1,8 +1,9 @@
|
||||
import pytest
|
||||
from fittrackee.email.email import EmailTemplate
|
||||
from flask import Flask
|
||||
|
||||
from ..template_results.password_reset_request import (
|
||||
from fittrackee.emails.email import EmailTemplate
|
||||
|
||||
from .template_results.password_reset_request import (
|
||||
expected_en_html_body,
|
||||
expected_en_text_body,
|
||||
expected_fr_html_body,
|
@ -1,5 +1,9 @@
|
||||
import pytest
|
||||
from fittrackee.email.utils_email import InvalidEmailUrlScheme, parse_email_url
|
||||
|
||||
from fittrackee.emails.utils_email import (
|
||||
InvalidEmailUrlScheme,
|
||||
parse_email_url,
|
||||
)
|
||||
|
||||
|
||||
class TestEmailUrlParser:
|
@ -3,11 +3,12 @@ from datetime import datetime, timedelta
|
||||
from io import BytesIO
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from flask import Flask
|
||||
from freezegun import freeze_time
|
||||
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.users.utils_token import get_user_token
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
from flask import Flask
|
||||
from freezegun import freeze_time
|
||||
|
||||
|
||||
class TestUserRegistration:
|
||||
|
@ -3,9 +3,10 @@ from datetime import datetime, timedelta
|
||||
from io import BytesIO
|
||||
from unittest.mock import patch
|
||||
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
from flask import Flask
|
||||
|
||||
|
||||
class TestGetUser:
|
||||
|
@ -1,6 +1,7 @@
|
||||
from fittrackee.users.models import User
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.users.models import User
|
||||
|
||||
|
||||
class TestUserModel:
|
||||
def test_user_model(self, app: Flask, user_1: User) -> None:
|
||||
|
@ -1,8 +1,9 @@
|
||||
import json
|
||||
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
from flask import Flask
|
||||
|
||||
|
||||
class TestGetRecords:
|
||||
|
@ -1,8 +1,9 @@
|
||||
import datetime
|
||||
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Record, Sport, Workout
|
||||
from flask import Flask
|
||||
|
||||
|
||||
class TestRecordModel:
|
||||
|
@ -1,8 +1,9 @@
|
||||
import json
|
||||
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
from flask import Flask
|
||||
|
||||
expected_sport_1_cycling_result = {
|
||||
'id': 1,
|
||||
|
@ -1,8 +1,9 @@
|
||||
from typing import Dict, Optional
|
||||
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
from flask import Flask
|
||||
|
||||
|
||||
class TestSportModel:
|
||||
|
@ -1,8 +1,9 @@
|
||||
import json
|
||||
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
from flask import Flask
|
||||
|
||||
|
||||
class TestGetStatsByTime:
|
||||
|
@ -1,9 +1,10 @@
|
||||
import json
|
||||
from uuid import uuid4
|
||||
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
from flask import Flask
|
||||
|
||||
from .utils import get_random_short_id
|
||||
|
||||
|
@ -4,10 +4,11 @@ from datetime import datetime
|
||||
from io import BytesIO
|
||||
from typing import Dict
|
||||
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
from fittrackee.workouts.utils_id import decode_short_id
|
||||
from flask import Flask
|
||||
|
||||
|
||||
def assert_workout_data_with_gpx(data: Dict) -> None:
|
||||
|
@ -1,10 +1,11 @@
|
||||
import json
|
||||
from typing import Dict
|
||||
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
from fittrackee.workouts.utils_id import decode_short_id
|
||||
from flask import Flask
|
||||
|
||||
from .utils import get_random_short_id, post_an_workout
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
from fittrackee.workouts.utils import get_absolute_file_path
|
||||
from flask import Flask
|
||||
|
||||
from .utils import get_random_short_id, post_an_workout
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
from uuid import UUID
|
||||
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
from fittrackee.workouts.utils_id import decode_short_id
|
||||
from flask import Flask
|
||||
|
||||
|
||||
class TestWorkoutModel:
|
||||
|
@ -3,9 +3,10 @@ from io import BytesIO
|
||||
from typing import Tuple
|
||||
from uuid import uuid4
|
||||
|
||||
from fittrackee.workouts.utils_id import encode_uuid
|
||||
from flask import Flask
|
||||
|
||||
from fittrackee.workouts.utils_id import encode_uuid
|
||||
|
||||
|
||||
def get_random_short_id() -> str:
|
||||
return encode_uuid(uuid4())
|
||||
|
@ -3,6 +3,11 @@ import os
|
||||
from typing import Dict, Tuple, Union
|
||||
|
||||
import jwt
|
||||
from flask import Blueprint, current_app, request
|
||||
from sqlalchemy import exc, or_
|
||||
from werkzeug.exceptions import RequestEntityTooLarge
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from fittrackee import appLog, bcrypt, db
|
||||
from fittrackee.responses import (
|
||||
ForbiddenErrorResponse,
|
||||
@ -13,15 +18,11 @@ from fittrackee.responses import (
|
||||
handle_error_and_return_response,
|
||||
)
|
||||
from fittrackee.tasks import reset_password_email
|
||||
from flask import Blueprint, current_app, request
|
||||
from sqlalchemy import exc, or_
|
||||
from werkzeug.exceptions import RequestEntityTooLarge
|
||||
from werkzeug.utils import secure_filename
|
||||
from fittrackee.workouts.utils_files import get_absolute_file_path
|
||||
|
||||
from ..workouts.utils_files import get_absolute_file_path
|
||||
from .decorators import authenticate
|
||||
from .models import User
|
||||
from .utils import (
|
||||
authenticate,
|
||||
check_passwords,
|
||||
display_readable_file_size,
|
||||
get_readable_duration,
|
||||
|
36
fittrackee/users/decorators.py
Normal file
36
fittrackee/users/decorators.py
Normal file
@ -0,0 +1,36 @@
|
||||
from functools import wraps
|
||||
from typing import Any, Callable, Union
|
||||
|
||||
from flask import request
|
||||
|
||||
from fittrackee.responses import HttpResponse
|
||||
|
||||
from .utils import verify_user
|
||||
|
||||
|
||||
def authenticate(f: Callable) -> Callable:
|
||||
@wraps(f)
|
||||
def decorated_function(
|
||||
*args: Any, **kwargs: Any
|
||||
) -> Union[Callable, HttpResponse]:
|
||||
verify_admin = False
|
||||
response_object, resp = verify_user(request, verify_admin)
|
||||
if response_object:
|
||||
return response_object
|
||||
return f(resp, *args, **kwargs)
|
||||
|
||||
return decorated_function
|
||||
|
||||
|
||||
def authenticate_as_admin(f: Callable) -> Callable:
|
||||
@wraps(f)
|
||||
def decorated_function(
|
||||
*args: Any, **kwargs: Any
|
||||
) -> Union[Callable, HttpResponse]:
|
||||
verify_admin = True
|
||||
response_object, resp = verify_user(request, verify_admin)
|
||||
if response_object:
|
||||
return response_object
|
||||
return f(resp, *args, **kwargs)
|
||||
|
||||
return decorated_function
|
@ -2,14 +2,15 @@ from datetime import datetime
|
||||
from typing import Dict, Optional, Union
|
||||
|
||||
import jwt
|
||||
from fittrackee import bcrypt, db
|
||||
from flask import current_app
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.ext.declarative import DeclarativeMeta
|
||||
from sqlalchemy.ext.hybrid import hybrid_property
|
||||
from sqlalchemy.sql.expression import select
|
||||
|
||||
from ..workouts.models import Workout
|
||||
from fittrackee import bcrypt, db
|
||||
from fittrackee.workouts.models import Workout
|
||||
|
||||
from .utils_token import decode_user_token, get_user_token
|
||||
|
||||
BaseModel: DeclarativeMeta = db.Model
|
||||
|
@ -2,6 +2,9 @@ import os
|
||||
import shutil
|
||||
from typing import Any, Dict, Tuple, Union
|
||||
|
||||
from flask import Blueprint, request, send_file
|
||||
from sqlalchemy import exc
|
||||
|
||||
from fittrackee import db
|
||||
from fittrackee.responses import (
|
||||
ForbiddenErrorResponse,
|
||||
@ -11,12 +14,10 @@ from fittrackee.responses import (
|
||||
UserNotFoundErrorResponse,
|
||||
handle_error_and_return_response,
|
||||
)
|
||||
from flask import Blueprint, request, send_file
|
||||
from sqlalchemy import exc
|
||||
from fittrackee.workouts.utils_files import get_absolute_file_path
|
||||
|
||||
from ..workouts.utils_files import get_absolute_file_path
|
||||
from .decorators import authenticate, authenticate_as_admin
|
||||
from .models import User, Workout
|
||||
from .utils import authenticate, authenticate_as_admin
|
||||
|
||||
users_blueprint = Blueprint('users', __name__)
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
import re
|
||||
from datetime import timedelta
|
||||
from functools import wraps
|
||||
from typing import Any, Callable, Optional, Tuple, Union
|
||||
from typing import Optional, Tuple, Union
|
||||
|
||||
import humanize
|
||||
from flask import Request, current_app
|
||||
|
||||
from fittrackee.responses import (
|
||||
ForbiddenErrorResponse,
|
||||
HttpResponse,
|
||||
@ -11,7 +12,6 @@ from fittrackee.responses import (
|
||||
PayloadTooLargeErrorResponse,
|
||||
UnauthorizedErrorResponse,
|
||||
)
|
||||
from flask import Request, current_app, request
|
||||
|
||||
from .models import User
|
||||
|
||||
@ -130,34 +130,6 @@ def verify_user(
|
||||
return None, resp
|
||||
|
||||
|
||||
def authenticate(f: Callable) -> Callable:
|
||||
@wraps(f)
|
||||
def decorated_function(
|
||||
*args: Any, **kwargs: Any
|
||||
) -> Union[Callable, HttpResponse]:
|
||||
verify_admin = False
|
||||
response_object, resp = verify_user(request, verify_admin)
|
||||
if response_object:
|
||||
return response_object
|
||||
return f(resp, *args, **kwargs)
|
||||
|
||||
return decorated_function
|
||||
|
||||
|
||||
def authenticate_as_admin(f: Callable) -> Callable:
|
||||
@wraps(f)
|
||||
def decorated_function(
|
||||
*args: Any, **kwargs: Any
|
||||
) -> Union[Callable, HttpResponse]:
|
||||
verify_admin = True
|
||||
response_object, resp = verify_user(request, verify_admin)
|
||||
if response_object:
|
||||
return response_object
|
||||
return f(resp, *args, **kwargs)
|
||||
|
||||
return decorated_function
|
||||
|
||||
|
||||
def can_view_workout(
|
||||
auth_user_id: int, workout_user_id: int
|
||||
) -> Optional[HttpResponse]:
|
||||
|
9
fittrackee/workouts/exceptions.py
Normal file
9
fittrackee/workouts/exceptions.py
Normal file
@ -0,0 +1,9 @@
|
||||
from fittrackee.exceptions import GenericException
|
||||
|
||||
|
||||
class WorkoutException(GenericException):
|
||||
...
|
||||
|
||||
|
||||
class WorkoutGPXException(GenericException):
|
||||
...
|
@ -3,7 +3,6 @@ import os
|
||||
from typing import Any, Dict, Optional, Union
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from fittrackee import db
|
||||
from sqlalchemy.dialects import postgresql
|
||||
from sqlalchemy.engine.base import Connection
|
||||
from sqlalchemy.event import listens_for
|
||||
@ -13,6 +12,8 @@ from sqlalchemy.orm.mapper import Mapper
|
||||
from sqlalchemy.orm.session import Session, object_session
|
||||
from sqlalchemy.types import JSON, Enum
|
||||
|
||||
from fittrackee import db
|
||||
|
||||
from .utils_files import get_absolute_file_path
|
||||
from .utils_format import convert_in_duration, convert_value_to_integer
|
||||
from .utils_id import encode_uuid
|
||||
|
@ -2,7 +2,8 @@ from typing import Dict
|
||||
|
||||
from flask import Blueprint
|
||||
|
||||
from ..users.utils import authenticate
|
||||
from fittrackee.users.decorators import authenticate
|
||||
|
||||
from .models import Record
|
||||
|
||||
records_blueprint = Blueprint('records', __name__)
|
||||
|
@ -1,5 +1,8 @@
|
||||
from typing import Dict, Union
|
||||
|
||||
from flask import Blueprint, request
|
||||
from sqlalchemy import exc
|
||||
|
||||
from fittrackee import db
|
||||
from fittrackee.responses import (
|
||||
DataNotFoundErrorResponse,
|
||||
@ -7,11 +10,9 @@ from fittrackee.responses import (
|
||||
InvalidPayloadErrorResponse,
|
||||
handle_error_and_return_response,
|
||||
)
|
||||
from flask import Blueprint, request
|
||||
from sqlalchemy import exc
|
||||
from fittrackee.users.decorators import authenticate, authenticate_as_admin
|
||||
from fittrackee.users.models import User
|
||||
|
||||
from ..users.models import User
|
||||
from ..users.utils import authenticate, authenticate_as_admin
|
||||
from .models import Sport
|
||||
|
||||
sports_blueprint = Blueprint('sports', __name__)
|
||||
|
@ -1,6 +1,9 @@
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, Union
|
||||
|
||||
from flask import Blueprint, request
|
||||
from sqlalchemy import func
|
||||
|
||||
from fittrackee import db
|
||||
from fittrackee.responses import (
|
||||
HttpResponse,
|
||||
@ -9,11 +12,9 @@ from fittrackee.responses import (
|
||||
UserNotFoundErrorResponse,
|
||||
handle_error_and_return_response,
|
||||
)
|
||||
from flask import Blueprint, request
|
||||
from sqlalchemy import func
|
||||
from fittrackee.users.decorators import authenticate, authenticate_as_admin
|
||||
from fittrackee.users.models import User
|
||||
|
||||
from ..users.models import User
|
||||
from ..users.utils import authenticate, authenticate_as_admin
|
||||
from .models import Sport, Workout
|
||||
from .utils import get_datetime_with_tz, get_upload_dir_size
|
||||
from .utils_format import convert_timedelta_to_integer
|
||||
|
@ -8,28 +8,21 @@ from uuid import UUID
|
||||
|
||||
import gpxpy.gpx
|
||||
import pytz
|
||||
from fittrackee import appLog, db
|
||||
from flask import current_app
|
||||
from sqlalchemy import exc
|
||||
from staticmap import Line, StaticMap
|
||||
from werkzeug.datastructures import FileStorage
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from ..users.models import User
|
||||
from fittrackee import appLog, db
|
||||
from fittrackee.users.models import User
|
||||
|
||||
from .exceptions import WorkoutException
|
||||
from .models import Sport, Workout, WorkoutSegment
|
||||
from .utils_files import get_absolute_file_path
|
||||
from .utils_gpx import get_gpx_info
|
||||
|
||||
|
||||
class WorkoutException(Exception):
|
||||
def __init__(
|
||||
self, status: str, message: str, e: Optional[Exception] = None
|
||||
) -> None:
|
||||
self.status = status
|
||||
self.message = message
|
||||
self.e = e
|
||||
|
||||
|
||||
def get_datetime_with_tz(
|
||||
timezone: str, workout_date: datetime, gpx_data: Optional[Dict] = None
|
||||
) -> Tuple[Optional[datetime], datetime]:
|
||||
|
@ -3,18 +3,10 @@ from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
import gpxpy.gpx
|
||||
|
||||
from .exceptions import WorkoutGPXException
|
||||
from .utils_weather import get_weather
|
||||
|
||||
|
||||
class WorkoutGPXException(Exception):
|
||||
def __init__(
|
||||
self, status: str, message: str, e: Optional[Exception] = None
|
||||
) -> None:
|
||||
self.status = status
|
||||
self.message = message
|
||||
self.e = e
|
||||
|
||||
|
||||
def open_gpx_file(gpx_file: str) -> Optional[gpxpy.gpx.GPX]:
|
||||
gpx_file = open(gpx_file, 'r') # type: ignore
|
||||
gpx = gpxpy.parse(gpx_file)
|
||||
|
@ -3,9 +3,10 @@ from typing import Dict, Optional
|
||||
|
||||
import forecastio
|
||||
import pytz
|
||||
from fittrackee import appLog
|
||||
from gpxpy.gpx import GPXRoutePoint
|
||||
|
||||
from fittrackee import appLog
|
||||
|
||||
API_KEY = os.getenv('WEATHER_API_KEY')
|
||||
|
||||
|
||||
|
@ -5,6 +5,9 @@ from datetime import datetime, timedelta
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
import requests
|
||||
from flask import Blueprint, Response, current_app, request, send_file
|
||||
from sqlalchemy import exc
|
||||
|
||||
from fittrackee import appLog, db
|
||||
from fittrackee.responses import (
|
||||
DataInvalidPayloadErrorResponse,
|
||||
@ -15,15 +18,13 @@ from fittrackee.responses import (
|
||||
NotFoundErrorResponse,
|
||||
handle_error_and_return_response,
|
||||
)
|
||||
from flask import Blueprint, Response, current_app, request, send_file
|
||||
from sqlalchemy import exc
|
||||
|
||||
from ..users.utils import (
|
||||
from fittrackee.users.decorators import authenticate
|
||||
from fittrackee.users.utils import (
|
||||
User,
|
||||
authenticate,
|
||||
can_view_workout,
|
||||
verify_extension_and_size,
|
||||
)
|
||||
|
||||
from .models import Workout
|
||||
from .utils import (
|
||||
WorkoutException,
|
||||
|
71
poetry.lock
generated
71
poetry.lock
generated
@ -8,17 +8,17 @@ python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "alembic"
|
||||
version = "1.4.3"
|
||||
version = "1.5.1"
|
||||
description = "A database migration tool for SQLAlchemy."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
|
||||
|
||||
[package.dependencies]
|
||||
Mako = "*"
|
||||
python-dateutil = "*"
|
||||
python-editor = ">=0.3"
|
||||
SQLAlchemy = ">=1.1.0"
|
||||
SQLAlchemy = ">=1.3.0"
|
||||
|
||||
[[package]]
|
||||
name = "appdirs"
|
||||
@ -277,7 +277,7 @@ dramatiq = ">=1.5,<2.0"
|
||||
|
||||
[[package]]
|
||||
name = "flask-migrate"
|
||||
version = "2.5.3"
|
||||
version = "2.6.0"
|
||||
description = "SQLAlchemy database migrations for Flask applications using Alembic"
|
||||
category = "main"
|
||||
optional = false
|
||||
@ -302,7 +302,7 @@ SQLAlchemy = ">=0.8.0"
|
||||
|
||||
[[package]]
|
||||
name = "freezegun"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
description = "Let your Python tests travel through time"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@ -368,7 +368,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "importlib-metadata"
|
||||
version = "3.3.0"
|
||||
version = "3.4.0"
|
||||
description = "Read metadata from Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@ -376,8 +376,8 @@ python-versions = ">=3.6"
|
||||
marker = "python_version < \"3.8\""
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
|
||||
testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
|
||||
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
|
||||
testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
|
||||
|
||||
[package.dependencies]
|
||||
zipp = ">=0.5"
|
||||
@ -431,7 +431,7 @@ MarkupSafe = ">=0.23"
|
||||
|
||||
[[package]]
|
||||
name = "mako"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
|
||||
category = "main"
|
||||
optional = false
|
||||
@ -580,7 +580,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.7.3"
|
||||
version = "2.7.4"
|
||||
description = "Pygments is a syntax highlighting package written in Python."
|
||||
category = "dev"
|
||||
optional = false
|
||||
@ -588,7 +588,7 @@ python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "pyjwt"
|
||||
version = "2.0.0"
|
||||
version = "2.0.1"
|
||||
description = "JSON Web Token implementation in Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
@ -679,7 +679,7 @@ python = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "pytest-cov"
|
||||
version = "2.10.1"
|
||||
version = "2.11.1"
|
||||
description = "Pytest plugin for measuring coverage."
|
||||
category = "dev"
|
||||
optional = false
|
||||
@ -689,7 +689,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
testing = ["fields", "hunter", "process-tests (2.0.2)", "six", "pytest-xdist", "virtualenv"]
|
||||
|
||||
[package.dependencies]
|
||||
coverage = ">=4.4"
|
||||
coverage = ">=5.2.1"
|
||||
pytest = ">=4.6"
|
||||
|
||||
[[package]]
|
||||
@ -718,7 +718,7 @@ pytest-metadata = "*"
|
||||
|
||||
[[package]]
|
||||
name = "pytest-isort"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
description = "py.test plugin to check import ordering using isort"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@ -1111,7 +1111,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
[[package]]
|
||||
name = "tqdm"
|
||||
version = "4.55.2"
|
||||
version = "4.56.0"
|
||||
description = "Fast, Extensible Progress Meter"
|
||||
category = "main"
|
||||
optional = false
|
||||
@ -1178,7 +1178,7 @@ testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pyt
|
||||
[metadata]
|
||||
lock-version = "1.0"
|
||||
python-versions = "^3.7"
|
||||
content-hash = "11c3a22ee800d71d1d9804e33e756be81c5b7c56bba85d7de03a0f27acb35148"
|
||||
content-hash = "463a3452e6d951925f8d8747dc2fca0016de60c587961006e2dff2d2f8c59fd0"
|
||||
|
||||
[metadata.files]
|
||||
alabaster = [
|
||||
@ -1186,8 +1186,8 @@ alabaster = [
|
||||
{file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"},
|
||||
]
|
||||
alembic = [
|
||||
{file = "alembic-1.4.3-py2.py3-none-any.whl", hash = "sha256:4e02ed2aa796bd179965041afa092c55b51fb077de19d61835673cc80672c01c"},
|
||||
{file = "alembic-1.4.3.tar.gz", hash = "sha256:5334f32314fb2a56d86b4c4dd1ae34b08c03cae4cb888bc699942104d66bc245"},
|
||||
{file = "alembic-1.5.1-py2.py3-none-any.whl", hash = "sha256:944f8d541d204f3ae22de17ebcfdd0da9539074f1d2fee4eb42735039b3072d8"},
|
||||
{file = "alembic-1.5.1.tar.gz", hash = "sha256:52d1d48109f17959982779e3c4b5cdeca701e449897bacb75bab173bd6ba984e"},
|
||||
]
|
||||
appdirs = [
|
||||
{file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
|
||||
@ -1365,16 +1365,16 @@ flask-dramatiq = [
|
||||
{file = "flask_dramatiq-0.6.0-py3-none-any.whl", hash = "sha256:7d4a9289721577f726183f7c44c6713a16bbdff54b946f27abc2ffcc65768adf"},
|
||||
]
|
||||
flask-migrate = [
|
||||
{file = "Flask-Migrate-2.5.3.tar.gz", hash = "sha256:a69d508c2e09d289f6e55a417b3b8c7bfe70e640f53d2d9deb0d056a384f37ee"},
|
||||
{file = "Flask_Migrate-2.5.3-py2.py3-none-any.whl", hash = "sha256:4dc4a5cce8cbbb06b8dc963fd86cf8136bd7d875aabe2d840302ea739b243732"},
|
||||
{file = "Flask-Migrate-2.6.0.tar.gz", hash = "sha256:8626af845e6071ef80c70b0dc16d373f761c981f0ad61bb143a529cab649e725"},
|
||||
{file = "Flask_Migrate-2.6.0-py2.py3-none-any.whl", hash = "sha256:c1601dfd46b9204233935e5d73473cd7fa959db7a4b0e894c7aa7a9e8aeebf0e"},
|
||||
]
|
||||
flask-sqlalchemy = [
|
||||
{file = "Flask-SQLAlchemy-2.4.4.tar.gz", hash = "sha256:bfc7150eaf809b1c283879302f04c42791136060c6eeb12c0c6674fb1291fae5"},
|
||||
{file = "Flask_SQLAlchemy-2.4.4-py2.py3-none-any.whl", hash = "sha256:05b31d2034dd3f2a685cbbae4cfc4ed906b2a733cff7964ada450fd5e462b84e"},
|
||||
]
|
||||
freezegun = [
|
||||
{file = "freezegun-1.0.0-py2.py3-none-any.whl", hash = "sha256:02b35de52f4699a78f6ac4518e4cd3390dddc43b0aeb978335a8f270a2d9668b"},
|
||||
{file = "freezegun-1.0.0.tar.gz", hash = "sha256:1cf08e441f913ff5e59b19cc065a8faa9dd1ddc442eaf0375294f344581a0643"},
|
||||
{file = "freezegun-1.1.0-py2.py3-none-any.whl", hash = "sha256:2ae695f7eb96c62529f03a038461afe3c692db3465e215355e1bb4b0ab408712"},
|
||||
{file = "freezegun-1.1.0.tar.gz", hash = "sha256:177f9dd59861d871e27a484c3332f35a6e3f5d14626f2bf91be37891f18927f3"},
|
||||
]
|
||||
gpxpy = [
|
||||
{file = "gpxpy-1.3.4.tar.gz", hash = "sha256:4a0f072ae5bdf9270c7450e452f93a6c5c91d888114e8d78868a8f163b0dbb15"},
|
||||
@ -1396,8 +1396,8 @@ imagesize = [
|
||||
{file = "imagesize-1.2.0.tar.gz", hash = "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"},
|
||||
]
|
||||
importlib-metadata = [
|
||||
{file = "importlib_metadata-3.3.0-py3-none-any.whl", hash = "sha256:bf792d480abbd5eda85794e4afb09dd538393f7d6e6ffef6e9f03d2014cf9450"},
|
||||
{file = "importlib_metadata-3.3.0.tar.gz", hash = "sha256:5c5a2720817414a6c41f0a49993908068243ae02c1635a228126519b509c8aed"},
|
||||
{file = "importlib_metadata-3.4.0-py3-none-any.whl", hash = "sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771"},
|
||||
{file = "importlib_metadata-3.4.0.tar.gz", hash = "sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d"},
|
||||
]
|
||||
iniconfig = [
|
||||
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
||||
@ -1416,8 +1416,7 @@ jinja2 = [
|
||||
{file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"},
|
||||
]
|
||||
mako = [
|
||||
{file = "Mako-1.1.3-py2.py3-none-any.whl", hash = "sha256:93729a258e4ff0747c876bd9e20df1b9758028946e976324ccd2d68245c7b6a9"},
|
||||
{file = "Mako-1.1.3.tar.gz", hash = "sha256:8195c8c1400ceb53496064314c6736719c6f25e7479cd24c77be3d9361cddc27"},
|
||||
{file = "Mako-1.1.4.tar.gz", hash = "sha256:17831f0b7087c313c0ffae2bcbbd3c1d5ba9eeac9c38f2eb7b50e8c99fe9d5ab"},
|
||||
]
|
||||
markupsafe = [
|
||||
{file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"},
|
||||
@ -1575,12 +1574,12 @@ pyflakes = [
|
||||
{file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"},
|
||||
]
|
||||
pygments = [
|
||||
{file = "Pygments-2.7.3-py3-none-any.whl", hash = "sha256:f275b6c0909e5dafd2d6269a656aa90fa58ebf4a74f8fcf9053195d226b24a08"},
|
||||
{file = "Pygments-2.7.3.tar.gz", hash = "sha256:ccf3acacf3782cbed4a989426012f1c535c9a90d3a7fc3f16d231b9372d2b716"},
|
||||
{file = "Pygments-2.7.4-py3-none-any.whl", hash = "sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435"},
|
||||
{file = "Pygments-2.7.4.tar.gz", hash = "sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337"},
|
||||
]
|
||||
pyjwt = [
|
||||
{file = "PyJWT-2.0.0-py3-none-any.whl", hash = "sha256:5c2ff2eb27d7e342dfc3cafcc16412781f06db2690fbef81922b0172598f085b"},
|
||||
{file = "PyJWT-2.0.0.tar.gz", hash = "sha256:7a2b271c6dac2fda9e0c33d176c4253faba2c6c6b3a99c7f28a32c3c97522779"},
|
||||
{file = "PyJWT-2.0.1-py3-none-any.whl", hash = "sha256:b70b15f89dc69b993d8a8d32c299032d5355c82f9b5b7e851d1a6d706dffe847"},
|
||||
{file = "PyJWT-2.0.1.tar.gz", hash = "sha256:a5c70a06e1f33d81ef25eecd50d50bd30e34de1ca8b2b9fa3fe0daaabcf69bf7"},
|
||||
]
|
||||
pyopenssl = [
|
||||
{file = "pyOpenSSL-20.0.1-py2.py3-none-any.whl", hash = "sha256:818ae18e06922c066f777a33f1fca45786d85edfe71cd043de6379337a7f274b"},
|
||||
@ -1602,8 +1601,8 @@ pytest-black = [
|
||||
{file = "pytest-black-0.3.12.tar.gz", hash = "sha256:1d339b004f764d6cd0f06e690f6dd748df3d62e6fe1a692d6a5500ac2c5b75a5"},
|
||||
]
|
||||
pytest-cov = [
|
||||
{file = "pytest-cov-2.10.1.tar.gz", hash = "sha256:47bd0ce14056fdd79f93e1713f88fad7bdcc583dcd7783da86ef2f085a0bb88e"},
|
||||
{file = "pytest_cov-2.10.1-py2.py3-none-any.whl", hash = "sha256:45ec2d5182f89a81fc3eb29e3d1ed3113b9e9a873bcddb2a71faaab066110191"},
|
||||
{file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"},
|
||||
{file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"},
|
||||
]
|
||||
pytest-flake8 = [
|
||||
{file = "pytest-flake8-1.0.7.tar.gz", hash = "sha256:f0259761a903563f33d6f099914afef339c085085e643bee8343eb323b32dd6b"},
|
||||
@ -1614,8 +1613,8 @@ pytest-html = [
|
||||
{file = "pytest_html-3.1.1-py3-none-any.whl", hash = "sha256:b7f82f123936a3f4d2950bc993c2c1ca09ce262c9ae12f9ac763a2401380b455"},
|
||||
]
|
||||
pytest-isort = [
|
||||
{file = "pytest-isort-1.2.0.tar.gz", hash = "sha256:f0fcf9674f3a627b36e07466d335e82b0f7c4f9e0f7ec39f2a1750b0189d5371"},
|
||||
{file = "pytest_isort-1.2.0-py3-none-any.whl", hash = "sha256:2c6a1d210e8c478e418ab25df2408c235c97b1b8958fb0b139d790d0ec246f58"},
|
||||
{file = "pytest-isort-1.3.0.tar.gz", hash = "sha256:46a12331a701e2f21d48548b2828c8b0a7956dbf1cd5347163f537deb24332dd"},
|
||||
{file = "pytest_isort-1.3.0-py3-none-any.whl", hash = "sha256:074255ad393088a2daee6ca7f2305b7b86358ff632f62302896d8d4b2b339107"},
|
||||
]
|
||||
pytest-metadata = [
|
||||
{file = "pytest-metadata-1.11.0.tar.gz", hash = "sha256:71b506d49d34e539cc3cfdb7ce2c5f072bea5c953320002c95968e0238f8ecf1"},
|
||||
@ -1813,8 +1812,8 @@ toml = [
|
||||
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
|
||||
]
|
||||
tqdm = [
|
||||
{file = "tqdm-4.55.2-py2.py3-none-any.whl", hash = "sha256:54985cecd51071f3faa2a07a862c24be1426f925353d82b3dde03f7609d63248"},
|
||||
{file = "tqdm-4.55.2.tar.gz", hash = "sha256:86ca00c4942c3b3dc7ed31bae44cd2db38ef85ca05a7920f6a6c52ad7fcac904"},
|
||||
{file = "tqdm-4.56.0-py2.py3-none-any.whl", hash = "sha256:4621f6823bab46a9cc33d48105753ccbea671b68bab2c50a9f0be23d4065cb5a"},
|
||||
{file = "tqdm-4.56.0.tar.gz", hash = "sha256:fe3d08dd00a526850568d542ff9de9bbc2a09a791da3c334f3213d8d0bbbca65"},
|
||||
]
|
||||
typed-ast = [
|
||||
{file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70"},
|
||||
|
@ -27,7 +27,7 @@ dramatiq = {extras = ["redis"], version = "^1.10.0"}
|
||||
flask = "^1.1"
|
||||
flask-bcrypt = "^0.7.1"
|
||||
flask-dramatiq = "^0.6.0"
|
||||
flask-migrate = "^2.5"
|
||||
flask-migrate = "^2.6"
|
||||
gpxpy = "=1.3.4"
|
||||
gunicorn = "^20.0"
|
||||
humanize = "^3.2.0"
|
||||
@ -37,18 +37,18 @@ python-forecastio = "^1.4"
|
||||
pytz = "^2020.5"
|
||||
shortuuid = "^1.0.1"
|
||||
staticmap = "^0.5.4"
|
||||
tqdm = "^4.55"
|
||||
tqdm = "^4.56"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
black = "^20.8b1"
|
||||
freezegun = "^1.0.0"
|
||||
freezegun = "^1.1"
|
||||
mypy = "^0.790"
|
||||
pyopenssl = "^20.0"
|
||||
pytest = "^6.2"
|
||||
pytest-black = "^0.3.12"
|
||||
pytest-cov = "^2.10"
|
||||
pytest-cov = "^2.11"
|
||||
pytest-flake8 = "^1.0"
|
||||
pytest-isort = "^1.1"
|
||||
pytest-isort = "^1.3"
|
||||
pytest-runner = "^5.2"
|
||||
pytest-selenium = "^2.0.1"
|
||||
recommonmark = "^0.7"
|
||||
@ -70,7 +70,6 @@ include = ".py$"
|
||||
exclude = "migrations"
|
||||
|
||||
[tool.isort]
|
||||
known_third_party = "fittrackee"
|
||||
multi_line_output = 3
|
||||
include_trailing_comma = true
|
||||
force_grid_wrap = 0
|
||||
|
Loading…
Reference in New Issue
Block a user