Merge pull request #150 from SamR1/improve-app-init
Improve application initialization
This commit is contained in:
commit
f3e0f5e4b7
@ -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
|
||||
|
14
Makefile
14
Makefile
@ -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)
|
||||
|
||||
|
@ -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
7
docker/set-admin.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
cd /usr/src/app
|
||||
|
||||
source .env.docker
|
||||
|
||||
flask set-admin $1
|
@ -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`
|
@ -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 <username>
|
||||
</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@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><username>
|
||||
</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@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><username>
|
||||
</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@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><username>
|
||||
</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@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
@ -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`
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
18
e2e/utils.py
18
e2e/utils.py
@ -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"))
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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.')
|
48
fittrackee/tests/application/test_database_utils.py
Normal file
48
fittrackee/tests/application/test_database_utils.py
Normal 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
|
30
fittrackee/tests/users/test_users_utils.py
Normal file
30
fittrackee/tests/users/test_users_utils.py
Normal 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
12
fittrackee/tests/utils.py
Normal 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)
|
||||
)
|
@ -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']
|
||||
|
2
fittrackee/users/exceptions.py
Normal file
2
fittrackee/users/exceptions.py
Normal file
@ -0,0 +1,2 @@
|
||||
class UserNotFoundException(Exception):
|
||||
...
|
@ -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()
|
||||
|
@ -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
64
poetry.lock
generated
@ -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"},
|
||||
|
@ -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'
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user