Merge branch 'api-refactor'

This commit is contained in:
Sam 2021-01-20 17:07:19 +01:00
commit 7a1f44ccc5
58 changed files with 209 additions and 161 deletions

View File

@ -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()

View File

@ -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')

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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')

View File

@ -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:

View File

@ -0,0 +1,2 @@
class InvalidEmailUrlScheme(Exception):
...

View File

@ -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
View 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

View File

@ -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 []

View File

@ -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(

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -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,

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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,

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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())

View File

@ -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,

View 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

View File

@ -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

View File

@ -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__)

View File

@ -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]:

View File

@ -0,0 +1,9 @@
from fittrackee.exceptions import GenericException
class WorkoutException(GenericException):
...
class WorkoutGPXException(GenericException):
...

View File

@ -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

View File

@ -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__)

View File

@ -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__)

View File

@ -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

View File

@ -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]:

View File

@ -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)

View 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')

View File

@ -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
View File

@ -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"},

View File

@ -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