Merge pull request #150 from SamR1/improve-app-init

Improve application initialization
This commit is contained in:
Sam 2022-02-13 13:38:55 +01:00 committed by GitHub
commit f3e0f5e4b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 296 additions and 264 deletions

View File

@ -93,7 +93,6 @@ firefox:
- poetry config virtualenvs.create false
- poetry install --no-interaction --quiet
- flask db upgrade --directory fittrackee/migrations
- flask init-data
- setsid nohup flask run --with-threads -h 0.0.0.0 -p 5000 >> nohup.out 2>&1 &
- export TEST_APP_URL=http://$(hostname --ip-address):5000
- sleep 5

View File

@ -59,6 +59,9 @@ docker-serve-client:
docker-compose -f docker-compose-dev.yml up -d fittrackee_client
docker-compose -f docker-compose-dev.yml exec fittrackee_client yarn serve
docker-set-admin:
docker-compose -f docker-compose-dev.yml exec fittrackee docker/set-admin.sh $(USERNAME)
docker-shell:
docker-compose -f docker-compose-dev.yml exec fittrackee docker/shell.sh
@ -85,15 +88,10 @@ html:
install-db:
psql -U postgres -f db/create.sql
$(FLASK) db upgrade --directory $(MIGRATIONS)
$(FLASK) init-data
init-app-config:
$(FLASK) init-app-config
init-db:
$(FLASK) drop-db
$(FLASK) db upgrade --directory $(MIGRATIONS)
$(FLASK) init-data
install: install-client install-python
@ -133,9 +131,6 @@ mail:
migrate-db:
$(FLASK) db migrate --directory $(MIGRATIONS)
recalculate:
$(FLASK) recalculate
revision:
$(FLASK) db revision --directory $(MIGRATIONS) --message $(MIGRATION_MESSAGE)
@ -166,6 +161,9 @@ serve-python-dev:
echo 'Running on https://$(HOST):$(PORT)'
$(FLASK) run --with-threads -h $(HOST) -p $(PORT) --cert=adhoc
set-admin:
$(FLASK) set-admin $(USERNAME)
test-e2e: init-db
$(PYTEST) e2e --driver firefox $(PYTEST_ARGS)

View File

@ -6,4 +6,3 @@ source .env.docker
flask drop-db
flask db upgrade --directory fittrackee/migrations
flask init-data

7
docker/set-admin.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
set -e
cd /usr/src/app
source .env.docker
flask set-admin $1

View File

@ -269,19 +269,12 @@ For instance, copy and update ``.env`` file from ``.env.example`` and source the
$ nano .env
$ source .env
- Upgrade database schema
- Initialize database schema
.. code-block:: bash
$ fittrackee_upgrade_db
- Initialize database
.. code-block:: bash
$ fittrackee_init_data
- Start the application
.. code-block:: bash
@ -297,6 +290,14 @@ For instance, copy and update ``.env`` file from ``.env.example`` and source the
.. note::
| To start application and workers with **systemd** service, see `Deployment <installation.html#deployment>`__
- Open http://localhost:3000 and register
- To set admin rights to the newly created account, use the following command:
.. code:: bash
$ fittrackee_set_admin <username>
From sources
^^^^^^^^^^^^
@ -349,8 +350,13 @@ Dev environment
$ make run-workers
Open http://localhost:3000 and log in (the email is ``admin@example.com``
and the password ``mpwoadmin``) or register
- Open http://localhost:3000 and register
- To set admin rights to the newly created account, use the following command:
.. code:: bash
$ make set-admin USERNAME=<username>
Production environment
@ -390,9 +396,13 @@ Production environment
$ make run
Open http://localhost:5000, log in as admin (the email is
``admin@example.com`` and the password ``mpwoadmin``) and change the
password
- Open http://localhost:5000 and register
- To set admin rights to the newly created account, use the following command:
.. code:: bash
$ make set-admin USERNAME=<username>
Upgrade
@ -642,10 +652,16 @@ installing **FitTrackee** from **sources**.
$ cd FitTrackee
$ make docker-build docker-run docker-init
Open http://localhost:5000, log in as admin (the email is `admin@example.com` and the password `mpwoadmin`) or register.
Open http://localhost:5000 and register.
Open http://localhost:8025 to access `MailHog interface <https://github.com/mailhog/MailHog>`_ (email testing tool)
- To set admin rights to the newly created account, use the following command:
.. code:: bash
$ make docker-set-admin USERNAME=<username>
- To stop **Fittrackee**:
.. code-block:: bash
@ -683,8 +699,7 @@ Development
$ make docker-serve-client
Open http://localhost:3000 and log in (the email is ``admin@example.com``
and the password ``mpwoadmin``) or register
Open http://localhost:3000
.. note::
Some environment variables need to be updated like `UI_URL`

View File

@ -527,18 +527,12 @@ $ <span class="nb">source</span> .env
</pre></div>
</div>
<ul class="simple">
<li><p>Upgrade database schema</p></li>
<li><p>Initialize database schema</p></li>
</ul>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ fittrackee_upgrade_db
</pre></div>
</div>
<ul class="simple">
<li><p>Initialize database</p></li>
</ul>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ fittrackee_init_data
</pre></div>
</div>
<ul class="simple">
<li><p>Start the application</p></li>
</ul>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ fittrackee
@ -556,6 +550,13 @@ $ <span class="nb">source</span> .env
<div class="line">To start application and workers with <strong>systemd</strong> service, see <a class="reference external" href="installation.html#deployment">Deployment</a></div>
</div>
</div>
<ul class="simple">
<li><p>Open <a class="reference external" href="http://localhost:3000">http://localhost:3000</a> and register</p></li>
<li><p>To set admin rights to the newly created account, use the following command:</p></li>
</ul>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ fittrackee_set_admin &lt;username&gt;
</pre></div>
</div>
</section>
<section id="from-sources">
<h3>From sources<a class="headerlink" href="#from-sources" title="Permalink to this headline"></a></h3>
@ -606,8 +607,13 @@ $ make install-db
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ make run-workers
</pre></div>
</div>
<p>Open <a class="reference external" href="http://localhost:3000">http://localhost:3000</a> and log in (the email is <code class="docutils literal notranslate"><span class="pre">admin&#64;example.com</span></code>
and the password <code class="docutils literal notranslate"><span class="pre">mpwoadmin</span></code>) or register</p>
<ul class="simple">
<li><p>Open <a class="reference external" href="http://localhost:3000">http://localhost:3000</a> and register</p></li>
<li><p>To set admin rights to the newly created account, use the following command:</p></li>
</ul>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ make set-admin <span class="nv">USERNAME</span><span class="o">=</span>&lt;username&gt;
</pre></div>
</div>
</section>
<section id="production-environment">
<h4>Production environment<a class="headerlink" href="#production-environment" title="Permalink to this headline"></a></h4>
@ -647,9 +653,13 @@ database credentials</strong>):</p></li>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ make run
</pre></div>
</div>
<p>Open <a class="reference external" href="http://localhost:5000">http://localhost:5000</a>, log in as admin (the email is
<code class="docutils literal notranslate"><span class="pre">admin&#64;example.com</span></code> and the password <code class="docutils literal notranslate"><span class="pre">mpwoadmin</span></code>) and change the
password</p>
<ul class="simple">
<li><p>Open <a class="reference external" href="http://localhost:5000">http://localhost:5000</a> and register</p></li>
<li><p>To set admin rights to the newly created account, use the following command:</p></li>
</ul>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ make set-admin <span class="nv">USERNAME</span><span class="o">=</span>&lt;username&gt;
</pre></div>
</div>
</section>
</section>
</section>
@ -892,9 +902,15 @@ $ <span class="nb">cd</span> FitTrackee
$ make docker-build docker-run docker-init
</pre></div>
</div>
<p>Open <a class="reference external" href="http://localhost:5000">http://localhost:5000</a>, log in as admin (the email is <cite>admin&#64;example.com</cite> and the password <cite>mpwoadmin</cite>) or register.</p>
<p>Open <a class="reference external" href="http://localhost:5000">http://localhost:5000</a> and register.</p>
<p>Open <a class="reference external" href="http://localhost:8025">http://localhost:8025</a> to access <a class="reference external" href="https://github.com/mailhog/MailHog">MailHog interface</a> (email testing tool)</p>
<ul class="simple">
<li><p>To set admin rights to the newly created account, use the following command:</p></li>
</ul>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ make docker-set-admin <span class="nv">USERNAME</span><span class="o">=</span>&lt;username&gt;
</pre></div>
</div>
<ul class="simple">
<li><p>To stop <strong>Fittrackee</strong>:</p></li>
</ul>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ make docker-stop
@ -930,8 +946,7 @@ $ make docker-build docker-run docker-init
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ make docker-serve-client
</pre></div>
</div>
<p>Open <a class="reference external" href="http://localhost:3000">http://localhost:3000</a> and log in (the email is <code class="docutils literal notranslate"><span class="pre">admin&#64;example.com</span></code>
and the password <code class="docutils literal notranslate"><span class="pre">mpwoadmin</span></code>) or register</p>
<p>Open <a class="reference external" href="http://localhost:3000">http://localhost:3000</a></p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>Some environment variables need to be updated like <cite>UI_URL</cite></p>

File diff suppressed because one or more lines are too long

View File

@ -269,19 +269,12 @@ For instance, copy and update ``.env`` file from ``.env.example`` and source the
$ nano .env
$ source .env
- Upgrade database schema
- Initialize database schema
.. code-block:: bash
$ fittrackee_upgrade_db
- Initialize database
.. code-block:: bash
$ fittrackee_init_data
- Start the application
.. code-block:: bash
@ -297,6 +290,14 @@ For instance, copy and update ``.env`` file from ``.env.example`` and source the
.. note::
| To start application and workers with **systemd** service, see `Deployment <installation.html#deployment>`__
- Open http://localhost:3000 and register
- To set admin rights to the newly created account, use the following command:
.. code:: bash
$ fittrackee_set_admin <username>
From sources
^^^^^^^^^^^^
@ -349,8 +350,13 @@ Dev environment
$ make run-workers
Open http://localhost:3000 and log in (the email is ``admin@example.com``
and the password ``mpwoadmin``) or register
- Open http://localhost:3000 and register
- To set admin rights to the newly created account, use the following command:
.. code:: bash
$ make set-admin USERNAME=<username>
Production environment
@ -390,9 +396,13 @@ Production environment
$ make run
Open http://localhost:5000, log in as admin (the email is
``admin@example.com`` and the password ``mpwoadmin``) and change the
password
- Open http://localhost:5000 and register
- To set admin rights to the newly created account, use the following command:
.. code:: bash
$ make set-admin USERNAME=<username>
Upgrade
@ -642,10 +652,16 @@ installing **FitTrackee** from **sources**.
$ cd FitTrackee
$ make docker-build docker-run docker-init
Open http://localhost:5000, log in as admin (the email is `admin@example.com` and the password `mpwoadmin`) or register.
Open http://localhost:5000 and register.
Open http://localhost:8025 to access `MailHog interface <https://github.com/mailhog/MailHog>`_ (email testing tool)
- To set admin rights to the newly created account, use the following command:
.. code:: bash
$ make docker-set-admin USERNAME=<username>
- To stop **Fittrackee**:
.. code-block:: bash
@ -683,8 +699,7 @@ Development
$ make docker-serve-client
Open http://localhost:3000 and log in (the email is ``admin@example.com``
and the password ``mpwoadmin``) or register
Open http://localhost:3000
.. note::
Some environment variables need to be updated like `UI_URL`

View File

@ -1,4 +1,9 @@
from .utils import TEST_URL, assert_navbar, login_valid_user
from .utils import (
TEST_URL,
assert_navbar,
login_valid_user,
register_valid_user_and_logout,
)
URL = f'{TEST_URL}/login'
@ -31,11 +36,7 @@ class TestLogin:
assert 'Forgot password?' in links[1].text
def test_user_can_log_in(self, selenium):
user = {
'username': 'admin',
'email': 'admin@example.com',
'password': 'mpwoadmin',
}
user = register_valid_user_and_logout(selenium)
login_valid_user(selenium, user)

View File

@ -4,6 +4,7 @@ from .utils import (
random_string,
register,
register_valid_user,
register_valid_user_and_logout,
)
URL = f'{TEST_URL}/register'
@ -57,30 +58,20 @@ class TestRegistration:
def test_user_can_not_register_if_username_is_already_taken(
self, selenium
):
user_name = random_string()
user_infos = {
'username': 'admin',
'email': f'{user_name}@example.com',
'password': 'p@ssw0rd',
'password_conf': 'p@ssw0rd',
}
user = register_valid_user_and_logout(selenium)
user['email'] = f'{random_string()}@example.com'
register(selenium, user_infos)
register(selenium, user)
assert selenium.current_url == URL
errors = selenium.find_element_by_class_name('error-message').text
assert 'Sorry, that user already exists.' in errors
def test_user_can_not_register_if_email_is_already_taken(self, selenium):
user_name = random_string()
user_infos = {
'username': user_name,
'email': 'admin@example.com',
'password': 'p@ssw0rd',
'password_conf': 'p@ssw0rd',
}
user = register_valid_user_and_logout(selenium)
user['username'] = random_string()
register(selenium, user_infos)
register(selenium, user)
assert selenium.current_url == URL
errors = selenium.find_element_by_class_name('error-message').text

View File

@ -54,6 +54,24 @@ def register_valid_user(selenium):
return user
def register_valid_user_and_logout(selenium):
user_name = random_string()
user = {
'username': user_name,
'email': f'{user_name}@example.com',
'password': 'p@ssw0rd',
'password_conf': 'p@ssw0rd',
}
register(selenium, user)
WebDriverWait(selenium, 15).until(EC.url_changes(f"{TEST_URL}/register"))
user_menu = selenium.find_element_by_class_name('nav-items-user-menu')
logout_link = user_menu.find_elements_by_class_name('nav-item')[2]
logout_link.click()
selenium.implicitly_wait(1)
return user
def login_valid_user(selenium, user):
login(selenium, user)
WebDriverWait(selenium, 10).until(EC.url_changes(f"{TEST_URL}/login"))

View File

@ -51,16 +51,16 @@ def create_app() -> Flask:
email_service.init_email(app)
# get configuration from database
from .application.models import AppConfig
from .application.utils import init_config, update_app_config_from_database
from .application.utils import (
get_or_init_config,
update_app_config_from_database,
)
with app.app_context():
# Note: check if "app_config" table exist to avoid errors when
# dropping tables on dev environments
if db.engine.dialect.has_table(db.engine.connect(), 'app_config'):
db_app_config = AppConfig.query.one_or_none()
if not db_app_config:
_, db_app_config = init_config()
db_app_config = get_or_init_config()
update_app_config_from_database(app, db_app_config)
from .application.app_config import config_blueprint # noqa

View File

@ -4,17 +4,15 @@ import os
import shutil
from typing import Dict, Optional
import click
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 fittrackee.users.exceptions import UserNotFoundException
from fittrackee.users.utils import set_admin_rights
HOST = os.getenv('HOST', '0.0.0.0')
PORT = os.getenv('PORT', '5000')
@ -52,7 +50,7 @@ def upgrade_db() -> None:
@app.cli.command('drop-db')
def drop_db() -> None:
"""Empty database for dev environments."""
"""Empty database and delete uploaded files for dev environments."""
db.engine.execute("DROP TABLE IF EXISTS alembic_version;")
db.drop_all()
db.session.commit()
@ -61,42 +59,15 @@ def drop_db() -> None:
print('Uploaded files deleted.')
@app.cli.command('init-data')
def init_data() -> None:
"""Init the database and application config."""
init_database(app)
@app.cli.command()
def recalculate() -> None:
print("Starting workouts data refresh")
workouts = (
Workout.query.filter(Workout.gpx != None) # noqa
.order_by(Workout.workout_date.asc()) # noqa
.all()
)
if len(workouts) == 0:
print('➡️ no workouts to upgrade.')
return None
pbar = tqdm(workouts)
for workout in pbar:
update_workout(workout)
pbar.set_postfix(activitiy_id=workout.id)
db.session.commit()
@app.cli.command('init-app-config')
def init_app_config() -> None:
"""Init application configuration."""
print("Init application configuration")
config_created, _ = init_config()
if config_created:
print("Creation done!")
else:
print(
"Application configuration already existing in database. "
"Please use web application to update it."
)
@app.cli.command('set-admin')
@click.argument('username')
def set_admin(username: str) -> None:
"""Set admin rights for given user"""
try:
set_admin_rights(username)
print(f"User '{username}' updated.")
except UserNotFoundException:
print(f"User '{username}' not found.")
def main() -> None:

View File

@ -1,42 +1,28 @@
import os
from typing import Dict, List, Tuple
from typing import Dict, List
from flask import Flask
from fittrackee import db
from fittrackee.users.models import User
from .models import AppConfig
MAX_FILE_SIZE = 1 * 1024 * 1024 # 1MB
def init_config() -> Tuple[bool, AppConfig]:
def get_or_init_config() -> AppConfig:
"""
init application configuration if not existing in database
Note: get some configuration values from env variables
(for FitTrackee versions prior to v0.3.0)
Init application configuration.
"""
existing_config = AppConfig.query.one_or_none()
nb_users = User.query.count()
if not existing_config:
if existing_config:
return existing_config
config = AppConfig()
config.max_users = (
nb_users
if os.getenv('REACT_APP_ALLOW_REGISTRATION') == "false"
else 0
)
config.max_single_file_size = os.environ.get(
'REACT_APP_MAX_SINGLE_FILE_SIZE', MAX_FILE_SIZE
)
config.max_zip_file_size = os.environ.get(
'REACT_APP_MAX_ZIP_FILE_SIZE', MAX_FILE_SIZE * 10
)
config.max_users = 0 # no limitation
config.max_single_file_size = MAX_FILE_SIZE
config.max_zip_file_size = MAX_FILE_SIZE * 10
db.session.add(config)
db.session.commit()
return True, config
return False, existing_config
return config
def update_app_config_from_database(

View File

@ -54,8 +54,6 @@ class DevelopmentConfig(BaseConfig):
DEBUG = True
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')
SECRET_KEY = 'development key'
USERNAME = 'admin'
PASSWORD = 'default'
BCRYPT_LOG_ROUNDS = 4
DRAMATIQ_BROKER_URL = os.getenv('REDIS_URL', 'redis://')
@ -67,8 +65,6 @@ class TestingConfig(BaseConfig):
TESTING = True
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_TEST_URL')
SECRET_KEY = 'test key'
USERNAME = 'admin'
PASSWORD = 'default'
BCRYPT_LOG_ROUNDS = 4
TOKEN_EXPIRATION_DAYS = 0
TOKEN_EXPIRATION_SECONDS = 3

View File

@ -1,23 +0,0 @@
from flask import Flask
from fittrackee import db
from fittrackee.application.utils import (
init_config,
update_app_config_from_database,
)
from fittrackee.users.models import User
def init_database(app: Flask) -> None:
"""Init the database."""
admin = User(
username='admin', email='admin@example.com', password='mpwoadmin'
)
admin.admin = True
admin.timezone = 'Europe/Paris'
db.session.add(admin)
db.session.commit()
_, db_app_config = init_config()
update_app_config_from_database(app, db_app_config)
print('Initial data stored in database.')

View File

@ -0,0 +1,48 @@
from flask import Flask
from fittrackee.application.models import AppConfig
from fittrackee.application.utils import get_or_init_config
class TestGetOrInitAppConfig:
def test_it_creates_app_config(self, app_no_config: Flask) -> None:
get_or_init_config()
assert AppConfig.query.count() == 1
def test_it_inits_max_users_with_default_value(
self, app_no_config: Flask
) -> None:
get_or_init_config()
app_config = AppConfig.query.first()
assert app_config.max_users == 0
def test_it_inits_max_single_file_size_with_default_value(
self, app_no_config: Flask
) -> None:
get_or_init_config()
app_config = AppConfig.query.first()
assert app_config.max_single_file_size == 1048576 # 1MB
def test_it_inits_max_zip_file_size_with_default_value(
self, app_no_config: Flask
) -> None:
get_or_init_config()
app_config = AppConfig.query.first()
assert app_config.max_zip_file_size == 10485760 # 10MB
def test_it_inits_gpx_limit_import_with_default_value(
self, app_no_config: Flask
) -> None:
get_or_init_config()
app_config = AppConfig.query.first()
assert app_config.gpx_limit_import == 10
def test_it_returns_existing_config(self, app: Flask) -> None:
app_config = get_or_init_config()
assert app_config.max_users == 100

View File

@ -0,0 +1,30 @@
import pytest
from flask import Flask
from fittrackee.users.exceptions import UserNotFoundException
from fittrackee.users.models import User
from fittrackee.users.utils import set_admin_rights
from ..utils import random_string
class TestSetAdminRights:
def test_it_raises_exception_if_user_does_not_exist(
self, app: Flask
) -> None:
with pytest.raises(UserNotFoundException):
set_admin_rights(random_string())
def test_it_sets_admin_right_for_a_given_user(
self, app: Flask, user_1: User
) -> None:
set_admin_rights(user_1.username)
assert user_1.admin is True
def test_it_does_not_raise_exception_when_user_has_already_admin_right(
self, app: Flask, user_1_admin: User
) -> None:
set_admin_rights(user_1_admin.username)
assert user_1_admin.admin is True

12
fittrackee/tests/utils.py Normal file
View File

@ -0,0 +1,12 @@
import random
import string
from typing import Optional
def random_string(length: Optional[int] = None) -> str:
if length is None:
length = 10
return ''.join(
random.choice(string.ascii_letters + string.digits)
for _ in range(length)
)

View File

@ -7,7 +7,6 @@ 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 ..api_test_case import ApiTestCaseMixin
from .utils import get_random_short_id, post_an_workout
@ -619,37 +618,3 @@ class TestEditWorkoutWithoutGpx(ApiTestCaseMixin):
assert response.status_code == 404
assert 'not found' in data['status']
assert len(data['data']['workouts']) == 0
class TestRefreshWorkoutWithGpx:
def test_refresh_an_workout_with_gpx(
self,
app: Flask,
user_1: User,
sport_1_cycling: Sport,
sport_2_running: Sport,
gpx_file: str,
) -> None:
token, workout_short_id = post_an_workout(app, gpx_file)
workout_uuid = decode_short_id(workout_short_id)
client = app.test_client()
# Edit some workout data
workout = Workout.query.filter_by(uuid=workout_uuid).first()
workout.ascent = 1000
workout.min_alt = -100
response = client.patch(
f'/api/workouts/{workout_short_id}',
content_type='application/json',
data=json.dumps(dict(refresh=True)),
headers=dict(Authorization=f'Bearer {token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert len(data['data']['workouts']) == 1
assert 1 == data['data']['workouts'][0]['sport_id']
assert 0.4 == data['data']['workouts'][0]['ascent']
assert 975.0 == data['data']['workouts'][0]['min_alt']

View File

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

View File

@ -3,12 +3,14 @@ from typing import Optional, Tuple
from flask import Request
from fittrackee import db
from fittrackee.responses import (
ForbiddenErrorResponse,
HttpResponse,
UnauthorizedErrorResponse,
)
from .exceptions import UserNotFoundException
from .models import User
@ -84,3 +86,11 @@ def can_view_workout(
if auth_user_id != workout_user_id:
return ForbiddenErrorResponse()
return None
def set_admin_rights(username: str) -> None:
user = User.query.filter_by(username=username).first()
if not user:
raise UserNotFoundException()
user.admin = True
db.session.commit()

View File

@ -189,14 +189,12 @@ def edit_workout(
workout: Workout, workout_data: Dict, auth_user: User
) -> Workout:
"""
Edit an workout
Edit a workout
Note: the gpx file is NOT modified
In a next version, map_data and weather_data will be updated
(case of a modified gpx file, see issue #7)
"""
if workout_data.get('refresh'):
workout = update_workout(workout)
if workout_data.get('sport_id'):
workout.sport_id = workout_data.get('sport_id')
if workout_data.get('title'):

64
poetry.lock generated
View File

@ -407,7 +407,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "importlib-metadata"
version = "4.10.1"
version = "4.11.0"
description = "Read metadata from Python packages"
category = "main"
optional = false
@ -578,7 +578,7 @@ python-versions = ">=3.7"
[[package]]
name = "platformdirs"
version = "2.4.1"
version = "2.5.0"
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev"
optional = false
@ -704,7 +704,7 @@ diagrams = ["jinja2", "railroad-diagrams"]
[[package]]
name = "pytest"
version = "7.0.0"
version = "7.0.1"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
@ -907,7 +907,7 @@ sphinx = ">=1.3.1"
[[package]]
name = "redis"
version = "4.1.2"
version = "4.1.3"
description = "Python client for Redis database and key-value store"
category = "main"
optional = false
@ -1200,28 +1200,12 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "tomli"
version = "2.0.0"
version = "2.0.1"
description = "A lil' TOML parser"
category = "dev"
optional = false
python-versions = ">=3.7"
[[package]]
name = "tqdm"
version = "4.62.3"
description = "Fast, Extensible Progress Meter"
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[package.extras]
dev = ["py-make (>=0.1.0)", "twine", "wheel"]
notebook = ["ipywidgets (>=6)"]
telegram = ["requests"]
[[package]]
name = "trio"
version = "0.19.0"
@ -1278,7 +1262,7 @@ python-versions = "*"
[[package]]
name = "types-requests"
version = "2.27.8"
version = "2.27.9"
description = "Typing stubs for requests"
category = "dev"
optional = false
@ -1324,7 +1308,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
name = "werkzeug"
version = "2.0.2"
version = "2.0.3"
description = "The comprehensive WSGI web application library."
category = "main"
optional = false
@ -1367,7 +1351,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
content-hash = "efbc9200e445df97c991ac1e6b72873262c32e0f09b21dc682593fd5034686ab"
content-hash = "ec6da4aaa4cef6ee6c235ef4d2f101b533409097ee8b5169596373a5b4c60cdb"
[metadata.files]
alabaster = [
@ -1679,8 +1663,8 @@ imagesize = [
{file = "imagesize-1.3.0.tar.gz", hash = "sha256:cd1750d452385ca327479d45b64d9c7729ecf0b3969a58148298c77092261f9d"},
]
importlib-metadata = [
{file = "importlib_metadata-4.10.1-py3-none-any.whl", hash = "sha256:899e2a40a8c4a1aec681feef45733de8a6c58f3f6a0dbed2eb6574b4387a77b6"},
{file = "importlib_metadata-4.10.1.tar.gz", hash = "sha256:951f0d8a5b7260e9db5e41d429285b5f451e928479f19d80818878527d36e95e"},
{file = "importlib_metadata-4.11.0-py3-none-any.whl", hash = "sha256:6affcdb3aec542dd98df8211e730bba6c5f2bec8288d47bacacde898f548c9ad"},
{file = "importlib_metadata-4.11.0.tar.gz", hash = "sha256:9e5e553bbba1843cb4a00823014b907616be46ee503d2b9ba001d214a8da218f"},
]
importlib-resources = [
{file = "importlib_resources-5.4.0-py3-none-any.whl", hash = "sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45"},
@ -1822,8 +1806,8 @@ pillow = [
{file = "Pillow-9.0.1.tar.gz", hash = "sha256:6c8bc8238a7dfdaf7a75f5ec5a663f4173f8c367e5a39f87e720495e1eed75fa"},
]
platformdirs = [
{file = "platformdirs-2.4.1-py3-none-any.whl", hash = "sha256:1d7385c7db91728b83efd0ca99a5afb296cab9d0ed8313a45ed8ba17967ecfca"},
{file = "platformdirs-2.4.1.tar.gz", hash = "sha256:440633ddfebcc36264232365d7840a970e75e1018d15b4327d11f91909045fda"},
{file = "platformdirs-2.5.0-py3-none-any.whl", hash = "sha256:30671902352e97b1eafd74ade8e4a694782bd3471685e78c32d0fdfd3aa7e7bb"},
{file = "platformdirs-2.5.0.tar.gz", hash = "sha256:8ec11dfba28ecc0715eb5fb0147a87b1bf325f349f3da9aab2cd6b50b96b692b"},
]
pluggy = [
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
@ -1924,8 +1908,8 @@ pyparsing = [
{file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"},
]
pytest = [
{file = "pytest-7.0.0-py3-none-any.whl", hash = "sha256:42901e6bd4bd4a0e533358a86e848427a49005a3256f657c5c8f8dd35ef137a9"},
{file = "pytest-7.0.0.tar.gz", hash = "sha256:dad48ffda394e5ad9aa3b7d7ddf339ed502e5e365b1350e0af65f4a602344b11"},
{file = "pytest-7.0.1-py3-none-any.whl", hash = "sha256:9ce3ff477af913ecf6321fe337b93a2c0dcf2a0a1439c43f5452112c1e4280db"},
{file = "pytest-7.0.1.tar.gz", hash = "sha256:e30905a0c131d3d94b89624a1cc5afec3e0ba2fbdb151867d8e0ebd49850f171"},
]
pytest-base-url = [
{file = "pytest-base-url-1.4.2.tar.gz", hash = "sha256:7f1f32e08c2ee751e59e7f5880235b46e83496adc5cba5a01ca218c6fe81333d"},
@ -1982,8 +1966,8 @@ recommonmark = [
{file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"},
]
redis = [
{file = "redis-4.1.2-py3-none-any.whl", hash = "sha256:f13eea4254e302485add677cadedaf1305c1b3a4e07535e23b7b239798ce9301"},
{file = "redis-4.1.2.tar.gz", hash = "sha256:bf86397be532fc0a888d5976a5313a3a70d8f912d52bc0c09bffda4b8425a1d4"},
{file = "redis-4.1.3-py3-none-any.whl", hash = "sha256:267e89e476eb684517584e8988f1e5d755f483a368c133020c4c40e8b676bc5d"},
{file = "redis-4.1.3.tar.gz", hash = "sha256:f2715caad9f0e8c6ff8df46d3c4c9022a3929001f530f66b62747554d3067068"},
]
requests = [
{file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
@ -2102,12 +2086,8 @@ toml = [
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
]
tomli = [
{file = "tomli-2.0.0-py3-none-any.whl", hash = "sha256:b5bde28da1fed24b9bd1d4d2b8cba62300bfb4ec9a6187a957e8ddb9434c5224"},
{file = "tomli-2.0.0.tar.gz", hash = "sha256:c292c34f58502a1eb2bbb9f5bbc9a5ebc37bee10ffb8c2d6bbdfa8eb13cc14e1"},
]
tqdm = [
{file = "tqdm-4.62.3-py2.py3-none-any.whl", hash = "sha256:8dd278a422499cd6b727e6ae4061c40b48fce8b76d1ccbf5d34fca9b7f925b0c"},
{file = "tqdm-4.62.3.tar.gz", hash = "sha256:d359de7217506c9851b7869f3708d8ee53ed70a1b8edbba4dbcb47442592920d"},
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
]
trio = [
{file = "trio-0.19.0-py3-none-any.whl", hash = "sha256:c27c231e66336183c484fbfe080fa6cc954149366c15dc21db8b7290081ec7b8"},
@ -2152,8 +2132,8 @@ types-pytz = [
{file = "types_pytz-2021.3.4-py3-none-any.whl", hash = "sha256:ccfa2ed29f816e3de2f882541c06ad2791f808a79cfe38265411820190999f0f"},
]
types-requests = [
{file = "types-requests-2.27.8.tar.gz", hash = "sha256:c2f4e4754d07ca0a88fd8a89bbc6c8a9f90fb441f9c9b572fd5c484f04817486"},
{file = "types_requests-2.27.8-py3-none-any.whl", hash = "sha256:8ec9f5f84adc6f579f53943312c28a84e87dc70201b54f7c4fbc7d22ecfa8a3e"},
{file = "types-requests-2.27.9.tar.gz", hash = "sha256:7368974534d297939492efdfdab232930440b11e2203f6df1f0c40e3242a87ea"},
{file = "types_requests-2.27.9-py3-none-any.whl", hash = "sha256:74070045418faf710f3154403d6a16c9e67db50e5119906ca6955f1658d20f7b"},
]
types-urllib3 = [
{file = "types-urllib3-1.26.9.tar.gz", hash = "sha256:abd2d4857837482b1834b4817f0587678dcc531dbc9abe4cde4da28cef3f522c"},
@ -2168,8 +2148,8 @@ urllib3 = [
{file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"},
]
werkzeug = [
{file = "Werkzeug-2.0.2-py3-none-any.whl", hash = "sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f"},
{file = "Werkzeug-2.0.2.tar.gz", hash = "sha256:aa2bb6fc8dee8d6c504c0ac1e7f5f7dc5810a9903e793b6f715a9f015bdadb9a"},
{file = "Werkzeug-2.0.3-py3-none-any.whl", hash = "sha256:1421ebfc7648a39a5c58c601b154165d05cf47a3cd0ccb70857cbdacf6c8f2b8"},
{file = "Werkzeug-2.0.3.tar.gz", hash = "sha256:b863f8ff057c522164b6067c9e28b041161b4be5ba4d0daceeaa50a163822d3c"},
]
wrapt = [
{file = "wrapt-1.13.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:e05e60ff3b2b0342153be4d1b597bbcfd8330890056b9619f4ad6b8d5c96a81a"},

View File

@ -38,7 +38,6 @@ python-forecastio = "^1.4"
pytz = "^2021.3"
shortuuid = "^1.0.8"
staticmap = "^0.5.4"
tqdm = "^4.62"
SQLAlchemy = "1.4.31"
pyOpenSSL = "^22.0"
@ -63,7 +62,7 @@ Sphinx = "^4.4.0"
[tool.poetry.scripts]
fittrackee = 'fittrackee.__main__:main'
fittrackee_init_data = 'fittrackee.__main__:init_data'
fittrackee_set_admin = 'fittrackee.__main__:set_admin'
fittrackee_upgrade_db = 'fittrackee.__main__:upgrade_db'
fittrackee_worker = 'fittrackee.__main__:dramatiq_worker'