Merged dev branch
@@ -25,6 +25,7 @@ export SENDER_EMAIL=
 | 
			
		||||
 | 
			
		||||
# Workouts
 | 
			
		||||
# export TILE_SERVER_URL=
 | 
			
		||||
# export STATICMAP_SUBDOMAINS=
 | 
			
		||||
# export MAP_ATTRIBUTION=
 | 
			
		||||
# export DEFAULT_STATICMAP=False
 | 
			
		||||
# export WEATHER_API_KEY=
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										34
									
								
								.github/workflows/.tests-javascript.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,34 @@
 | 
			
		||||
name: Javascript CI
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    paths: ['fittrackee_client/**']
 | 
			
		||||
  pull_request:
 | 
			
		||||
    paths: ['fittrackee_client/**']
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  working-directory: fittrackee_client
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  javascript:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      - name: Use Node.js 17.x
 | 
			
		||||
        uses: actions/setup-node@v2
 | 
			
		||||
        with:
 | 
			
		||||
          node-version: "17.x"
 | 
			
		||||
      - name: Install yarn and dependencies
 | 
			
		||||
        working-directory: ${{env.working-directory}}
 | 
			
		||||
        run: |
 | 
			
		||||
          npm install --global yarn
 | 
			
		||||
          yarn install
 | 
			
		||||
      - name: Lint
 | 
			
		||||
        working-directory: ${{env.working-directory}}
 | 
			
		||||
        run: yarn lint
 | 
			
		||||
      - name: Tests
 | 
			
		||||
        working-directory: ${{env.working-directory}}
 | 
			
		||||
        run: yarn test:unit
 | 
			
		||||
      - name: Build
 | 
			
		||||
        working-directory: ${{env.working-directory}}
 | 
			
		||||
        run: yarn build
 | 
			
		||||
							
								
								
									
										98
									
								
								.github/workflows/.tests-python.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,98 @@
 | 
			
		||||
name: Python CI
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    paths-ignore: ['docs/**', 'docsrc/**', 'fittrackee_client/**', '*.md']
 | 
			
		||||
  pull_request:
 | 
			
		||||
    paths-ignore: ['docs/**', 'docsrc/**', 'fittrackee_client/**', '*.md']
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  APP_SETTINGS: fittrackee.config.TestingConfig
 | 
			
		||||
  DATABASE_TEST_URL: "postgresql://fittrackee:fittrackee@postgres:5432/fittrackee_test"
 | 
			
		||||
  EMAIL_URL: "smtp://none:none@0.0.0.0:1025"
 | 
			
		||||
  FLASK_APP: fittrackee/__main__.py
 | 
			
		||||
  SENDER_EMAIL: fittrackee@example.com
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  python:
 | 
			
		||||
    name: python ${{ matrix.python-version }}
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    container: python:${{ matrix.python-version }}
 | 
			
		||||
    services:
 | 
			
		||||
      postgres:
 | 
			
		||||
        image: postgres:latest
 | 
			
		||||
        env:
 | 
			
		||||
          POSTGRES_DB: fittrackee_test
 | 
			
		||||
          POSTGRES_USER: fittrackee
 | 
			
		||||
          POSTGRES_PASSWORD: fittrackee
 | 
			
		||||
        options: >-
 | 
			
		||||
          --health-cmd pg_isready
 | 
			
		||||
          --health-interval 10s
 | 
			
		||||
          --health-timeout 5s
 | 
			
		||||
          --health-retries 5
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        python-version:  [ "3.7", "3.8", "3.9", "3.10" ]
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      - name: Install Poetry and Dependencies
 | 
			
		||||
        run: |
 | 
			
		||||
          python -m pip install --upgrade pip
 | 
			
		||||
          pip install --quiet poetry
 | 
			
		||||
          poetry config virtualenvs.create false
 | 
			
		||||
          poetry install --no-interaction --quiet
 | 
			
		||||
      - name: Bandit
 | 
			
		||||
        if: matrix.python-version == '3.10'
 | 
			
		||||
        run: bandit -r fittrackee -c pyproject.toml
 | 
			
		||||
      - name: Lint
 | 
			
		||||
        if: matrix.python-version == '3.10'
 | 
			
		||||
        run: pytest --flake8 --isort --black -m "flake8 or isort or black" fittrackee e2e --ignore=fittrackee/migrations -p no:warnings
 | 
			
		||||
      - name: Mypy
 | 
			
		||||
        if: matrix.python-version == '3.10'
 | 
			
		||||
        run: mypy fittrackee
 | 
			
		||||
      - name: Pytest
 | 
			
		||||
        run: pytest fittrackee -p no:warnings --cov fittrackee --cov-report term-missing
 | 
			
		||||
 | 
			
		||||
  end2end:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs: ["python"]
 | 
			
		||||
    container: python:3.10
 | 
			
		||||
    services:
 | 
			
		||||
      postgres:
 | 
			
		||||
        image: postgres:latest
 | 
			
		||||
        env:
 | 
			
		||||
          POSTGRES_DB: fittrackee_test
 | 
			
		||||
          POSTGRES_USER: fittrackee
 | 
			
		||||
          POSTGRES_PASSWORD: fittrackee
 | 
			
		||||
        options: >-
 | 
			
		||||
          --health-cmd pg_isready
 | 
			
		||||
          --health-interval 10s
 | 
			
		||||
          --health-timeout 5s
 | 
			
		||||
          --health-retries 5
 | 
			
		||||
      selenium:
 | 
			
		||||
        image: selenium/standalone-firefox
 | 
			
		||||
      mailhog:
 | 
			
		||||
        image: mailhog/mailhog:latest
 | 
			
		||||
      redis:
 | 
			
		||||
        image: redis:latest
 | 
			
		||||
    env:
 | 
			
		||||
      APP_SETTINGS: fittrackee.config.End2EndTestingConfig
 | 
			
		||||
      EMAIL_URL: "smtp://mailhog:1025"
 | 
			
		||||
      REDIS_URL: "redis://redis:6379"
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v2
 | 
			
		||||
      - name: Install Poetry and Dependencies
 | 
			
		||||
        run: |
 | 
			
		||||
          python -m pip install --upgrade pip
 | 
			
		||||
          pip install --quiet poetry
 | 
			
		||||
          poetry config virtualenvs.create false
 | 
			
		||||
          poetry install --no-interaction --quiet
 | 
			
		||||
      - name: Run migrations
 | 
			
		||||
        run: flask db upgrade --directory fittrackee/migrations
 | 
			
		||||
      - name: Start application and run tests with Selenium
 | 
			
		||||
        run: |
 | 
			
		||||
          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
 | 
			
		||||
          nohup flask worker --processes=1 >> nohup.out 2>&1 &
 | 
			
		||||
          pytest e2e --driver Remote --capability browserName firefox --host selenium --port 4444
 | 
			
		||||
							
								
								
									
										100
									
								
								.gitlab-ci.yml
									
									
									
									
									
								
							
							
						
						@@ -1,100 +0,0 @@
 | 
			
		||||
image: python:3.9
 | 
			
		||||
 | 
			
		||||
variables:
 | 
			
		||||
  POSTGRES_DB: fittrackee_test
 | 
			
		||||
  POSTGRES_USER: fittrackee
 | 
			
		||||
  POSTGRES_PASSWORD: fittrackee
 | 
			
		||||
  POSTGRES_HOST: postgres
 | 
			
		||||
  APP_SETTINGS: fittrackee.config.TestingConfig
 | 
			
		||||
  DATABASE_TEST_URL: postgresql://fittrackee:fittrackee@postgres:5432/fittrackee_test
 | 
			
		||||
  EMAIL_URL: smtp://none:none@0.0.0.0:1025
 | 
			
		||||
  FLASK_APP: fittrackee/__main__.py
 | 
			
		||||
  SENDER_EMAIL: fittrackee@example.com
 | 
			
		||||
 | 
			
		||||
services:
 | 
			
		||||
  - name: postgres:latest
 | 
			
		||||
    alias: postgres
 | 
			
		||||
 | 
			
		||||
stages:
 | 
			
		||||
  - lint
 | 
			
		||||
  - tests
 | 
			
		||||
  - selenium
 | 
			
		||||
 | 
			
		||||
.python:
 | 
			
		||||
  stage: tests
 | 
			
		||||
  before_script:
 | 
			
		||||
    - pip install --quiet poetry
 | 
			
		||||
    - poetry config virtualenvs.create false
 | 
			
		||||
    - poetry install --no-interaction --quiet
 | 
			
		||||
  script:
 | 
			
		||||
    - pytest fittrackee -p no:warnings --cov fittrackee --cov-report term-missing
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.javascript:
 | 
			
		||||
  stage: tests
 | 
			
		||||
  before_script:
 | 
			
		||||
    - apt-get update && apt-get install -y nodejs npm
 | 
			
		||||
    - npm install --global yarn
 | 
			
		||||
    - cd fittrackee_client
 | 
			
		||||
    - yarn install
 | 
			
		||||
 | 
			
		||||
python-lint:
 | 
			
		||||
  stage: lint
 | 
			
		||||
  extends: .python
 | 
			
		||||
  script:
 | 
			
		||||
    - pytest --flake8 --isort --black -m "flake8 or isort or black" fittrackee e2e --ignore=fittrackee/migrations
 | 
			
		||||
 | 
			
		||||
python-type-check:
 | 
			
		||||
  stage: lint
 | 
			
		||||
  extends: .python
 | 
			
		||||
  script:
 | 
			
		||||
    - mypy fittrackee
 | 
			
		||||
 | 
			
		||||
eslint:
 | 
			
		||||
  stage: lint
 | 
			
		||||
  extends: .javascript
 | 
			
		||||
  script:
 | 
			
		||||
    - yarn lint
 | 
			
		||||
 | 
			
		||||
python-3.7:
 | 
			
		||||
  extends: .python
 | 
			
		||||
  image: python:3.7
 | 
			
		||||
 | 
			
		||||
python-3.8:
 | 
			
		||||
  extends: .python
 | 
			
		||||
  image: python:3.8
 | 
			
		||||
 | 
			
		||||
python-3.9:
 | 
			
		||||
  extends: .python
 | 
			
		||||
 | 
			
		||||
python-3.10:
 | 
			
		||||
  extends: .python
 | 
			
		||||
  image: python:3.10
 | 
			
		||||
 | 
			
		||||
typescript:
 | 
			
		||||
  stage: tests
 | 
			
		||||
  before_script:
 | 
			
		||||
    - apt-get update && apt-get install -y nodejs npm
 | 
			
		||||
    - npm install --global yarn
 | 
			
		||||
    - cd fittrackee_client
 | 
			
		||||
    - yarn install
 | 
			
		||||
  script:
 | 
			
		||||
    - yarn test:unit
 | 
			
		||||
 | 
			
		||||
firefox:
 | 
			
		||||
  stage: selenium
 | 
			
		||||
  services:
 | 
			
		||||
    - name: postgres:latest
 | 
			
		||||
      alias: postgres
 | 
			
		||||
    - name: selenium/standalone-firefox
 | 
			
		||||
      alias: selenium
 | 
			
		||||
  before_script:
 | 
			
		||||
    - pip install --quiet poetry
 | 
			
		||||
    - poetry config virtualenvs.create false
 | 
			
		||||
    - poetry install --no-interaction --quiet
 | 
			
		||||
    - flask db upgrade --directory fittrackee/migrations
 | 
			
		||||
    - 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
 | 
			
		||||
  script:
 | 
			
		||||
    - pytest e2e --driver Remote --capability browserName firefox --host selenium --port 4444
 | 
			
		||||
							
								
								
									
										177
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						@@ -1,5 +1,182 @@
 | 
			
		||||
# Change log
 | 
			
		||||
 | 
			
		||||
## Version 0.6.10 (2022/07/13)
 | 
			
		||||
 | 
			
		||||
### Issues Closed
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#210](https://github.com/SamR1/FitTrackee/issues/210) - ERROR - could not download 6 tiles  
 | 
			
		||||
  **Note**: for tile server requiring subdomains, see the new environment variable [`STATICMAP_SUBDOMAINS`](https://samr1.github.io/FitTrackee/installation.html#envvar-STATICMAP_SUBDOMAINS)
 | 
			
		||||
 | 
			
		||||
### Pull Requests
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#209](https://github.com/SamR1/FitTrackee/pull/209) - Incorrect duration with track containing multiple segments
 | 
			
		||||
 | 
			
		||||
Thanks to @gorgobacka
 | 
			
		||||
 | 
			
		||||
In this release 1 issue was closed.  
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.6.9 (2022/07/03)
 | 
			
		||||
 | 
			
		||||
FitTrackee is now available in German (thanks to @gorgobacka).  
 | 
			
		||||
And translations can be updated on Weblate.
 | 
			
		||||
 | 
			
		||||
### Issues Closed
 | 
			
		||||
 | 
			
		||||
#### Features
 | 
			
		||||
 | 
			
		||||
* [#200](https://github.com/SamR1/FitTrackee/issues/200) - Detect browser language to use matching translation if available
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [PR#208](https://github.com/SamR1/FitTrackee/pull/208) - fix order on records cards
 | 
			
		||||
* [#201](https://github.com/SamR1/FitTrackee/issues/201) - html lang attribute is not updated when changing language
 | 
			
		||||
 | 
			
		||||
#### Translations
 | 
			
		||||
 | 
			
		||||
* [PR#197](https://github.com/SamR1/FitTrackee/pull/197) - Translations update from Weblate (French)
 | 
			
		||||
* [#196](https://github.com/SamR1/FitTrackee/issues/196) - Use translation management tool
 | 
			
		||||
* [#190](https://github.com/SamR1/FitTrackee/issues/190) - Add German translation
 | 
			
		||||
 | 
			
		||||
In this release 4 issues were closed.  
 | 
			
		||||
 | 
			
		||||
Thanks to the contributors:
 | 
			
		||||
- @gorgobacka
 | 
			
		||||
- J. Lavoie (from Weblate)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.6.8 (2022/06/22)
 | 
			
		||||
 | 
			
		||||
### Issues Closed
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#193](https://github.com/SamR1/FitTrackee/issues/193) - Allow deleting a workout when files are missing
 | 
			
		||||
* [#192](https://github.com/SamR1/FitTrackee/issues/192) - Returns 404 instead of 500 when map file not found
 | 
			
		||||
* [#191](https://github.com/SamR1/FitTrackee/issues/191) - Layout issue on Workouts page
 | 
			
		||||
 | 
			
		||||
### Misc
 | 
			
		||||
 | 
			
		||||
* change gpx and map file naming (included in [PR#195](https://github.com/SamR1/FitTrackee/pull/195))  
 | 
			
		||||
  Note: it does not affect previously imported files
 | 
			
		||||
* [cc4287e](https://github.com/SamR1/FitTrackee/commit/cc4287ed327faaba268a0c689841d16a7aecc3fb) - Fix docker env file
 | 
			
		||||
 | 
			
		||||
In this release 3 issues were closed.  
 | 
			
		||||
 | 
			
		||||
## Version 0.6.7 (2022/06/11)
 | 
			
		||||
 | 
			
		||||
### Issues Closed
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#156](https://github.com/SamR1/FitTrackee/issues/156) - Process gpx file with offset
 | 
			
		||||
 | 
			
		||||
In this release 1 issue was closed.  
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.6.6 (2022/05/29)
 | 
			
		||||
 | 
			
		||||
### Misc
 | 
			
		||||
 | 
			
		||||
No new features in this release, only dependencies update and code refacto before introducing new features.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.6.5 (2022/04/24)
 | 
			
		||||
 | 
			
		||||
It is now possible to start FitTrackee without a configured SMTP provider (see [documentation](https://samr1.github.io/FitTrackee/installation.html#emails)).
 | 
			
		||||
It reduces pre-requisites for single-user instances.
 | 
			
		||||
 | 
			
		||||
To manage users, a new [CLI](https://samr1.github.io/FitTrackee/cli.html) is available.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Issues Closed
 | 
			
		||||
 | 
			
		||||
#### Features
 | 
			
		||||
 | 
			
		||||
* [#180](https://github.com/SamR1/FitTrackee/issues/180) - allow using FitTrackee without SMTP server
 | 
			
		||||
 | 
			
		||||
In this release 1 issue was closed.  
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.6.4 (2022/04/23)
 | 
			
		||||
 | 
			
		||||
### Issues Closed
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#178](https://github.com/SamR1/FitTrackee/issues/178) - cannot send email with TLS
 | 
			
		||||
 | 
			
		||||
In this release 1 issue was closed.  
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.6.3 (2022/04/09)
 | 
			
		||||
 | 
			
		||||
### Pull Requests
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#177](https://github.com/SamR1/FitTrackee/pull/177) - Minor fixes 
 | 
			
		||||
  * add missing translation 
 | 
			
		||||
  * fix 'Add Workout' card position on small screens
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.6.2 (2022/04/03)
 | 
			
		||||
 | 
			
		||||
### Issues Closed
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#175](https://github.com/SamR1/FitTrackee/issues/175) - Distance card on dashboard is not refreshed
 | 
			
		||||
* [#173](https://github.com/SamR1/FitTrackee/issues/173) - link to user profile in workout card is incorrect
 | 
			
		||||
 | 
			
		||||
In this release 2 issues were closed.  
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.6.1 (2022/03/27)
 | 
			
		||||
 | 
			
		||||
### Issues Closed
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#171](https://github.com/SamR1/FitTrackee/issues/171) - Stats chart is not updated correctly 
 | 
			
		||||
 | 
			
		||||
In this release 1 issue was closed.  
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.6.0 (2022/03/27)
 | 
			
		||||
 | 
			
		||||
This version introduces some changes on [user registration](https://samr1.github.io/FitTrackee/features.html#account-preferences).  
 | 
			
		||||
From now on, a user needs to confirm his account after registration (an email with confirmation instructions is sent after registration).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Issues Closed
 | 
			
		||||
 | 
			
		||||
#### Features
 | 
			
		||||
 | 
			
		||||
* [#155](https://github.com/SamR1/FitTrackee/issues/155) -  Improve user registration
 | 
			
		||||
* [#106](https://github.com/SamR1/FitTrackee/issues/106) -  Allow user to update email
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#169](https://github.com/SamR1/FitTrackee/issues/169) -  user picture is not refreshed after update
 | 
			
		||||
 | 
			
		||||
### Pull Requests
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#161](https://github.com/SamR1/FitTrackee/pull/161) - Minor translation issue on 'Farthest'
 | 
			
		||||
* [#160](https://github.com/SamR1/FitTrackee/pull/160) - Minor translation issue on APP_ERROR
 | 
			
		||||
 | 
			
		||||
Thanks to @Fmstrat
 | 
			
		||||
 | 
			
		||||
In this release 3 issues were closed.  
 | 
			
		||||
**Note:** This release contains database migration (see upgrade instructions in [documentation](https://samr1.github.io/FitTrackee/installation.html#upgrade))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.5.7 (2022/02/13)
 | 
			
		||||
 | 
			
		||||
This release contains several fixes including security fixes.  
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										106
									
								
								CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,106 @@
 | 
			
		||||
# Contributing to FitTrackee
 | 
			
		||||
 | 
			
		||||
First off, thank you for your interest in contributing!
 | 
			
		||||
 | 
			
		||||
## Report issues, ask for features
 | 
			
		||||
 | 
			
		||||
* If a bug is a **security vulnerability**, please refer to [security policy](https://github.com/SamR1/FitTrackee/blob/master/SECURITY.md).
 | 
			
		||||
 | 
			
		||||
* Ensure an issue was not **already opened** by searching on **GitHub** under [Issues](https://github.com/SamR1/FitTrackee/issues). 
 | 
			
		||||
 | 
			
		||||
* If not, [open a new one](https://github.com/SamR1/FitTrackee/issues/new) with a descriptive title and provide a description.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Contributing Code
 | 
			
		||||
 | 
			
		||||
### Project repository
 | 
			
		||||
 | 
			
		||||
The **GitHub** repository contains:
 | 
			
		||||
- source code (note that the repository also includes client build),
 | 
			
		||||
- translations,
 | 
			
		||||
- tests,
 | 
			
		||||
- documentation (source and build).
 | 
			
		||||
 | 
			
		||||
Continuous integration workflows run on **Github Actions** platform (on **push** and **pull requests**).
 | 
			
		||||
 | 
			
		||||
### Translations
 | 
			
		||||
 | 
			
		||||
The available languages are:  
 | 
			
		||||
[](https://hosted.weblate.org/engage/fittrackee/)
 | 
			
		||||
 | 
			
		||||
Translations files are located:
 | 
			
		||||
- on API side (emails): `fittrackee/emails/translations/` (implemented with [Babel](https://babel.pocoo.org/en/latest/))
 | 
			
		||||
- on client side: `fittrackee_client/src/locales` (implemented with [Vue I18n](https://vue-i18n.intlify.dev/))
 | 
			
		||||
 | 
			
		||||
Translations can be updated through [Weblate](https://hosted.weblate.org/engage/fittrackee/).  
 | 
			
		||||
 | 
			
		||||
### How to install FitTrackee
 | 
			
		||||
 | 
			
		||||
see [Installations instructions](https://samr1.github.io/FitTrackee/installation.html)
 | 
			
		||||
 | 
			
		||||
### Pull Requests
 | 
			
		||||
 | 
			
		||||
Please make your changes from the development branch (`dev`).
 | 
			
		||||
 | 
			
		||||
* Fork the repository (see [GitHub instructions](https://docs.github.com/en/get-started/quickstart/contributing-to-projects))
 | 
			
		||||
 | 
			
		||||
* Implement your feature.
 | 
			
		||||
 | 
			
		||||
* If your changes need a database migration:
 | 
			
		||||
  * You can generate one after updating models with the following command:
 | 
			
		||||
    ```shell
 | 
			
		||||
    $ make migrate-db
 | 
			
		||||
    ```
 | 
			
		||||
  * For data migration, an empty migration can be created with this following command:
 | 
			
		||||
    ```shell
 | 
			
		||||
    $ make revision MIGRATION_MESSAGE="<MIGRATION_MESSAGE>"
 | 
			
		||||
    ```
 | 
			
		||||
  * Rename the migration, prefixing with the next number.
 | 
			
		||||
  * To apply database changes:
 | 
			
		||||
    ```shell
 | 
			
		||||
    $ make upgrade-db
 | 
			
		||||
    ```
 | 
			
		||||
  * Check the downgrade migration.
 | 
			
		||||
  
 | 
			
		||||
* Run checks (lint, type check and unit tests).
 | 
			
		||||
  ```shell
 | 
			
		||||
  $ make check-all
 | 
			
		||||
  ```
 | 
			
		||||
  There are some end-to-end tests, to run them (needs a running application):
 | 
			
		||||
  ```shell
 | 
			
		||||
  $ make test-e2e
 | 
			
		||||
  ```
 | 
			
		||||
 | 
			
		||||
* If needed, update translations.
 | 
			
		||||
   * On client side, update files in `fittrackee_client/src/locales` folder.  
 | 
			
		||||
   * On API side (emails), to extract new strings into `messages.pot`:
 | 
			
		||||
     ```shell
 | 
			
		||||
     $ make babel-extract
 | 
			
		||||
     ```
 | 
			
		||||
     To add new strings in translations files (`fittrackee/emails/translations/<LANG>/LC_MESSAGES/messages.po`):
 | 
			
		||||
     ```shell
 | 
			
		||||
     $ make babel-update
 | 
			
		||||
     ```
 | 
			
		||||
     After updating strings in `messages.po`, compile the translations:
 | 
			
		||||
     ```shell
 | 
			
		||||
     $ make babel-compile
 | 
			
		||||
     ```
 | 
			
		||||
 | 
			
		||||
* If needed, add or update tests.
 | 
			
		||||
 | 
			
		||||
* If needed, update documentation (no need to build documentation, it will be done when releasing).
 | 
			
		||||
 | 
			
		||||
* If updated code contains client-side changes, you can generate javascript assets to check **FitTrackee** whithout starting client dev server:
 | 
			
		||||
  ```shell
 | 
			
		||||
  $ make build-client
 | 
			
		||||
  ```
 | 
			
		||||
  No need to commit these files, dist files will be generated before merging or when releasing.
 | 
			
		||||
 | 
			
		||||
* Create your pull request to merge on `dev` branch.
 | 
			
		||||
 | 
			
		||||
* Ensure the pull requests description clearly describes the problem and solution. Include the relevant issue number if applicable.
 | 
			
		||||
 | 
			
		||||
* If needed, [update your branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/keeping-your-pull-request-in-sync-with-the-base-branch). 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Thanks.
 | 
			
		||||
@@ -1,6 +1,4 @@
 | 
			
		||||
FROM python:3.9
 | 
			
		||||
 | 
			
		||||
MAINTAINER SamR1@users.noreply.github.com
 | 
			
		||||
FROM python:3.10
 | 
			
		||||
 | 
			
		||||
# set working directory
 | 
			
		||||
RUN mkdir -p /usr/src/app
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										59
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						@@ -6,12 +6,29 @@ make-p:
 | 
			
		||||
	# Launch all P targets in parallel and exit as soon as one exits.
 | 
			
		||||
	set -m; (for p in $(P); do ($(MAKE) $$p || kill 0)& done; wait)
 | 
			
		||||
 | 
			
		||||
babel-extract:
 | 
			
		||||
	$(PYBABEL) extract -F babel.cfg -k lazy_gettext -o messages.pot .
 | 
			
		||||
 | 
			
		||||
babel-init:
 | 
			
		||||
	$(PYBABEL) init -i messages.pot -d fittrackee/emails/translations -l $(LANG)
 | 
			
		||||
 | 
			
		||||
babel-compile:
 | 
			
		||||
	$(PYBABEL) compile -d fittrackee/emails/translations
 | 
			
		||||
 | 
			
		||||
babel-update:
 | 
			
		||||
	$(PYBABEL) update -i messages.pot -d fittrackee/emails/translations
 | 
			
		||||
 | 
			
		||||
bandit:
 | 
			
		||||
	$(BANDIT) -r fittrackee -c pyproject.toml
 | 
			
		||||
 | 
			
		||||
build-client: lint-client
 | 
			
		||||
	cd fittrackee_client && $(NPM) build
 | 
			
		||||
 | 
			
		||||
check-all: lint-all type-check test-python test-client
 | 
			
		||||
check-all: bandit lint-all type-check test-all
 | 
			
		||||
 | 
			
		||||
check-python: lint-python type-check test-python
 | 
			
		||||
check-client: lint-client test-client
 | 
			
		||||
 | 
			
		||||
check-python: bandit lint-python type-check test-python
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -rf .mypy_cache
 | 
			
		||||
@@ -69,7 +86,7 @@ docker-stop:
 | 
			
		||||
	docker-compose -f docker-compose-dev.yml stop
 | 
			
		||||
 | 
			
		||||
docker-up:
 | 
			
		||||
	docker-compose -f docker-compose-dev.yml up fittrackeee
 | 
			
		||||
	docker-compose -f docker-compose-dev.yml up fittrackee
 | 
			
		||||
 | 
			
		||||
downgrade-db:
 | 
			
		||||
	$(FLASK) db downgrade --directory $(MIGRATIONS)
 | 
			
		||||
@@ -87,19 +104,21 @@ html:
 | 
			
		||||
 | 
			
		||||
install-db:
 | 
			
		||||
	psql -U postgres -f db/create.sql
 | 
			
		||||
	$(FLASK) db upgrade --directory $(MIGRATIONS)
 | 
			
		||||
	$(FTCLI) db upgrade
 | 
			
		||||
 | 
			
		||||
init-db:
 | 
			
		||||
	$(FLASK) drop-db
 | 
			
		||||
	$(FLASK) db upgrade --directory $(MIGRATIONS)
 | 
			
		||||
	$(FTCLI) db drop
 | 
			
		||||
	$(FTCLI) db upgrade
 | 
			
		||||
 | 
			
		||||
install: install-client install-python
 | 
			
		||||
 | 
			
		||||
install-client:
 | 
			
		||||
	cd fittrackee_client && $(NPM) install --prod
 | 
			
		||||
	# NPM_ARGS="--ignore-engines", if errors with Node latest version
 | 
			
		||||
	cd fittrackee_client && $(NPM) install --prod $(NPM_ARGS)
 | 
			
		||||
 | 
			
		||||
install-client-dev:
 | 
			
		||||
	cd fittrackee_client && $(NPM) install
 | 
			
		||||
	# NPM_ARGS="--ignore-engines", if errors with Node latest version
 | 
			
		||||
	cd fittrackee_client && $(NPM) install $(NPM_ARGS)
 | 
			
		||||
 | 
			
		||||
install-dev: install-client-dev install-python-dev
 | 
			
		||||
 | 
			
		||||
@@ -162,12 +181,15 @@ serve-python-dev:
 | 
			
		||||
	$(FLASK) run --with-threads -h $(HOST) -p $(PORT) --cert=adhoc
 | 
			
		||||
 | 
			
		||||
set-admin:
 | 
			
		||||
	$(FLASK) users set-admin $(USERNAME)
 | 
			
		||||
	echo "Deprecated command, will be removed in a next version. Use 'user-set-admin' instead."
 | 
			
		||||
	$(FTCLI) users update $(USERNAME) --set-admin true
 | 
			
		||||
 | 
			
		||||
test-e2e: init-db
 | 
			
		||||
test-e2e:
 | 
			
		||||
	$(PYTEST) e2e --driver firefox $(PYTEST_ARGS)
 | 
			
		||||
 | 
			
		||||
test-e2e-client: init-db
 | 
			
		||||
test-all: test-client test-python
 | 
			
		||||
 | 
			
		||||
test-e2e-client:
 | 
			
		||||
	E2E_ARGS=client $(PYTEST) e2e --driver firefox $(PYTEST_ARGS)
 | 
			
		||||
 | 
			
		||||
test-python:
 | 
			
		||||
@@ -181,4 +203,17 @@ type-check:
 | 
			
		||||
	$(MYPY) fittrackee
 | 
			
		||||
 | 
			
		||||
upgrade-db:
 | 
			
		||||
	$(FLASK) db upgrade --directory $(MIGRATIONS)
 | 
			
		||||
	$(FTCLI) db upgrade
 | 
			
		||||
 | 
			
		||||
user-activate:
 | 
			
		||||
	$(FTCLI) users update $(USERNAME) --activate
 | 
			
		||||
 | 
			
		||||
user-reset-password:
 | 
			
		||||
	$(FTCLI) users update $(USERNAME) --reset-password
 | 
			
		||||
 | 
			
		||||
ADMIN := true
 | 
			
		||||
user-set-admin:
 | 
			
		||||
	$(FTCLI) users update $(USERNAME) --set-admin $(ADMIN)
 | 
			
		||||
 | 
			
		||||
user-update-email:
 | 
			
		||||
	$(FTCLI) users update $(USERNAME) --update-email $(EMAIL)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,15 +2,12 @@ export HOST = 0.0.0.0
 | 
			
		||||
export PORT = 5000
 | 
			
		||||
export CLIENT_PORT = 3000
 | 
			
		||||
 | 
			
		||||
export FLASK_APP = $(PWD)/fittrackee/__main__.py
 | 
			
		||||
export MIGRATIONS = $(PWD)/fittrackee/migrations
 | 
			
		||||
export APP_WORKERS = 1
 | 
			
		||||
export WORKERS_PROCESSES = 1
 | 
			
		||||
 | 
			
		||||
# for dev env
 | 
			
		||||
export FLASK_ENV = development
 | 
			
		||||
export APP_SETTINGS = fittrackee.config.DevelopmentConfig
 | 
			
		||||
export DATABASE_URL = postgresql://fittrackee:fittrackee@$(HOST):5432/fittrackee
 | 
			
		||||
export DATABASE_TEST_URL = postgresql://fittrackee:fittrackee@$(HOST):5432/fittrackee_test
 | 
			
		||||
export TEST_APP_URL = http://$(HOST):$(PORT)
 | 
			
		||||
export TEST_CLIENT_URL = http://$(HOST):$(CLIENT_PORT)
 | 
			
		||||
@@ -26,6 +23,9 @@ PYTEST = $(VENV)/bin/py.test -c pyproject.toml -W ignore::DeprecationWarning
 | 
			
		||||
GUNICORN = $(VENV)/bin/gunicorn
 | 
			
		||||
BLACK = $(VENV)/bin/black
 | 
			
		||||
MYPY = $(VENV)/bin/mypy
 | 
			
		||||
BANDIT = $(VENV)/bin/bandit
 | 
			
		||||
PYBABEL = $(VENV)/bin/pybabel
 | 
			
		||||
FTCLI = $(VENV)/bin/ftcli
 | 
			
		||||
 | 
			
		||||
# Node env
 | 
			
		||||
NODE_MODULES = $(PWD)/fittrackee_client/node_modules
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								README.md
									
									
									
									
									
								
							
							
						
						@@ -3,15 +3,15 @@
 | 
			
		||||
 | 
			
		||||
[](https://pypi.org/project/fittrackee/) 
 | 
			
		||||
[](https://python.org)
 | 
			
		||||
[](http://flask.pocoo.org/) 
 | 
			
		||||
[](http://flask.pocoo.org/) 
 | 
			
		||||
[](https://github.com/psf/black) 
 | 
			
		||||
[](http://mypy-lang.org/)  
 | 
			
		||||
[](https://v3.vuejs.org/) 
 | 
			
		||||
[](https://www.typescriptlang.org/) 
 | 
			
		||||
[](https://github.com/prettier/prettier)  
 | 
			
		||||
[](https://www.codacy.com/app/SamR1/FitTrackee)
 | 
			
		||||
[](https://gitlab.com/SamR1/FitTrackee/-/commits/master)
 | 
			
		||||
[](https://gitlab.com/SamR1/FitTrackee/-/commits/master) <sup><sup>1</sup></sup>
 | 
			
		||||
[](https://github.com/SamR1/FitTrackee/actions/workflows/.tests-python.yml)
 | 
			
		||||
[](https://github.com/SamR1/FitTrackee/actions/workflows/.tests-javascript.yml)
 | 
			
		||||
[](https://hosted.weblate.org/engage/fittrackee/)  
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
@@ -26,12 +26,11 @@ Examples for Android (non-exhaustive list):
 | 
			
		||||
Maps are displayed using [Open Street Map](https://www.openstreetmap.org).  
 | 
			
		||||
It is also possible to add a workout without a gpx file.
 | 
			
		||||
 | 
			
		||||
Translations can be updated through [Weblate](https://hosted.weblate.org/engage/fittrackee/).  
 | 
			
		||||
Available languages:  
 | 
			
		||||
[](https://hosted.weblate.org/engage/fittrackee/)
 | 
			
		||||
 | 
			
		||||
**Still under heavy development (some features may be unstable).**  
 | 
			
		||||
(see [issues](https://github.com/SamR1/FitTrackee/issues) and [documentation](https://samr1.github.io/FitTrackee) for more information)  
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
Notes:  
 | 
			
		||||
_1. Test coverage: only for Python API_
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								babel.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,5 @@
 | 
			
		||||
[jinja2: fittrackee/emails/templates/**.html]
 | 
			
		||||
silent=False
 | 
			
		||||
 | 
			
		||||
[jinja2: fittrackee/emails/templates/**.txt]
 | 
			
		||||
silent=False
 | 
			
		||||
@@ -1,5 +1,3 @@
 | 
			
		||||
FROM postgres:13
 | 
			
		||||
 | 
			
		||||
MAINTAINER SamR1@users.noreply.github.com
 | 
			
		||||
 | 
			
		||||
COPY create.sql /docker-entrypoint-initdb.d
 | 
			
		||||
@@ -19,7 +19,7 @@ services:
 | 
			
		||||
    ports:
 | 
			
		||||
      - "5000:5000"
 | 
			
		||||
    env_file:
 | 
			
		||||
      - .env.docker
 | 
			
		||||
      - .env
 | 
			
		||||
    depends_on:
 | 
			
		||||
      - fittrackee-db
 | 
			
		||||
      - redis
 | 
			
		||||
 
 | 
			
		||||
@@ -4,5 +4,5 @@ cd /usr/src/app
 | 
			
		||||
 | 
			
		||||
source .env.docker
 | 
			
		||||
 | 
			
		||||
flask drop-db
 | 
			
		||||
flask db upgrade --directory fittrackee/migrations
 | 
			
		||||
ftcli db drop
 | 
			
		||||
ftcli db upgrade
 | 
			
		||||
@@ -4,4 +4,4 @@ cd /usr/src/app
 | 
			
		||||
 | 
			
		||||
source .env.docker
 | 
			
		||||
 | 
			
		||||
flask users set-admin $1
 | 
			
		||||
ftcli users update $1 --set-admin true
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
# Sphinx build info version 1
 | 
			
		||||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
 | 
			
		||||
config: 1e2841616c48de88a07f12a07138022e
 | 
			
		||||
config: c52394c093f45e0ad0599926c90fff71
 | 
			
		||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
 | 
			
		||||
 
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 537 KiB After Width: | Height: | Size: 540 KiB  | 
| 
		 Before Width: | Height: | Size: 370 KiB After Width: | Height: | Size: 369 KiB  | 
| 
		 Before Width: | Height: | Size: 203 KiB After Width: | Height: | Size: 204 KiB  | 
| 
		 Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 69 KiB  | 
| 
		 Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 71 KiB  | 
| 
		 Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB  | 
@@ -4,8 +4,9 @@ Authentication
 | 
			
		||||
.. autoflask:: fittrackee:create_app()
 | 
			
		||||
   :endpoints:
 | 
			
		||||
    auth.register_user,
 | 
			
		||||
    auth.confirm_account,
 | 
			
		||||
    auth.resend_account_confirmation_email,
 | 
			
		||||
    auth.login_user,
 | 
			
		||||
    auth.logout_user,
 | 
			
		||||
    auth.get_authenticated_user_profile,
 | 
			
		||||
    auth.edit_user,
 | 
			
		||||
    auth.edit_user_preferences,
 | 
			
		||||
@@ -14,4 +15,6 @@ Authentication
 | 
			
		||||
    auth.edit_picture,
 | 
			
		||||
    auth.del_picture,
 | 
			
		||||
    auth.request_password_reset,
 | 
			
		||||
    auth.update_password
 | 
			
		||||
    auth.update_user_account,
 | 
			
		||||
    auth.update_password,
 | 
			
		||||
    auth.update_email
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,182 @@
 | 
			
		||||
# Change log
 | 
			
		||||
 | 
			
		||||
## Version 0.6.10 (2022/07/13)
 | 
			
		||||
 | 
			
		||||
### Issues Closed
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#210](https://github.com/SamR1/FitTrackee/issues/210) - ERROR - could not download 6 tiles  
 | 
			
		||||
  **Note**: for tile server requiring subdomains, see the new environment variable [`STATICMAP_SUBDOMAINS`](https://samr1.github.io/FitTrackee/installation.html#envvar-STATICMAP_SUBDOMAINS)
 | 
			
		||||
 | 
			
		||||
### Pull Requests
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#209](https://github.com/SamR1/FitTrackee/pull/209) - Incorrect duration with track containing multiple segments
 | 
			
		||||
 | 
			
		||||
Thanks to @gorgobacka
 | 
			
		||||
 | 
			
		||||
In this release 1 issue was closed.  
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.6.9 (2022/07/03)
 | 
			
		||||
 | 
			
		||||
FitTrackee is now available in German (thanks to @gorgobacka).  
 | 
			
		||||
And translations can be updated on Weblate.
 | 
			
		||||
 | 
			
		||||
### Issues Closed
 | 
			
		||||
 | 
			
		||||
#### Features
 | 
			
		||||
 | 
			
		||||
* [#200](https://github.com/SamR1/FitTrackee/issues/200) - Detect browser language to use matching translation if available
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [PR#208](https://github.com/SamR1/FitTrackee/pull/208) - fix order on records cards
 | 
			
		||||
* [#201](https://github.com/SamR1/FitTrackee/issues/201) - html lang attribute is not updated when changing language
 | 
			
		||||
 | 
			
		||||
#### Translations
 | 
			
		||||
 | 
			
		||||
* [PR#197](https://github.com/SamR1/FitTrackee/pull/197) - Translations update from Weblate (French)
 | 
			
		||||
* [#196](https://github.com/SamR1/FitTrackee/issues/196) - Use translation management tool
 | 
			
		||||
* [#190](https://github.com/SamR1/FitTrackee/issues/190) - Add German translation
 | 
			
		||||
 | 
			
		||||
In this release 4 issues were closed.  
 | 
			
		||||
 | 
			
		||||
Thanks to the contributors:
 | 
			
		||||
- @gorgobacka
 | 
			
		||||
- J. Lavoie (from Weblate)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.6.8 (2022/06/22)
 | 
			
		||||
 | 
			
		||||
### Issues Closed
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#193](https://github.com/SamR1/FitTrackee/issues/193) - Allow deleting a workout when files are missing
 | 
			
		||||
* [#192](https://github.com/SamR1/FitTrackee/issues/192) - Returns 404 instead of 500 when map file not found
 | 
			
		||||
* [#191](https://github.com/SamR1/FitTrackee/issues/191) - Layout issue on Workouts page
 | 
			
		||||
 | 
			
		||||
### Misc
 | 
			
		||||
 | 
			
		||||
* change gpx and map file naming (included in [PR#195](https://github.com/SamR1/FitTrackee/pull/195))  
 | 
			
		||||
  Note: it does not affect previously imported files
 | 
			
		||||
* [cc4287e](https://github.com/SamR1/FitTrackee/commit/cc4287ed327faaba268a0c689841d16a7aecc3fb) - Fix docker env file
 | 
			
		||||
 | 
			
		||||
In this release 3 issues were closed.  
 | 
			
		||||
 | 
			
		||||
## Version 0.6.7 (2022/06/11)
 | 
			
		||||
 | 
			
		||||
### Issues Closed
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#156](https://github.com/SamR1/FitTrackee/issues/156) - Process gpx file with offset
 | 
			
		||||
 | 
			
		||||
In this release 1 issue was closed.  
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.6.6 (2022/05/29)
 | 
			
		||||
 | 
			
		||||
### Misc
 | 
			
		||||
 | 
			
		||||
No new features in this release, only dependencies update and code refacto before introducing new features.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.6.5 (2022/04/24)
 | 
			
		||||
 | 
			
		||||
It is now possible to start FitTrackee without a configured SMTP provider (see [documentation](https://samr1.github.io/FitTrackee/installation.html#emails)).
 | 
			
		||||
It reduces pre-requisites for single-user instances.
 | 
			
		||||
 | 
			
		||||
To manage users, a new [CLI](https://samr1.github.io/FitTrackee/cli.html) is available.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Issues Closed
 | 
			
		||||
 | 
			
		||||
#### Features
 | 
			
		||||
 | 
			
		||||
* [#180](https://github.com/SamR1/FitTrackee/issues/180) - allow using FitTrackee without SMTP server
 | 
			
		||||
 | 
			
		||||
In this release 1 issue was closed.  
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.6.4 (2022/04/23)
 | 
			
		||||
 | 
			
		||||
### Issues Closed
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#178](https://github.com/SamR1/FitTrackee/issues/178) - cannot send email with TLS
 | 
			
		||||
 | 
			
		||||
In this release 1 issue was closed.  
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.6.3 (2022/04/09)
 | 
			
		||||
 | 
			
		||||
### Pull Requests
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#177](https://github.com/SamR1/FitTrackee/pull/177) - Minor fixes 
 | 
			
		||||
  * add missing translation 
 | 
			
		||||
  * fix 'Add Workout' card position on small screens
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.6.2 (2022/04/03)
 | 
			
		||||
 | 
			
		||||
### Issues Closed
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#175](https://github.com/SamR1/FitTrackee/issues/175) - Distance card on dashboard is not refreshed
 | 
			
		||||
* [#173](https://github.com/SamR1/FitTrackee/issues/173) - link to user profile in workout card is incorrect
 | 
			
		||||
 | 
			
		||||
In this release 2 issues were closed.  
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.6.1 (2022/03/27)
 | 
			
		||||
 | 
			
		||||
### Issues Closed
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#171](https://github.com/SamR1/FitTrackee/issues/171) - Stats chart is not updated correctly 
 | 
			
		||||
 | 
			
		||||
In this release 1 issue was closed.  
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.6.0 (2022/03/27)
 | 
			
		||||
 | 
			
		||||
This version introduces some changes on [user registration](https://samr1.github.io/FitTrackee/features.html#account-preferences).  
 | 
			
		||||
From now on, a user needs to confirm his account after registration (an email with confirmation instructions is sent after registration).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Issues Closed
 | 
			
		||||
 | 
			
		||||
#### Features
 | 
			
		||||
 | 
			
		||||
* [#155](https://github.com/SamR1/FitTrackee/issues/155) -  Improve user registration
 | 
			
		||||
* [#106](https://github.com/SamR1/FitTrackee/issues/106) -  Allow user to update email
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#169](https://github.com/SamR1/FitTrackee/issues/169) -  user picture is not refreshed after update
 | 
			
		||||
 | 
			
		||||
### Pull Requests
 | 
			
		||||
 | 
			
		||||
#### Bugs Fixed
 | 
			
		||||
 | 
			
		||||
* [#161](https://github.com/SamR1/FitTrackee/pull/161) - Minor translation issue on 'Farthest'
 | 
			
		||||
* [#160](https://github.com/SamR1/FitTrackee/pull/160) - Minor translation issue on APP_ERROR
 | 
			
		||||
 | 
			
		||||
Thanks to @Fmstrat
 | 
			
		||||
 | 
			
		||||
In this release 3 issues were closed.  
 | 
			
		||||
**Note:** This release contains database migration (see upgrade instructions in [documentation](https://samr1.github.io/FitTrackee/installation.html#upgrade))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Version 0.5.7 (2022/02/13)
 | 
			
		||||
 | 
			
		||||
This release contains several fixes including security fixes.  
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										67
									
								
								docs/_sources/cli.rst.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,67 @@
 | 
			
		||||
Command line interface
 | 
			
		||||
######################
 | 
			
		||||
 | 
			
		||||
A command line interface (CLI) is available to manage database and users.
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
    $ ftcli
 | 
			
		||||
    Usage: ftcli [OPTIONS] COMMAND [ARGS]...
 | 
			
		||||
 | 
			
		||||
      FitTrackee Command Line Interface
 | 
			
		||||
 | 
			
		||||
    Options:
 | 
			
		||||
      --help  Show this message and exit.
 | 
			
		||||
 | 
			
		||||
    Commands:
 | 
			
		||||
      db     Manage database.
 | 
			
		||||
      users  Manage users.
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
    | The following commands are now deprecated and will be removed in a next version:
 | 
			
		||||
    | - ``fittrackee_set_admin``
 | 
			
		||||
    | - ``fittrackee_upgrade_db``
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Database
 | 
			
		||||
~~~~~~~~
 | 
			
		||||
 | 
			
		||||
``ftcli db upgrade``
 | 
			
		||||
""""""""""""""""""""
 | 
			
		||||
.. versionadded:: 0.6.5
 | 
			
		||||
 | 
			
		||||
Apply migrations.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
``ftcli db drop``
 | 
			
		||||
"""""""""""""""""
 | 
			
		||||
.. versionadded:: 0.6.5
 | 
			
		||||
 | 
			
		||||
Empty database and delete uploaded files, only on development environments.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Users
 | 
			
		||||
~~~~~
 | 
			
		||||
 | 
			
		||||
``ftcli users update``
 | 
			
		||||
""""""""""""""""""""""
 | 
			
		||||
.. versionadded:: 0.6.5
 | 
			
		||||
 | 
			
		||||
Modify a user account (admin rights, active status, email and password).
 | 
			
		||||
 | 
			
		||||
.. cssclass:: table-bordered
 | 
			
		||||
.. list-table::
 | 
			
		||||
   :widths: 25 50
 | 
			
		||||
   :header-rows: 1
 | 
			
		||||
 | 
			
		||||
   * - Options
 | 
			
		||||
     - Description
 | 
			
		||||
   * - ``--set-admin BOOLEAN``
 | 
			
		||||
     - Add/remove admin rights (when adding admin rights, it also activates user account if not active).
 | 
			
		||||
   * - ``--activate``
 | 
			
		||||
     - Activate user account.
 | 
			
		||||
   * - ``--reset-password``
 | 
			
		||||
     - Reset user password (a new password will be displayed).
 | 
			
		||||
   * - ``--update-email EMAIL``
 | 
			
		||||
     - Update user email.
 | 
			
		||||
@@ -44,9 +44,13 @@ Workouts
 | 
			
		||||
    - average speed  (**new in 0.5.1**)
 | 
			
		||||
- User records by sports:
 | 
			
		||||
    - average speed
 | 
			
		||||
    - farest distance
 | 
			
		||||
    - farthest distance
 | 
			
		||||
    - longest duration
 | 
			
		||||
    - maximum speed
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
  Records may differ from records displayed by the application that originally generated the gpx files.
 | 
			
		||||
 | 
			
		||||
- Workouts list and filter. Only sports with workouts are displayed in sport dropdown.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
@@ -55,9 +59,17 @@ Workouts
 | 
			
		||||
 | 
			
		||||
Account & preferences
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
- A user can create, update and deleted his account
 | 
			
		||||
- A user can create, update and deleted his account.
 | 
			
		||||
- On registration, the user account is created with selected language in dropdown as user preference (*new in 0.6.9*).
 | 
			
		||||
- After registration, the user account is inactive and an email with confirmation instructions is sent to activate it.
 | 
			
		||||
  A user with an inactive account cannot log in. (*new in 0.6.0*)
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
  In case email sending is not configured, a `command line <cli.html#ftcli-users-update>`__ allows to activate users account.
 | 
			
		||||
 | 
			
		||||
- A user can set language, timezone and first day of week.
 | 
			
		||||
- A user can reset his password (*new in 0.3.0*)
 | 
			
		||||
- A user can change his email address (*new in 0.6.0*)
 | 
			
		||||
- A user can choose between metric system and imperial system for distance, elevation and speed display (*new in 0.5.0*)
 | 
			
		||||
- A user can set sport preferences (*new in 0.5.0*):
 | 
			
		||||
     - change sport color (used for sport image and charts)
 | 
			
		||||
@@ -82,15 +94,23 @@ Administration
 | 
			
		||||
  - maximum size of uploaded files
 | 
			
		||||
  - maximum size of zip archive
 | 
			
		||||
  - maximum number of files in the zip archive. If an archive contains more files, only the configured number of files is processed, without raising errors.
 | 
			
		||||
  - administrator email for contact (*new in 0.6.0*)
 | 
			
		||||
 | 
			
		||||
  .. warning::
 | 
			
		||||
      Updating server configuration may be necessary to handle large files (like `nginx <https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size>`_ for instance).
 | 
			
		||||
 | 
			
		||||
  .. note::
 | 
			
		||||
      If email sending is disabled, a warning is displayed.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
- **Users**
 | 
			
		||||
 | 
			
		||||
  - display users list and details
 | 
			
		||||
  - edit a user to add/remove administration rights
 | 
			
		||||
  - display and filter users list
 | 
			
		||||
  - edit a user to:
 | 
			
		||||
      - add/remove administration rights
 | 
			
		||||
      - activate his account (*new in 0.6.0*)
 | 
			
		||||
      - update his email (in case his account is locked) (*new in 0.6.0*)
 | 
			
		||||
      - reset his password (in case his account is locked) (*new in 0.6.0*). If email sending is disabled, it is only possible via CLI.
 | 
			
		||||
  - delete a user
 | 
			
		||||
 | 
			
		||||
- **Sports**
 | 
			
		||||
@@ -100,7 +120,9 @@ Administration
 | 
			
		||||
 | 
			
		||||
Translations
 | 
			
		||||
^^^^^^^^^^^^
 | 
			
		||||
FitTrackee is available in English and French (which can be saved in the user preferences).
 | 
			
		||||
FitTrackee is available in the following languages (which can be saved in the user preferences):
 | 
			
		||||
 | 
			
		||||
.. figure:: https://hosted.weblate.org/widgets/fittrackee/-/multi-auto.svg
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Screenshots
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,7 @@ Table of contents
 | 
			
		||||
 | 
			
		||||
   features
 | 
			
		||||
   installation
 | 
			
		||||
   cli
 | 
			
		||||
   api/index
 | 
			
		||||
   troubleshooting/index
 | 
			
		||||
   changelog
 | 
			
		||||
 
 | 
			
		||||
@@ -14,17 +14,17 @@ This application is written in Python (API) and Typescript (client):
 | 
			
		||||
    - `Leaflet <https://leafletjs.com/>`__ to display map
 | 
			
		||||
    - `Chart.js <https://www.chartjs.org/>`__ to display charts with elevation and speed
 | 
			
		||||
 | 
			
		||||
Logo, sports and weather icons are made by `Freepik <https://www.freepik.com/>`__ from `www.flaticon.com <https://www.flaticon.com/>`__.
 | 
			
		||||
| Logo, some sports and weather icons are made by `Freepik <https://www.freepik.com/>`__ from `www.flaticon.com <https://www.flaticon.com/>`__.
 | 
			
		||||
| FitTrackee also uses icons from `Fork Awesome <https://forkaweso.me>`__.
 | 
			
		||||
 | 
			
		||||
Prerequisites
 | 
			
		||||
~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
-  PostgreSQL database (10+)
 | 
			
		||||
-  Redis for task queue
 | 
			
		||||
-  Python 3.7+
 | 
			
		||||
-  PostgreSQL database (10+)
 | 
			
		||||
-  SMTP provider and Redis for task queue (if email sending is enabled)
 | 
			
		||||
-  API key from `Dark Sky <https://darksky.net/dev>`__ (not mandatory)
 | 
			
		||||
-  `Poetry <https://poetry.eustace.io>`__ (for installation from sources only)
 | 
			
		||||
-  API key from `Dark Sky <https://darksky.net/dev>`__ [not mandatory]
 | 
			
		||||
-  SMTP provider
 | 
			
		||||
-  `Yarn <https://yarnpkg.com>`__ (for development only)
 | 
			
		||||
-  Docker and Docker Compose (for development or evaluation purposes)
 | 
			
		||||
 | 
			
		||||
@@ -95,9 +95,9 @@ deployment method.
 | 
			
		||||
 | 
			
		||||
    .. versionadded:: 0.4.0
 | 
			
		||||
 | 
			
		||||
    Directory containing uploaded files.
 | 
			
		||||
    **Absolute path** to the directory where `uploads` folder will be created.
 | 
			
		||||
 | 
			
		||||
    :default: `fittrackee/uploads/`
 | 
			
		||||
    :default: `<application_directory>/fittrackee`
 | 
			
		||||
 | 
			
		||||
    .. danger::
 | 
			
		||||
        | With installation from PyPI, the directory will be located in
 | 
			
		||||
@@ -108,7 +108,7 @@ deployment method.
 | 
			
		||||
    | Database URL with username and password, must be initialized in production environment.
 | 
			
		||||
    | For example in dev environment : ``postgresql://fittrackee:fittrackee@localhost:5432/fittrackee``
 | 
			
		||||
 | 
			
		||||
    .. danger::
 | 
			
		||||
    .. warning::
 | 
			
		||||
        | Since `SQLAlchemy update (1.4+) <https://docs.sqlalchemy.org/en/14/changelog/changelog_14.html#change-3687655465c25a39b968b4f5f6e9170b>`__,
 | 
			
		||||
          engine URL should begin with `postgresql://`.
 | 
			
		||||
 | 
			
		||||
@@ -132,6 +132,13 @@ deployment method.
 | 
			
		||||
 | 
			
		||||
    Email URL with credentials, see `Emails <installation.html#emails>`__.
 | 
			
		||||
 | 
			
		||||
    .. versionchanged:: 0.6.5
 | 
			
		||||
 | 
			
		||||
    :default: empty string
 | 
			
		||||
 | 
			
		||||
    .. danger::
 | 
			
		||||
        If the email URL is empty, email sending will be disabled.
 | 
			
		||||
 | 
			
		||||
    .. warning::
 | 
			
		||||
        If the email URL is invalid, the application may not start.
 | 
			
		||||
 | 
			
		||||
@@ -168,6 +175,16 @@ deployment method.
 | 
			
		||||
    :default: `https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. envvar:: STATICMAP_SUBDOMAINS 🆕
 | 
			
		||||
 | 
			
		||||
    .. versionadded:: 0.6.10
 | 
			
		||||
 | 
			
		||||
    | Some tile servers require a subdomain, see `Map tile server <installation.html#map-tile-server>`__.
 | 
			
		||||
    | For instance: "a,b,c" for OSM France.
 | 
			
		||||
 | 
			
		||||
    :default: empty string
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. envvar:: MAP_ATTRIBUTION
 | 
			
		||||
 | 
			
		||||
    .. versionadded:: 0.4.0
 | 
			
		||||
@@ -177,11 +194,17 @@ deployment method.
 | 
			
		||||
    :default: `© <a href="http://www.openstreetmap.org/copyright" target="_blank" rel="noopener noreferrer">OpenStreetMap</a> contributors`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. envvar:: DEFAULT_STATICMAP 🆕
 | 
			
		||||
.. envvar:: DEFAULT_STATICMAP
 | 
			
		||||
 | 
			
		||||
    .. versionadded:: 0.4.9
 | 
			
		||||
 | 
			
		||||
    If `True`, it keeps using default tile server to generate static maps.
 | 
			
		||||
    | If `True`, it keeps using default tile server to generate static maps (Komoot.de tile server).
 | 
			
		||||
    | Otherwise, it uses the tile server set in `TILE_SERVER_URL <installation.html#envvar-TILE_SERVER_URL>`__.
 | 
			
		||||
 | 
			
		||||
    .. versionchanged:: 0.6.10
 | 
			
		||||
 | 
			
		||||
    | This variable is now case-insensitive.
 | 
			
		||||
    | If `False`, depending on tile server, `subdomains <installation.html#envvar-STATICMAP_SUBDOMAINS>`__ may be mandatory.
 | 
			
		||||
 | 
			
		||||
    :default: False
 | 
			
		||||
 | 
			
		||||
@@ -209,12 +232,31 @@ To send emails, a valid ``EMAIL_URL`` must be provided:
 | 
			
		||||
- with SSL: ``smtp://username:password@smtp.example.com:465/?ssl=True``
 | 
			
		||||
- with STARTTLS: ``smtp://username:password@smtp.example.com:587/?tls=True``
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
    | - If the email URL is invalid, the application may not start.
 | 
			
		||||
    | - Sending emails with Office365 may not work if SMTP auth is disabled.
 | 
			
		||||
 | 
			
		||||
.. versionadded:: 0.5.3
 | 
			
		||||
.. versionchanged:: 0.5.3
 | 
			
		||||
 | 
			
		||||
| Credentials can be omitted: ``smtp://smtp.example.com:25``.
 | 
			
		||||
| If ``:<port>`` is omitted, the port defaults to 25.
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
     | Since 0.6.0, newly created accounts must be confirmed (an email with confirmation instructions is sent after registration).
 | 
			
		||||
 | 
			
		||||
Emails sent by FitTrackee are:
 | 
			
		||||
 | 
			
		||||
- account confirmation instructions
 | 
			
		||||
- password reset request
 | 
			
		||||
- email change (to old and new email adresses)
 | 
			
		||||
- password change
 | 
			
		||||
 | 
			
		||||
.. versionchanged:: 0.6.5
 | 
			
		||||
 | 
			
		||||
| For single-user instance, it is possible to disable email sending with an empty ``EMAIL_URL`` (in this case, no need to start dramatiq workers).
 | 
			
		||||
| A `CLI <cli.html#ftcli-users-update>`__ is available to activate account and modify email and password.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Map tile server
 | 
			
		||||
^^^^^^^^^^^^^^^
 | 
			
		||||
.. versionadded:: 0.4.0
 | 
			
		||||
@@ -230,6 +272,20 @@ To keep using **ThunderForest Outdoors**, the configuration is:
 | 
			
		||||
.. note::
 | 
			
		||||
    | Check the terms of service of tile provider for map attribution
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. versionchanged:: 0.6.10
 | 
			
		||||
 | 
			
		||||
Since the tile server can be used for static map generation, some servers require a subdomain.
 | 
			
		||||
 | 
			
		||||
For instance, to set OSM France tile server, the expected values are:
 | 
			
		||||
 | 
			
		||||
- ``TILE_SERVER_URL=https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png``
 | 
			
		||||
- ``MAP_ATTRIBUTION='fond de carte par <a href="http://www.openstreetmap.fr/mentions-legales/" target="_blank" rel="nofollow noopener">OpenStreetMap France</a>, sous <a href="http://creativecommons.org/licenses/by-sa/2.0/fr/" target="_blank" rel="nofollow noopener">licence CC BY-SA</a>'``
 | 
			
		||||
- ``STATICMAP_SUBDOMAINS=a,b,c``
 | 
			
		||||
 | 
			
		||||
The subdomain will be chosen randomly.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Installation
 | 
			
		||||
~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
@@ -273,7 +329,7 @@ For instance, copy and update ``.env`` file from ``.env.example`` and source the
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
    $ fittrackee_upgrade_db
 | 
			
		||||
    $ ftcli db upgrade
 | 
			
		||||
 | 
			
		||||
- Start the application
 | 
			
		||||
 | 
			
		||||
@@ -281,7 +337,7 @@ For instance, copy and update ``.env`` file from ``.env.example`` and source the
 | 
			
		||||
 | 
			
		||||
    $ fittrackee
 | 
			
		||||
 | 
			
		||||
- Start task queue workers
 | 
			
		||||
- Start task queue workers if email sending is enabled.
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
@@ -292,12 +348,14 @@ For instance, copy and update ``.env`` file from ``.env.example`` and source the
 | 
			
		||||
 | 
			
		||||
- Open http://localhost:3000 and register
 | 
			
		||||
 | 
			
		||||
- To set admin rights to the newly created account, use the following command:
 | 
			
		||||
- To set admin rights to the newly created account, use the following command line:
 | 
			
		||||
 | 
			
		||||
.. code:: bash
 | 
			
		||||
 | 
			
		||||
   $ fittrackee_set_admin <username>
 | 
			
		||||
   $ ftcli users update <username> --set-admin true
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
    If the user account is inactive, it activates it.
 | 
			
		||||
 | 
			
		||||
From sources
 | 
			
		||||
^^^^^^^^^^^^
 | 
			
		||||
@@ -352,12 +410,14 @@ Dev environment
 | 
			
		||||
 | 
			
		||||
- Open http://localhost:3000 and register
 | 
			
		||||
 | 
			
		||||
- To set admin rights to the newly created account, use the following command:
 | 
			
		||||
- To set admin rights to the newly created account, use the following command line:
 | 
			
		||||
 | 
			
		||||
.. code:: bash
 | 
			
		||||
 | 
			
		||||
   $ make set-admin USERNAME=<username>
 | 
			
		||||
   $ make user-set-admin USERNAME=<username>
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
    If the user account is inactive, it activates it.
 | 
			
		||||
 | 
			
		||||
Production environment
 | 
			
		||||
""""""""""""""""""""""
 | 
			
		||||
@@ -365,13 +425,13 @@ Production environment
 | 
			
		||||
.. warning::
 | 
			
		||||
    | Note that FitTrackee is under heavy development, some features may be unstable.
 | 
			
		||||
 | 
			
		||||
-  Download the last release (for now, it is the release v0.5.7):
 | 
			
		||||
-  Download the last release (for now, it is the release v0.6.10):
 | 
			
		||||
 | 
			
		||||
.. code:: bash
 | 
			
		||||
 | 
			
		||||
   $ wget https://github.com/SamR1/FitTrackee/archive/v0.5.7.tar.gz
 | 
			
		||||
   $ tar -xzf v0.5.7.tar.gz
 | 
			
		||||
   $ mv FitTrackee-0.5.7 FitTrackee
 | 
			
		||||
   $ wget https://github.com/SamR1/FitTrackee/archive/v0.6.10.tar.gz
 | 
			
		||||
   $ tar -xzf v0.6.10.tar.gz
 | 
			
		||||
   $ mv FitTrackee-0.6.10 FitTrackee
 | 
			
		||||
   $ cd FitTrackee
 | 
			
		||||
 | 
			
		||||
-  Create **.env** from example and update it
 | 
			
		||||
@@ -396,14 +456,19 @@ Production environment
 | 
			
		||||
 | 
			
		||||
   $ make run
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
    If email sending is disabled: ``$ make run-server``
 | 
			
		||||
 | 
			
		||||
- Open http://localhost:5000 and register
 | 
			
		||||
 | 
			
		||||
- To set admin rights to the newly created account, use the following command:
 | 
			
		||||
- To set admin rights to the newly created account, use the following command line:
 | 
			
		||||
 | 
			
		||||
.. code:: bash
 | 
			
		||||
 | 
			
		||||
   $ make set-admin USERNAME=<username>
 | 
			
		||||
   $ make user-set-admin USERNAME=<username>
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
    If the user account is inactive, it activates it.
 | 
			
		||||
 | 
			
		||||
Upgrade
 | 
			
		||||
~~~~~~~
 | 
			
		||||
@@ -417,7 +482,7 @@ Upgrade
 | 
			
		||||
From PyPI
 | 
			
		||||
^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
- Activate the virtualenv
 | 
			
		||||
- Stop the application and activate the virtualenv
 | 
			
		||||
 | 
			
		||||
- Upgrade with pip
 | 
			
		||||
 | 
			
		||||
@@ -436,10 +501,9 @@ From PyPI
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
    $ fittrackee_upgrade_db
 | 
			
		||||
    $ ftcli db upgrade
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
- Restart the application and task queue workers.
 | 
			
		||||
- Restart the application and task queue workers (if email sending is enabled).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
From sources
 | 
			
		||||
@@ -487,13 +551,13 @@ Prod environment
 | 
			
		||||
 | 
			
		||||
- Change to the directory where FitTrackee directory is located
 | 
			
		||||
 | 
			
		||||
- Download the last release (for now, it is the release v0.5.7) and overwrite existing files:
 | 
			
		||||
- Download the last release (for now, it is the release v0.6.10) and overwrite existing files:
 | 
			
		||||
 | 
			
		||||
.. code:: bash
 | 
			
		||||
 | 
			
		||||
   $ wget https://github.com/SamR1/FitTrackee/archive/v0.5.7.tar.gz
 | 
			
		||||
   $ tar -xzf v0.5.7.tar.gz
 | 
			
		||||
   $ cp -R FitTrackee-0.5.7/* FitTrackee/
 | 
			
		||||
   $ wget https://github.com/SamR1/FitTrackee/archive/v0.6.10.tar.gz
 | 
			
		||||
   $ tar -xzf v0.6.10.tar.gz
 | 
			
		||||
   $ cp -R FitTrackee-0.6.10/* FitTrackee/
 | 
			
		||||
   $ cd FitTrackee
 | 
			
		||||
 | 
			
		||||
- Update **.env** if needed (see `Environment variables <installation.html#environment-variables>`__).
 | 
			
		||||
@@ -516,6 +580,8 @@ Prod environment
 | 
			
		||||
 | 
			
		||||
   $ make run
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
    If email sending is disabled: ``$ make run-server``
 | 
			
		||||
 | 
			
		||||
Deployment
 | 
			
		||||
~~~~~~~~~~
 | 
			
		||||
@@ -554,6 +620,7 @@ Examples (to update depending on your application configuration and given distri
 | 
			
		||||
    Environment="SENDER_EMAIL="
 | 
			
		||||
    Environment="REDIS_URL="
 | 
			
		||||
    Environment="TILE_SERVER_URL="
 | 
			
		||||
    Environment="STATICMAP_SUBDOMAINS="
 | 
			
		||||
    Environment="MAP_ATTRIBUTION="
 | 
			
		||||
    Environment="WEATHER_API_KEY="
 | 
			
		||||
    WorkingDirectory=/home/<USER>/<FITTRACKEE DIRECTORY>
 | 
			
		||||
@@ -641,8 +708,7 @@ Installation
 | 
			
		||||
 | 
			
		||||
.. versionadded:: 0.4.4
 | 
			
		||||
 | 
			
		||||
For evaluation purposes , docker files are available,
 | 
			
		||||
installing **FitTrackee** from **sources**.
 | 
			
		||||
For evaluation purposes, docker files are available, installing **FitTrackee** from **sources**.
 | 
			
		||||
 | 
			
		||||
- To install **FitTrackee** with database initialisation and run the application and dramatiq workers:
 | 
			
		||||
 | 
			
		||||
@@ -650,18 +716,22 @@ installing **FitTrackee** from **sources**.
 | 
			
		||||
 | 
			
		||||
    $ git clone https://github.com/SamR1/FitTrackee.git
 | 
			
		||||
    $ cd FitTrackee
 | 
			
		||||
    $ cp .env.docker .env
 | 
			
		||||
    $ make docker-build docker-run docker-init
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
- To set admin rights to the newly created account, use the following command line:
 | 
			
		||||
 | 
			
		||||
.. code:: bash
 | 
			
		||||
 | 
			
		||||
   $ make docker-set-admin USERNAME=<username>
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
    If the user account is inactive, it activates it.
 | 
			
		||||
 | 
			
		||||
- To stop **Fittrackee**:
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 
 | 
			
		||||
@@ -5,10 +5,24 @@ Administrator
 | 
			
		||||
`FitTrackee fails to start`
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
- Check the database URL in `Environment variables <../installation.html#envvar-DATABASE_URL>`__ if the following error is displayed in **gunicorn** logs:
 | 
			
		||||
- Check the database URL in `environment variables <../installation.html#envvar-DATABASE_URL>`__ if the following error is displayed in **gunicorn** logs:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
  .. code::
 | 
			
		||||
 | 
			
		||||
   sqlalchemy.exc.NoSuchModuleError: Can't load plugin: sqlalchemy.dialects:postgres
 | 
			
		||||
     sqlalchemy.exc.NoSuchModuleError: Can't load plugin: sqlalchemy.dialects:postgres
 | 
			
		||||
 | 
			
		||||
It must start with `postgresql://` (engine URLs starting with `postgres://` are no longer supported).
 | 
			
		||||
  It must start with `postgresql://` (engine URLs starting with `postgres://` are no longer supported).
 | 
			
		||||
 | 
			
		||||
- Check the email URL in `environment variables <../installation.html#envvar-EMAIL_URL>`__ if the following error is displayed in **gunicorn** logs:
 | 
			
		||||
 | 
			
		||||
  .. code::
 | 
			
		||||
 | 
			
		||||
     fittrackee.emails.exceptions.InvalidEmailUrlScheme
 | 
			
		||||
 | 
			
		||||
  A valid ``EMAIL_URL`` must be provided (see `emails <../installation.html#emails>`__).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
`Map images are not displayed but map is shown in Workout detail`
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
- Check the path in `environment variables <../installation.html#envvar-UPLOAD_FOLDER>`__. ``UPLOAD_FOLDER`` must be set with an absolute path.
 | 
			
		||||
@@ -3,7 +3,6 @@ Troubleshooting
 | 
			
		||||
 | 
			
		||||
.. toctree::
 | 
			
		||||
   :maxdepth: 2
 | 
			
		||||
   :caption: Endpoints:
 | 
			
		||||
 | 
			
		||||
   administrator
 | 
			
		||||
   user
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										134
									
								
								docs/_static/_sphinx_javascript_frameworks_compat.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,134 @@
 | 
			
		||||
/*
 | 
			
		||||
 * _sphinx_javascript_frameworks_compat.js
 | 
			
		||||
 * ~~~~~~~~~~
 | 
			
		||||
 *
 | 
			
		||||
 * Compatability shim for jQuery and underscores.js.
 | 
			
		||||
 *
 | 
			
		||||
 * WILL BE REMOVED IN Sphinx 6.0
 | 
			
		||||
 * xref RemovedInSphinx60Warning
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * select a different prefix for underscore
 | 
			
		||||
 */
 | 
			
		||||
$u = _.noConflict();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * small helper function to urldecode strings
 | 
			
		||||
 *
 | 
			
		||||
 * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL
 | 
			
		||||
 */
 | 
			
		||||
jQuery.urldecode = function(x) {
 | 
			
		||||
    if (!x) {
 | 
			
		||||
        return x
 | 
			
		||||
    }
 | 
			
		||||
    return decodeURIComponent(x.replace(/\+/g, ' '));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * small helper function to urlencode strings
 | 
			
		||||
 */
 | 
			
		||||
jQuery.urlencode = encodeURIComponent;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This function returns the parsed url parameters of the
 | 
			
		||||
 * current request. Multiple values per key are supported,
 | 
			
		||||
 * it will always return arrays of strings for the value parts.
 | 
			
		||||
 */
 | 
			
		||||
jQuery.getQueryParameters = function(s) {
 | 
			
		||||
    if (typeof s === 'undefined')
 | 
			
		||||
        s = document.location.search;
 | 
			
		||||
    var parts = s.substr(s.indexOf('?') + 1).split('&');
 | 
			
		||||
    var result = {};
 | 
			
		||||
    for (var i = 0; i < parts.length; i++) {
 | 
			
		||||
        var tmp = parts[i].split('=', 2);
 | 
			
		||||
        var key = jQuery.urldecode(tmp[0]);
 | 
			
		||||
        var value = jQuery.urldecode(tmp[1]);
 | 
			
		||||
        if (key in result)
 | 
			
		||||
            result[key].push(value);
 | 
			
		||||
        else
 | 
			
		||||
            result[key] = [value];
 | 
			
		||||
    }
 | 
			
		||||
    return result;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * highlight a given string on a jquery object by wrapping it in
 | 
			
		||||
 * span elements with the given class name.
 | 
			
		||||
 */
 | 
			
		||||
jQuery.fn.highlightText = function(text, className) {
 | 
			
		||||
    function highlight(node, addItems) {
 | 
			
		||||
        if (node.nodeType === 3) {
 | 
			
		||||
            var val = node.nodeValue;
 | 
			
		||||
            var pos = val.toLowerCase().indexOf(text);
 | 
			
		||||
            if (pos >= 0 &&
 | 
			
		||||
                !jQuery(node.parentNode).hasClass(className) &&
 | 
			
		||||
                !jQuery(node.parentNode).hasClass("nohighlight")) {
 | 
			
		||||
                var span;
 | 
			
		||||
                var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
 | 
			
		||||
                if (isInSVG) {
 | 
			
		||||
                    span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
 | 
			
		||||
                } else {
 | 
			
		||||
                    span = document.createElement("span");
 | 
			
		||||
                    span.className = className;
 | 
			
		||||
                }
 | 
			
		||||
                span.appendChild(document.createTextNode(val.substr(pos, text.length)));
 | 
			
		||||
                node.parentNode.insertBefore(span, node.parentNode.insertBefore(
 | 
			
		||||
                    document.createTextNode(val.substr(pos + text.length)),
 | 
			
		||||
                    node.nextSibling));
 | 
			
		||||
                node.nodeValue = val.substr(0, pos);
 | 
			
		||||
                if (isInSVG) {
 | 
			
		||||
                    var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
 | 
			
		||||
                    var bbox = node.parentElement.getBBox();
 | 
			
		||||
                    rect.x.baseVal.value = bbox.x;
 | 
			
		||||
                    rect.y.baseVal.value = bbox.y;
 | 
			
		||||
                    rect.width.baseVal.value = bbox.width;
 | 
			
		||||
                    rect.height.baseVal.value = bbox.height;
 | 
			
		||||
                    rect.setAttribute('class', className);
 | 
			
		||||
                    addItems.push({
 | 
			
		||||
                        "parent": node.parentNode,
 | 
			
		||||
                        "target": rect});
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (!jQuery(node).is("button, select, textarea")) {
 | 
			
		||||
            jQuery.each(node.childNodes, function() {
 | 
			
		||||
                highlight(this, addItems);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    var addItems = [];
 | 
			
		||||
    var result = this.each(function() {
 | 
			
		||||
        highlight(this, addItems);
 | 
			
		||||
    });
 | 
			
		||||
    for (var i = 0; i < addItems.length; ++i) {
 | 
			
		||||
        jQuery(addItems[i].parent).before(addItems[i].target);
 | 
			
		||||
    }
 | 
			
		||||
    return result;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * backward compatibility for jQuery.browser
 | 
			
		||||
 * This will be supported until firefox bug is fixed.
 | 
			
		||||
 */
 | 
			
		||||
if (!jQuery.browser) {
 | 
			
		||||
    jQuery.uaMatch = function(ua) {
 | 
			
		||||
        ua = ua.toLowerCase();
 | 
			
		||||
 | 
			
		||||
        var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
 | 
			
		||||
            /(webkit)[ \/]([\w.]+)/.exec(ua) ||
 | 
			
		||||
            /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
 | 
			
		||||
            /(msie) ([\w.]+)/.exec(ua) ||
 | 
			
		||||
            ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
 | 
			
		||||
            [];
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            browser: match[ 1 ] || "",
 | 
			
		||||
            version: match[ 2 ] || "0"
 | 
			
		||||
        };
 | 
			
		||||
    };
 | 
			
		||||
    jQuery.browser = {};
 | 
			
		||||
    jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										54
									
								
								docs/_static/basic.css
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -222,7 +222,7 @@ table.modindextable td {
 | 
			
		||||
/* -- general body styles --------------------------------------------------- */
 | 
			
		||||
 | 
			
		||||
div.body {
 | 
			
		||||
    min-width: 450px;
 | 
			
		||||
    min-width: 360px;
 | 
			
		||||
    max-width: 800px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -237,16 +237,6 @@ a.headerlink {
 | 
			
		||||
    visibility: hidden;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
a.brackets:before,
 | 
			
		||||
span.brackets > a:before{
 | 
			
		||||
    content: "[";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
a.brackets:after,
 | 
			
		||||
span.brackets > a:after {
 | 
			
		||||
    content: "]";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
h1:hover > a.headerlink,
 | 
			
		||||
h2:hover > a.headerlink,
 | 
			
		||||
h3:hover > a.headerlink,
 | 
			
		||||
@@ -334,12 +324,16 @@ aside.sidebar {
 | 
			
		||||
p.sidebar-title {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
nav.contents,
 | 
			
		||||
aside.topic,
 | 
			
		||||
 | 
			
		||||
div.admonition, div.topic, blockquote {
 | 
			
		||||
    clear: left;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* -- topics ---------------------------------------------------------------- */
 | 
			
		||||
nav.contents,
 | 
			
		||||
aside.topic,
 | 
			
		||||
 | 
			
		||||
div.topic {
 | 
			
		||||
    border: 1px solid #ccc;
 | 
			
		||||
@@ -379,6 +373,9 @@ div.body p.centered {
 | 
			
		||||
 | 
			
		||||
div.sidebar > :last-child,
 | 
			
		||||
aside.sidebar > :last-child,
 | 
			
		||||
nav.contents > :last-child,
 | 
			
		||||
aside.topic > :last-child,
 | 
			
		||||
 | 
			
		||||
div.topic > :last-child,
 | 
			
		||||
div.admonition > :last-child {
 | 
			
		||||
    margin-bottom: 0;
 | 
			
		||||
@@ -386,6 +383,9 @@ div.admonition > :last-child {
 | 
			
		||||
 | 
			
		||||
div.sidebar::after,
 | 
			
		||||
aside.sidebar::after,
 | 
			
		||||
nav.contents::after,
 | 
			
		||||
aside.topic::after,
 | 
			
		||||
 | 
			
		||||
div.topic::after,
 | 
			
		||||
div.admonition::after,
 | 
			
		||||
blockquote::after {
 | 
			
		||||
@@ -428,10 +428,6 @@ table.docutils td, table.docutils th {
 | 
			
		||||
    border-bottom: 1px solid #aaa;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
table.footnote td, table.footnote th {
 | 
			
		||||
    border: 0 !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
th {
 | 
			
		||||
    text-align: left;
 | 
			
		||||
    padding-right: 5px;
 | 
			
		||||
@@ -615,6 +611,7 @@ ul.simple p {
 | 
			
		||||
    margin-bottom: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Docutils 0.17 and older (footnotes & citations) */
 | 
			
		||||
dl.footnote > dt,
 | 
			
		||||
dl.citation > dt {
 | 
			
		||||
    float: left;
 | 
			
		||||
@@ -632,6 +629,33 @@ dl.citation > dd:after {
 | 
			
		||||
    clear: both;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Docutils 0.18+ (footnotes & citations) */
 | 
			
		||||
aside.footnote > span,
 | 
			
		||||
div.citation > span {
 | 
			
		||||
    float: left;
 | 
			
		||||
}
 | 
			
		||||
aside.footnote > span:last-of-type,
 | 
			
		||||
div.citation > span:last-of-type {
 | 
			
		||||
  padding-right: 0.5em;
 | 
			
		||||
}
 | 
			
		||||
aside.footnote > p {
 | 
			
		||||
  margin-left: 2em;
 | 
			
		||||
}
 | 
			
		||||
div.citation > p {
 | 
			
		||||
  margin-left: 4em;
 | 
			
		||||
}
 | 
			
		||||
aside.footnote > p:last-of-type,
 | 
			
		||||
div.citation > p:last-of-type {
 | 
			
		||||
    margin-bottom: 0em;
 | 
			
		||||
}
 | 
			
		||||
aside.footnote > p:last-of-type:after,
 | 
			
		||||
div.citation > p:last-of-type:after {
 | 
			
		||||
    content: "";
 | 
			
		||||
    clear: both;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Footnotes & citations ends */
 | 
			
		||||
 | 
			
		||||
dl.field-list {
 | 
			
		||||
    display: grid;
 | 
			
		||||
    grid-template-columns: fit-content(30%) auto;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										452
									
								
								docs/_static/doctools.js
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -2,325 +2,263 @@
 | 
			
		||||
 * doctools.js
 | 
			
		||||
 * ~~~~~~~~~~~
 | 
			
		||||
 *
 | 
			
		||||
 * Sphinx JavaScript utilities for all documentation.
 | 
			
		||||
 * Base JavaScript utilities for all Sphinx HTML documentation.
 | 
			
		||||
 *
 | 
			
		||||
 * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS.
 | 
			
		||||
 * :license: BSD, see LICENSE for details.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
"use strict";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * select a different prefix for underscore
 | 
			
		||||
 */
 | 
			
		||||
$u = _.noConflict();
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * make the code below compatible with browsers without
 | 
			
		||||
 * an installed firebug like debugger
 | 
			
		||||
if (!window.console || !console.firebug) {
 | 
			
		||||
  var names = ["log", "debug", "info", "warn", "error", "assert", "dir",
 | 
			
		||||
    "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace",
 | 
			
		||||
    "profile", "profileEnd"];
 | 
			
		||||
  window.console = {};
 | 
			
		||||
  for (var i = 0; i < names.length; ++i)
 | 
			
		||||
    window.console[names[i]] = function() {};
 | 
			
		||||
}
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * small helper function to urldecode strings
 | 
			
		||||
 *
 | 
			
		||||
 * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL
 | 
			
		||||
 */
 | 
			
		||||
jQuery.urldecode = function(x) {
 | 
			
		||||
  if (!x) {
 | 
			
		||||
    return x
 | 
			
		||||
const _ready = (callback) => {
 | 
			
		||||
  if (document.readyState !== "loading") {
 | 
			
		||||
    callback();
 | 
			
		||||
  } else {
 | 
			
		||||
    document.addEventListener("DOMContentLoaded", callback);
 | 
			
		||||
  }
 | 
			
		||||
  return decodeURIComponent(x.replace(/\+/g, ' '));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * small helper function to urlencode strings
 | 
			
		||||
 */
 | 
			
		||||
jQuery.urlencode = encodeURIComponent;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This function returns the parsed url parameters of the
 | 
			
		||||
 * current request. Multiple values per key are supported,
 | 
			
		||||
 * it will always return arrays of strings for the value parts.
 | 
			
		||||
 */
 | 
			
		||||
jQuery.getQueryParameters = function(s) {
 | 
			
		||||
  if (typeof s === 'undefined')
 | 
			
		||||
    s = document.location.search;
 | 
			
		||||
  var parts = s.substr(s.indexOf('?') + 1).split('&');
 | 
			
		||||
  var result = {};
 | 
			
		||||
  for (var i = 0; i < parts.length; i++) {
 | 
			
		||||
    var tmp = parts[i].split('=', 2);
 | 
			
		||||
    var key = jQuery.urldecode(tmp[0]);
 | 
			
		||||
    var value = jQuery.urldecode(tmp[1]);
 | 
			
		||||
    if (key in result)
 | 
			
		||||
      result[key].push(value);
 | 
			
		||||
    else
 | 
			
		||||
      result[key] = [value];
 | 
			
		||||
  }
 | 
			
		||||
  return result;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * highlight a given string on a jquery object by wrapping it in
 | 
			
		||||
 * highlight a given string on a node by wrapping it in
 | 
			
		||||
 * span elements with the given class name.
 | 
			
		||||
 */
 | 
			
		||||
jQuery.fn.highlightText = function(text, className) {
 | 
			
		||||
  function highlight(node, addItems) {
 | 
			
		||||
    if (node.nodeType === 3) {
 | 
			
		||||
      var val = node.nodeValue;
 | 
			
		||||
      var pos = val.toLowerCase().indexOf(text);
 | 
			
		||||
      if (pos >= 0 &&
 | 
			
		||||
          !jQuery(node.parentNode).hasClass(className) &&
 | 
			
		||||
          !jQuery(node.parentNode).hasClass("nohighlight")) {
 | 
			
		||||
        var span;
 | 
			
		||||
        var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
 | 
			
		||||
        if (isInSVG) {
 | 
			
		||||
          span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
 | 
			
		||||
        } else {
 | 
			
		||||
          span = document.createElement("span");
 | 
			
		||||
          span.className = className;
 | 
			
		||||
        }
 | 
			
		||||
        span.appendChild(document.createTextNode(val.substr(pos, text.length)));
 | 
			
		||||
        node.parentNode.insertBefore(span, node.parentNode.insertBefore(
 | 
			
		||||
const _highlight = (node, addItems, text, className) => {
 | 
			
		||||
  if (node.nodeType === Node.TEXT_NODE) {
 | 
			
		||||
    const val = node.nodeValue;
 | 
			
		||||
    const parent = node.parentNode;
 | 
			
		||||
    const pos = val.toLowerCase().indexOf(text);
 | 
			
		||||
    if (
 | 
			
		||||
      pos >= 0 &&
 | 
			
		||||
      !parent.classList.contains(className) &&
 | 
			
		||||
      !parent.classList.contains("nohighlight")
 | 
			
		||||
    ) {
 | 
			
		||||
      let span;
 | 
			
		||||
 | 
			
		||||
      const closestNode = parent.closest("body, svg, foreignObject");
 | 
			
		||||
      const isInSVG = closestNode && closestNode.matches("svg");
 | 
			
		||||
      if (isInSVG) {
 | 
			
		||||
        span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
 | 
			
		||||
      } else {
 | 
			
		||||
        span = document.createElement("span");
 | 
			
		||||
        span.classList.add(className);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      span.appendChild(document.createTextNode(val.substr(pos, text.length)));
 | 
			
		||||
      parent.insertBefore(
 | 
			
		||||
        span,
 | 
			
		||||
        parent.insertBefore(
 | 
			
		||||
          document.createTextNode(val.substr(pos + text.length)),
 | 
			
		||||
          node.nextSibling));
 | 
			
		||||
        node.nodeValue = val.substr(0, pos);
 | 
			
		||||
        if (isInSVG) {
 | 
			
		||||
          var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
 | 
			
		||||
          var bbox = node.parentElement.getBBox();
 | 
			
		||||
          rect.x.baseVal.value = bbox.x;
 | 
			
		||||
          rect.y.baseVal.value = bbox.y;
 | 
			
		||||
          rect.width.baseVal.value = bbox.width;
 | 
			
		||||
          rect.height.baseVal.value = bbox.height;
 | 
			
		||||
          rect.setAttribute('class', className);
 | 
			
		||||
          addItems.push({
 | 
			
		||||
              "parent": node.parentNode,
 | 
			
		||||
              "target": rect});
 | 
			
		||||
        }
 | 
			
		||||
          node.nextSibling
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
      node.nodeValue = val.substr(0, pos);
 | 
			
		||||
 | 
			
		||||
      if (isInSVG) {
 | 
			
		||||
        const rect = document.createElementNS(
 | 
			
		||||
          "http://www.w3.org/2000/svg",
 | 
			
		||||
          "rect"
 | 
			
		||||
        );
 | 
			
		||||
        const bbox = parent.getBBox();
 | 
			
		||||
        rect.x.baseVal.value = bbox.x;
 | 
			
		||||
        rect.y.baseVal.value = bbox.y;
 | 
			
		||||
        rect.width.baseVal.value = bbox.width;
 | 
			
		||||
        rect.height.baseVal.value = bbox.height;
 | 
			
		||||
        rect.setAttribute("class", className);
 | 
			
		||||
        addItems.push({ parent: parent, target: rect });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    else if (!jQuery(node).is("button, select, textarea")) {
 | 
			
		||||
      jQuery.each(node.childNodes, function() {
 | 
			
		||||
        highlight(this, addItems);
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  } else if (node.matches && !node.matches("button, select, textarea")) {
 | 
			
		||||
    node.childNodes.forEach((el) => _highlight(el, addItems, text, className));
 | 
			
		||||
  }
 | 
			
		||||
  var addItems = [];
 | 
			
		||||
  var result = this.each(function() {
 | 
			
		||||
    highlight(this, addItems);
 | 
			
		||||
  });
 | 
			
		||||
  for (var i = 0; i < addItems.length; ++i) {
 | 
			
		||||
    jQuery(addItems[i].parent).before(addItems[i].target);
 | 
			
		||||
  }
 | 
			
		||||
  return result;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * backward compatibility for jQuery.browser
 | 
			
		||||
 * This will be supported until firefox bug is fixed.
 | 
			
		||||
 */
 | 
			
		||||
if (!jQuery.browser) {
 | 
			
		||||
  jQuery.uaMatch = function(ua) {
 | 
			
		||||
    ua = ua.toLowerCase();
 | 
			
		||||
 | 
			
		||||
    var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
 | 
			
		||||
      /(webkit)[ \/]([\w.]+)/.exec(ua) ||
 | 
			
		||||
      /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
 | 
			
		||||
      /(msie) ([\w.]+)/.exec(ua) ||
 | 
			
		||||
      ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
 | 
			
		||||
      [];
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      browser: match[ 1 ] || "",
 | 
			
		||||
      version: match[ 2 ] || "0"
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
  jQuery.browser = {};
 | 
			
		||||
  jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
 | 
			
		||||
}
 | 
			
		||||
const _highlightText = (thisNode, text, className) => {
 | 
			
		||||
  let addItems = [];
 | 
			
		||||
  _highlight(thisNode, addItems, text, className);
 | 
			
		||||
  addItems.forEach((obj) =>
 | 
			
		||||
    obj.parent.insertAdjacentElement("beforebegin", obj.target)
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Small JavaScript module for the documentation.
 | 
			
		||||
 */
 | 
			
		||||
var Documentation = {
 | 
			
		||||
 | 
			
		||||
  init : function() {
 | 
			
		||||
    this.fixFirefoxAnchorBug();
 | 
			
		||||
    this.highlightSearchWords();
 | 
			
		||||
    this.initIndexTable();
 | 
			
		||||
    if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) {
 | 
			
		||||
      this.initOnKeyListeners();
 | 
			
		||||
    }
 | 
			
		||||
const Documentation = {
 | 
			
		||||
  init: () => {
 | 
			
		||||
    Documentation.highlightSearchWords();
 | 
			
		||||
    Documentation.initDomainIndexTable();
 | 
			
		||||
    Documentation.initOnKeyListeners();
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * i18n support
 | 
			
		||||
   */
 | 
			
		||||
  TRANSLATIONS : {},
 | 
			
		||||
  PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; },
 | 
			
		||||
  LOCALE : 'unknown',
 | 
			
		||||
  TRANSLATIONS: {},
 | 
			
		||||
  PLURAL_EXPR: (n) => (n === 1 ? 0 : 1),
 | 
			
		||||
  LOCALE: "unknown",
 | 
			
		||||
 | 
			
		||||
  // gettext and ngettext don't access this so that the functions
 | 
			
		||||
  // can safely bound to a different name (_ = Documentation.gettext)
 | 
			
		||||
  gettext : function(string) {
 | 
			
		||||
    var translated = Documentation.TRANSLATIONS[string];
 | 
			
		||||
    if (typeof translated === 'undefined')
 | 
			
		||||
      return string;
 | 
			
		||||
    return (typeof translated === 'string') ? translated : translated[0];
 | 
			
		||||
  gettext: (string) => {
 | 
			
		||||
    const translated = Documentation.TRANSLATIONS[string];
 | 
			
		||||
    switch (typeof translated) {
 | 
			
		||||
      case "undefined":
 | 
			
		||||
        return string; // no translation
 | 
			
		||||
      case "string":
 | 
			
		||||
        return translated; // translation exists
 | 
			
		||||
      default:
 | 
			
		||||
        return translated[0]; // (singular, plural) translation tuple exists
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  ngettext : function(singular, plural, n) {
 | 
			
		||||
    var translated = Documentation.TRANSLATIONS[singular];
 | 
			
		||||
    if (typeof translated === 'undefined')
 | 
			
		||||
      return (n == 1) ? singular : plural;
 | 
			
		||||
    return translated[Documentation.PLURALEXPR(n)];
 | 
			
		||||
  ngettext: (singular, plural, n) => {
 | 
			
		||||
    const translated = Documentation.TRANSLATIONS[singular];
 | 
			
		||||
    if (typeof translated !== "undefined")
 | 
			
		||||
      return translated[Documentation.PLURAL_EXPR(n)];
 | 
			
		||||
    return n === 1 ? singular : plural;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  addTranslations : function(catalog) {
 | 
			
		||||
    for (var key in catalog.messages)
 | 
			
		||||
      this.TRANSLATIONS[key] = catalog.messages[key];
 | 
			
		||||
    this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')');
 | 
			
		||||
    this.LOCALE = catalog.locale;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * add context elements like header anchor links
 | 
			
		||||
   */
 | 
			
		||||
  addContextElements : function() {
 | 
			
		||||
    $('div[id] > :header:first').each(function() {
 | 
			
		||||
      $('<a class="headerlink">\u00B6</a>').
 | 
			
		||||
      attr('href', '#' + this.id).
 | 
			
		||||
      attr('title', _('Permalink to this headline')).
 | 
			
		||||
      appendTo(this);
 | 
			
		||||
    });
 | 
			
		||||
    $('dt[id]').each(function() {
 | 
			
		||||
      $('<a class="headerlink">\u00B6</a>').
 | 
			
		||||
      attr('href', '#' + this.id).
 | 
			
		||||
      attr('title', _('Permalink to this definition')).
 | 
			
		||||
      appendTo(this);
 | 
			
		||||
    });
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * workaround a firefox stupidity
 | 
			
		||||
   * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075
 | 
			
		||||
   */
 | 
			
		||||
  fixFirefoxAnchorBug : function() {
 | 
			
		||||
    if (document.location.hash && $.browser.mozilla)
 | 
			
		||||
      window.setTimeout(function() {
 | 
			
		||||
        document.location.href += '';
 | 
			
		||||
      }, 10);
 | 
			
		||||
  addTranslations: (catalog) => {
 | 
			
		||||
    Object.assign(Documentation.TRANSLATIONS, catalog.messages);
 | 
			
		||||
    Documentation.PLURAL_EXPR = new Function(
 | 
			
		||||
      "n",
 | 
			
		||||
      `return (${catalog.plural_expr})`
 | 
			
		||||
    );
 | 
			
		||||
    Documentation.LOCALE = catalog.locale;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * highlight the search words provided in the url in the text
 | 
			
		||||
   */
 | 
			
		||||
  highlightSearchWords : function() {
 | 
			
		||||
    var params = $.getQueryParameters();
 | 
			
		||||
    var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : [];
 | 
			
		||||
    if (terms.length) {
 | 
			
		||||
      var body = $('div.body');
 | 
			
		||||
      if (!body.length) {
 | 
			
		||||
        body = $('body');
 | 
			
		||||
      }
 | 
			
		||||
      window.setTimeout(function() {
 | 
			
		||||
        $.each(terms, function() {
 | 
			
		||||
          body.highlightText(this.toLowerCase(), 'highlighted');
 | 
			
		||||
        });
 | 
			
		||||
      }, 10);
 | 
			
		||||
      $('<p class="highlight-link"><a href="javascript:Documentation.' +
 | 
			
		||||
        'hideSearchWords()">' + _('Hide Search Matches') + '</a></p>')
 | 
			
		||||
          .appendTo($('#searchbox'));
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  highlightSearchWords: () => {
 | 
			
		||||
    const highlight =
 | 
			
		||||
      new URLSearchParams(window.location.search).get("highlight") || "";
 | 
			
		||||
    const terms = highlight.toLowerCase().split(/\s+/).filter(x => x);
 | 
			
		||||
    if (terms.length === 0) return; // nothing to do
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * init the domain index toggle buttons
 | 
			
		||||
   */
 | 
			
		||||
  initIndexTable : function() {
 | 
			
		||||
    var togglers = $('img.toggler').click(function() {
 | 
			
		||||
      var src = $(this).attr('src');
 | 
			
		||||
      var idnum = $(this).attr('id').substr(7);
 | 
			
		||||
      $('tr.cg-' + idnum).toggle();
 | 
			
		||||
      if (src.substr(-9) === 'minus.png')
 | 
			
		||||
        $(this).attr('src', src.substr(0, src.length-9) + 'plus.png');
 | 
			
		||||
      else
 | 
			
		||||
        $(this).attr('src', src.substr(0, src.length-8) + 'minus.png');
 | 
			
		||||
    }).css('display', '');
 | 
			
		||||
    if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) {
 | 
			
		||||
        togglers.click();
 | 
			
		||||
    }
 | 
			
		||||
    // There should never be more than one element matching "div.body"
 | 
			
		||||
    const divBody = document.querySelectorAll("div.body");
 | 
			
		||||
    const body = divBody.length ? divBody[0] : document.querySelector("body");
 | 
			
		||||
    window.setTimeout(() => {
 | 
			
		||||
      terms.forEach((term) => _highlightText(body, term, "highlighted"));
 | 
			
		||||
    }, 10);
 | 
			
		||||
 | 
			
		||||
    const searchBox = document.getElementById("searchbox");
 | 
			
		||||
    if (searchBox === null) return;
 | 
			
		||||
    searchBox.appendChild(
 | 
			
		||||
      document
 | 
			
		||||
        .createRange()
 | 
			
		||||
        .createContextualFragment(
 | 
			
		||||
          '<p class="highlight-link">' +
 | 
			
		||||
            '<a href="javascript:Documentation.hideSearchWords()">' +
 | 
			
		||||
            Documentation.gettext("Hide Search Matches") +
 | 
			
		||||
            "</a></p>"
 | 
			
		||||
        )
 | 
			
		||||
    );
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * helper function to hide the search marks again
 | 
			
		||||
   */
 | 
			
		||||
  hideSearchWords : function() {
 | 
			
		||||
    $('#searchbox .highlight-link').fadeOut(300);
 | 
			
		||||
    $('span.highlighted').removeClass('highlighted');
 | 
			
		||||
    var url = new URL(window.location);
 | 
			
		||||
    url.searchParams.delete('highlight');
 | 
			
		||||
    window.history.replaceState({}, '', url);
 | 
			
		||||
  hideSearchWords: () => {
 | 
			
		||||
    document
 | 
			
		||||
      .querySelectorAll("#searchbox .highlight-link")
 | 
			
		||||
      .forEach((el) => el.remove());
 | 
			
		||||
    document
 | 
			
		||||
      .querySelectorAll("span.highlighted")
 | 
			
		||||
      .forEach((el) => el.classList.remove("highlighted"));
 | 
			
		||||
    const url = new URL(window.location);
 | 
			
		||||
    url.searchParams.delete("highlight");
 | 
			
		||||
    window.history.replaceState({}, "", url);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * make the url absolute
 | 
			
		||||
   * helper function to focus on search bar
 | 
			
		||||
   */
 | 
			
		||||
  makeURL : function(relativeURL) {
 | 
			
		||||
    return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL;
 | 
			
		||||
  focusSearchBar: () => {
 | 
			
		||||
    document.querySelectorAll("input[name=q]")[0]?.focus();
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * get the current relative url
 | 
			
		||||
   * Initialise the domain index toggle buttons
 | 
			
		||||
   */
 | 
			
		||||
  getCurrentURL : function() {
 | 
			
		||||
    var path = document.location.pathname;
 | 
			
		||||
    var parts = path.split(/\//);
 | 
			
		||||
    $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() {
 | 
			
		||||
      if (this === '..')
 | 
			
		||||
        parts.pop();
 | 
			
		||||
    });
 | 
			
		||||
    var url = parts.join('/');
 | 
			
		||||
    return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
 | 
			
		||||
  initDomainIndexTable: () => {
 | 
			
		||||
    const toggler = (el) => {
 | 
			
		||||
      const idNumber = el.id.substr(7);
 | 
			
		||||
      const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`);
 | 
			
		||||
      if (el.src.substr(-9) === "minus.png") {
 | 
			
		||||
        el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`;
 | 
			
		||||
        toggledRows.forEach((el) => (el.style.display = "none"));
 | 
			
		||||
      } else {
 | 
			
		||||
        el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`;
 | 
			
		||||
        toggledRows.forEach((el) => (el.style.display = ""));
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const togglerElements = document.querySelectorAll("img.toggler");
 | 
			
		||||
    togglerElements.forEach((el) =>
 | 
			
		||||
      el.addEventListener("click", (event) => toggler(event.currentTarget))
 | 
			
		||||
    );
 | 
			
		||||
    togglerElements.forEach((el) => (el.style.display = ""));
 | 
			
		||||
    if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  initOnKeyListeners: function() {
 | 
			
		||||
    $(document).keydown(function(event) {
 | 
			
		||||
      var activeElementType = document.activeElement.tagName;
 | 
			
		||||
      // don't navigate when in search box, textarea, dropdown or button
 | 
			
		||||
      if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT'
 | 
			
		||||
          && activeElementType !== 'BUTTON' && !event.altKey && !event.ctrlKey && !event.metaKey
 | 
			
		||||
          && !event.shiftKey) {
 | 
			
		||||
        switch (event.keyCode) {
 | 
			
		||||
          case 37: // left
 | 
			
		||||
            var prevHref = $('link[rel="prev"]').prop('href');
 | 
			
		||||
            if (prevHref) {
 | 
			
		||||
              window.location.href = prevHref;
 | 
			
		||||
              return false;
 | 
			
		||||
  initOnKeyListeners: () => {
 | 
			
		||||
    // only install a listener if it is really needed
 | 
			
		||||
    if (
 | 
			
		||||
      !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS &&
 | 
			
		||||
      !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
 | 
			
		||||
    )
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    const blacklistedElements = new Set([
 | 
			
		||||
      "TEXTAREA",
 | 
			
		||||
      "INPUT",
 | 
			
		||||
      "SELECT",
 | 
			
		||||
      "BUTTON",
 | 
			
		||||
    ]);
 | 
			
		||||
    document.addEventListener("keydown", (event) => {
 | 
			
		||||
      if (blacklistedElements.has(document.activeElement.tagName)) return; // bail for input elements
 | 
			
		||||
      if (event.altKey || event.ctrlKey || event.metaKey) return; // bail with special keys
 | 
			
		||||
 | 
			
		||||
      if (!event.shiftKey) {
 | 
			
		||||
        switch (event.key) {
 | 
			
		||||
          case "ArrowLeft":
 | 
			
		||||
            if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
 | 
			
		||||
 | 
			
		||||
            const prevLink = document.querySelector('link[rel="prev"]');
 | 
			
		||||
            if (prevLink && prevLink.href) {
 | 
			
		||||
              window.location.href = prevLink.href;
 | 
			
		||||
              event.preventDefault();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
          case 39: // right
 | 
			
		||||
            var nextHref = $('link[rel="next"]').prop('href');
 | 
			
		||||
            if (nextHref) {
 | 
			
		||||
              window.location.href = nextHref;
 | 
			
		||||
              return false;
 | 
			
		||||
          case "ArrowRight":
 | 
			
		||||
            if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
 | 
			
		||||
 | 
			
		||||
            const nextLink = document.querySelector('link[rel="next"]');
 | 
			
		||||
            if (nextLink && nextLink.href) {
 | 
			
		||||
              window.location.href = nextLink.href;
 | 
			
		||||
              event.preventDefault();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
          case "Escape":
 | 
			
		||||
            if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
 | 
			
		||||
            Documentation.hideSearchWords();
 | 
			
		||||
            event.preventDefault();
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // some keyboard layouts may need Shift to get /
 | 
			
		||||
      switch (event.key) {
 | 
			
		||||
        case "/":
 | 
			
		||||
          if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
 | 
			
		||||
          Documentation.focusSearchBar();
 | 
			
		||||
          event.preventDefault();
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// quick alias for translations
 | 
			
		||||
_ = Documentation.gettext;
 | 
			
		||||
const _ = Documentation.gettext;
 | 
			
		||||
 | 
			
		||||
$(document).ready(function() {
 | 
			
		||||
  Documentation.init();
 | 
			
		||||
});
 | 
			
		||||
_ready(Documentation.init);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								docs/_static/documentation_options.js
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1,12 +1,14 @@
 | 
			
		||||
var DOCUMENTATION_OPTIONS = {
 | 
			
		||||
    URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
 | 
			
		||||
    VERSION: '0.5.7',
 | 
			
		||||
    LANGUAGE: 'None',
 | 
			
		||||
    VERSION: '0.6.10',
 | 
			
		||||
    LANGUAGE: 'en',
 | 
			
		||||
    COLLAPSE_INDEX: false,
 | 
			
		||||
    BUILDER: 'html',
 | 
			
		||||
    FILE_SUFFIX: '.html',
 | 
			
		||||
    LINK_SUFFIX: '.html',
 | 
			
		||||
    HAS_SOURCE: true,
 | 
			
		||||
    SOURCELINK_SUFFIX: '.txt',
 | 
			
		||||
    NAVIGATION_WITH_KEYS: false
 | 
			
		||||
    NAVIGATION_WITH_KEYS: false,
 | 
			
		||||
    SHOW_SEARCH_SUMMARY: true,
 | 
			
		||||
    ENABLE_SEARCH_SHORTCUTS: false,
 | 
			
		||||
};
 | 
			
		||||
@@ -1,15 +1,15 @@
 | 
			
		||||
/*!
 | 
			
		||||
 * jQuery JavaScript Library v3.5.1
 | 
			
		||||
 * jQuery JavaScript Library v3.6.0
 | 
			
		||||
 * https://jquery.com/
 | 
			
		||||
 *
 | 
			
		||||
 * Includes Sizzle.js
 | 
			
		||||
 * https://sizzlejs.com/
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright JS Foundation and other contributors
 | 
			
		||||
 * Copyright OpenJS Foundation and other contributors
 | 
			
		||||
 * Released under the MIT license
 | 
			
		||||
 * https://jquery.org/license
 | 
			
		||||
 *
 | 
			
		||||
 * Date: 2020-05-04T22:49Z
 | 
			
		||||
 * Date: 2021-03-02T17:08Z
 | 
			
		||||
 */
 | 
			
		||||
( function( global, factory ) {
 | 
			
		||||
 | 
			
		||||
@@ -76,12 +76,16 @@ var support = {};
 | 
			
		||||
 | 
			
		||||
var isFunction = function isFunction( obj ) {
 | 
			
		||||
 | 
			
		||||
      // Support: Chrome <=57, Firefox <=52
 | 
			
		||||
      // In some browsers, typeof returns "function" for HTML <object> elements
 | 
			
		||||
      // (i.e., `typeof document.createElement( "object" ) === "function"`).
 | 
			
		||||
      // We don't want to classify *any* DOM node as a function.
 | 
			
		||||
      return typeof obj === "function" && typeof obj.nodeType !== "number";
 | 
			
		||||
  };
 | 
			
		||||
		// Support: Chrome <=57, Firefox <=52
 | 
			
		||||
		// In some browsers, typeof returns "function" for HTML <object> elements
 | 
			
		||||
		// (i.e., `typeof document.createElement( "object" ) === "function"`).
 | 
			
		||||
		// We don't want to classify *any* DOM node as a function.
 | 
			
		||||
		// Support: QtWeb <=3.8.5, WebKit <=534.34, wkhtmltopdf tool <=0.12.5
 | 
			
		||||
		// Plus for old WebKit, typeof returns "function" for HTML collections
 | 
			
		||||
		// (e.g., `typeof document.getElementsByTagName("div") === "function"`). (gh-4756)
 | 
			
		||||
		return typeof obj === "function" && typeof obj.nodeType !== "number" &&
 | 
			
		||||
			typeof obj.item !== "function";
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
var isWindow = function isWindow( obj ) {
 | 
			
		||||
@@ -147,7 +151,7 @@ function toType( obj ) {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
var
 | 
			
		||||
	version = "3.5.1",
 | 
			
		||||
	version = "3.6.0",
 | 
			
		||||
 | 
			
		||||
	// Define a local copy of jQuery
 | 
			
		||||
	jQuery = function( selector, context ) {
 | 
			
		||||
@@ -401,7 +405,7 @@ jQuery.extend( {
 | 
			
		||||
			if ( isArrayLike( Object( arr ) ) ) {
 | 
			
		||||
				jQuery.merge( ret,
 | 
			
		||||
					typeof arr === "string" ?
 | 
			
		||||
					[ arr ] : arr
 | 
			
		||||
						[ arr ] : arr
 | 
			
		||||
				);
 | 
			
		||||
			} else {
 | 
			
		||||
				push.call( ret, arr );
 | 
			
		||||
@@ -496,9 +500,9 @@ if ( typeof Symbol === "function" ) {
 | 
			
		||||
 | 
			
		||||
// Populate the class2type map
 | 
			
		||||
jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
 | 
			
		||||
function( _i, name ) {
 | 
			
		||||
	class2type[ "[object " + name + "]" ] = name.toLowerCase();
 | 
			
		||||
} );
 | 
			
		||||
	function( _i, name ) {
 | 
			
		||||
		class2type[ "[object " + name + "]" ] = name.toLowerCase();
 | 
			
		||||
	} );
 | 
			
		||||
 | 
			
		||||
function isArrayLike( obj ) {
 | 
			
		||||
 | 
			
		||||
@@ -518,14 +522,14 @@ function isArrayLike( obj ) {
 | 
			
		||||
}
 | 
			
		||||
var Sizzle =
 | 
			
		||||
/*!
 | 
			
		||||
 * Sizzle CSS Selector Engine v2.3.5
 | 
			
		||||
 * Sizzle CSS Selector Engine v2.3.6
 | 
			
		||||
 * https://sizzlejs.com/
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright JS Foundation and other contributors
 | 
			
		||||
 * Released under the MIT license
 | 
			
		||||
 * https://js.foundation/
 | 
			
		||||
 *
 | 
			
		||||
 * Date: 2020-03-14
 | 
			
		||||
 * Date: 2021-02-16
 | 
			
		||||
 */
 | 
			
		||||
( function( window ) {
 | 
			
		||||
var i,
 | 
			
		||||
@@ -1108,8 +1112,8 @@ support = Sizzle.support = {};
 | 
			
		||||
 * @returns {Boolean} True iff elem is a non-HTML XML node
 | 
			
		||||
 */
 | 
			
		||||
isXML = Sizzle.isXML = function( elem ) {
 | 
			
		||||
	var namespace = elem.namespaceURI,
 | 
			
		||||
		docElem = ( elem.ownerDocument || elem ).documentElement;
 | 
			
		||||
	var namespace = elem && elem.namespaceURI,
 | 
			
		||||
		docElem = elem && ( elem.ownerDocument || elem ).documentElement;
 | 
			
		||||
 | 
			
		||||
	// Support: IE <=8
 | 
			
		||||
	// Assume HTML when documentElement doesn't yet exist, such as inside loading iframes
 | 
			
		||||
@@ -3024,9 +3028,9 @@ var rneedsContext = jQuery.expr.match.needsContext;
 | 
			
		||||
 | 
			
		||||
function nodeName( elem, name ) {
 | 
			
		||||
 | 
			
		||||
  return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
 | 
			
		||||
	return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -3997,8 +4001,8 @@ jQuery.extend( {
 | 
			
		||||
			resolveContexts = Array( i ),
 | 
			
		||||
			resolveValues = slice.call( arguments ),
 | 
			
		||||
 | 
			
		||||
			// the master Deferred
 | 
			
		||||
			master = jQuery.Deferred(),
 | 
			
		||||
			// the primary Deferred
 | 
			
		||||
			primary = jQuery.Deferred(),
 | 
			
		||||
 | 
			
		||||
			// subordinate callback factory
 | 
			
		||||
			updateFunc = function( i ) {
 | 
			
		||||
@@ -4006,30 +4010,30 @@ jQuery.extend( {
 | 
			
		||||
					resolveContexts[ i ] = this;
 | 
			
		||||
					resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
 | 
			
		||||
					if ( !( --remaining ) ) {
 | 
			
		||||
						master.resolveWith( resolveContexts, resolveValues );
 | 
			
		||||
						primary.resolveWith( resolveContexts, resolveValues );
 | 
			
		||||
					}
 | 
			
		||||
				};
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
		// Single- and empty arguments are adopted like Promise.resolve
 | 
			
		||||
		if ( remaining <= 1 ) {
 | 
			
		||||
			adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject,
 | 
			
		||||
			adoptValue( singleValue, primary.done( updateFunc( i ) ).resolve, primary.reject,
 | 
			
		||||
				!remaining );
 | 
			
		||||
 | 
			
		||||
			// Use .then() to unwrap secondary thenables (cf. gh-3000)
 | 
			
		||||
			if ( master.state() === "pending" ||
 | 
			
		||||
			if ( primary.state() === "pending" ||
 | 
			
		||||
				isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {
 | 
			
		||||
 | 
			
		||||
				return master.then();
 | 
			
		||||
				return primary.then();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Multiple arguments are aggregated like Promise.all array elements
 | 
			
		||||
		while ( i-- ) {
 | 
			
		||||
			adoptValue( resolveValues[ i ], updateFunc( i ), master.reject );
 | 
			
		||||
			adoptValue( resolveValues[ i ], updateFunc( i ), primary.reject );
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return master.promise();
 | 
			
		||||
		return primary.promise();
 | 
			
		||||
	}
 | 
			
		||||
} );
 | 
			
		||||
 | 
			
		||||
@@ -4180,8 +4184,8 @@ var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
 | 
			
		||||
			for ( ; i < len; i++ ) {
 | 
			
		||||
				fn(
 | 
			
		||||
					elems[ i ], key, raw ?
 | 
			
		||||
					value :
 | 
			
		||||
					value.call( elems[ i ], i, fn( elems[ i ], key ) )
 | 
			
		||||
						value :
 | 
			
		||||
						value.call( elems[ i ], i, fn( elems[ i ], key ) )
 | 
			
		||||
				);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -5089,10 +5093,7 @@ function buildFragment( elems, context, scripts, selection, ignored ) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
var
 | 
			
		||||
	rkeyEvent = /^key/,
 | 
			
		||||
	rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,
 | 
			
		||||
	rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
 | 
			
		||||
var rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
 | 
			
		||||
 | 
			
		||||
function returnTrue() {
 | 
			
		||||
	return true;
 | 
			
		||||
@@ -5387,8 +5388,8 @@ jQuery.event = {
 | 
			
		||||
			event = jQuery.event.fix( nativeEvent ),
 | 
			
		||||
 | 
			
		||||
			handlers = (
 | 
			
		||||
					dataPriv.get( this, "events" ) || Object.create( null )
 | 
			
		||||
				)[ event.type ] || [],
 | 
			
		||||
				dataPriv.get( this, "events" ) || Object.create( null )
 | 
			
		||||
			)[ event.type ] || [],
 | 
			
		||||
			special = jQuery.event.special[ event.type ] || {};
 | 
			
		||||
 | 
			
		||||
		// Use the fix-ed jQuery.Event rather than the (read-only) native event
 | 
			
		||||
@@ -5512,12 +5513,12 @@ jQuery.event = {
 | 
			
		||||
			get: isFunction( hook ) ?
 | 
			
		||||
				function() {
 | 
			
		||||
					if ( this.originalEvent ) {
 | 
			
		||||
							return hook( this.originalEvent );
 | 
			
		||||
						return hook( this.originalEvent );
 | 
			
		||||
					}
 | 
			
		||||
				} :
 | 
			
		||||
				function() {
 | 
			
		||||
					if ( this.originalEvent ) {
 | 
			
		||||
							return this.originalEvent[ name ];
 | 
			
		||||
						return this.originalEvent[ name ];
 | 
			
		||||
					}
 | 
			
		||||
				},
 | 
			
		||||
 | 
			
		||||
@@ -5656,7 +5657,13 @@ function leverageNative( el, type, expectSync ) {
 | 
			
		||||
						// Cancel the outer synthetic event
 | 
			
		||||
						event.stopImmediatePropagation();
 | 
			
		||||
						event.preventDefault();
 | 
			
		||||
						return result.value;
 | 
			
		||||
 | 
			
		||||
						// Support: Chrome 86+
 | 
			
		||||
						// In Chrome, if an element having a focusout handler is blurred by
 | 
			
		||||
						// clicking outside of it, it invokes the handler synchronously. If
 | 
			
		||||
						// that handler calls `.remove()` on the element, the data is cleared,
 | 
			
		||||
						// leaving `result` undefined. We need to guard against this.
 | 
			
		||||
						return result && result.value;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
				// If this is an inner synthetic event for an event with a bubbling surrogate
 | 
			
		||||
@@ -5821,34 +5828,7 @@ jQuery.each( {
 | 
			
		||||
	targetTouches: true,
 | 
			
		||||
	toElement: true,
 | 
			
		||||
	touches: true,
 | 
			
		||||
 | 
			
		||||
	which: function( event ) {
 | 
			
		||||
		var button = event.button;
 | 
			
		||||
 | 
			
		||||
		// Add which for key events
 | 
			
		||||
		if ( event.which == null && rkeyEvent.test( event.type ) ) {
 | 
			
		||||
			return event.charCode != null ? event.charCode : event.keyCode;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Add which for click: 1 === left; 2 === middle; 3 === right
 | 
			
		||||
		if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) {
 | 
			
		||||
			if ( button & 1 ) {
 | 
			
		||||
				return 1;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if ( button & 2 ) {
 | 
			
		||||
				return 3;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if ( button & 4 ) {
 | 
			
		||||
				return 2;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return event.which;
 | 
			
		||||
	}
 | 
			
		||||
	which: true
 | 
			
		||||
}, jQuery.event.addProp );
 | 
			
		||||
 | 
			
		||||
jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) {
 | 
			
		||||
@@ -5874,6 +5854,12 @@ jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateTyp
 | 
			
		||||
			return true;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		// Suppress native focus or blur as it's already being fired
 | 
			
		||||
		// in leverageNative.
 | 
			
		||||
		_default: function() {
 | 
			
		||||
			return true;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		delegateType: delegateType
 | 
			
		||||
	};
 | 
			
		||||
} );
 | 
			
		||||
@@ -6541,6 +6527,10 @@ var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
 | 
			
		||||
		// set in CSS while `offset*` properties report correct values.
 | 
			
		||||
		// Behavior in IE 9 is more subtle than in newer versions & it passes
 | 
			
		||||
		// some versions of this test; make sure not to make it pass there!
 | 
			
		||||
		//
 | 
			
		||||
		// Support: Firefox 70+
 | 
			
		||||
		// Only Firefox includes border widths
 | 
			
		||||
		// in computed dimensions. (gh-4529)
 | 
			
		||||
		reliableTrDimensions: function() {
 | 
			
		||||
			var table, tr, trChild, trStyle;
 | 
			
		||||
			if ( reliableTrDimensionsVal == null ) {
 | 
			
		||||
@@ -6548,17 +6538,32 @@ var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
 | 
			
		||||
				tr = document.createElement( "tr" );
 | 
			
		||||
				trChild = document.createElement( "div" );
 | 
			
		||||
 | 
			
		||||
				table.style.cssText = "position:absolute;left:-11111px";
 | 
			
		||||
				table.style.cssText = "position:absolute;left:-11111px;border-collapse:separate";
 | 
			
		||||
				tr.style.cssText = "border:1px solid";
 | 
			
		||||
 | 
			
		||||
				// Support: Chrome 86+
 | 
			
		||||
				// Height set through cssText does not get applied.
 | 
			
		||||
				// Computed height then comes back as 0.
 | 
			
		||||
				tr.style.height = "1px";
 | 
			
		||||
				trChild.style.height = "9px";
 | 
			
		||||
 | 
			
		||||
				// Support: Android 8 Chrome 86+
 | 
			
		||||
				// In our bodyBackground.html iframe,
 | 
			
		||||
				// display for all div elements is set to "inline",
 | 
			
		||||
				// which causes a problem only in Android 8 Chrome 86.
 | 
			
		||||
				// Ensuring the div is display: block
 | 
			
		||||
				// gets around this issue.
 | 
			
		||||
				trChild.style.display = "block";
 | 
			
		||||
 | 
			
		||||
				documentElement
 | 
			
		||||
					.appendChild( table )
 | 
			
		||||
					.appendChild( tr )
 | 
			
		||||
					.appendChild( trChild );
 | 
			
		||||
 | 
			
		||||
				trStyle = window.getComputedStyle( tr );
 | 
			
		||||
				reliableTrDimensionsVal = parseInt( trStyle.height ) > 3;
 | 
			
		||||
				reliableTrDimensionsVal = ( parseInt( trStyle.height, 10 ) +
 | 
			
		||||
					parseInt( trStyle.borderTopWidth, 10 ) +
 | 
			
		||||
					parseInt( trStyle.borderBottomWidth, 10 ) ) === tr.offsetHeight;
 | 
			
		||||
 | 
			
		||||
				documentElement.removeChild( table );
 | 
			
		||||
			}
 | 
			
		||||
@@ -7022,10 +7027,10 @@ jQuery.each( [ "height", "width" ], function( _i, dimension ) {
 | 
			
		||||
					// Running getBoundingClientRect on a disconnected node
 | 
			
		||||
					// in IE throws an error.
 | 
			
		||||
					( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?
 | 
			
		||||
						swap( elem, cssShow, function() {
 | 
			
		||||
							return getWidthOrHeight( elem, dimension, extra );
 | 
			
		||||
						} ) :
 | 
			
		||||
						getWidthOrHeight( elem, dimension, extra );
 | 
			
		||||
					swap( elem, cssShow, function() {
 | 
			
		||||
						return getWidthOrHeight( elem, dimension, extra );
 | 
			
		||||
					} ) :
 | 
			
		||||
					getWidthOrHeight( elem, dimension, extra );
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@@ -7084,7 +7089,7 @@ jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,
 | 
			
		||||
					swap( elem, { marginLeft: 0 }, function() {
 | 
			
		||||
						return elem.getBoundingClientRect().left;
 | 
			
		||||
					} )
 | 
			
		||||
				) + "px";
 | 
			
		||||
			) + "px";
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
);
 | 
			
		||||
@@ -7223,7 +7228,7 @@ Tween.propHooks = {
 | 
			
		||||
			if ( jQuery.fx.step[ tween.prop ] ) {
 | 
			
		||||
				jQuery.fx.step[ tween.prop ]( tween );
 | 
			
		||||
			} else if ( tween.elem.nodeType === 1 && (
 | 
			
		||||
					jQuery.cssHooks[ tween.prop ] ||
 | 
			
		||||
				jQuery.cssHooks[ tween.prop ] ||
 | 
			
		||||
					tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) {
 | 
			
		||||
				jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
 | 
			
		||||
			} else {
 | 
			
		||||
@@ -7468,7 +7473,7 @@ function defaultPrefilter( elem, props, opts ) {
 | 
			
		||||
 | 
			
		||||
			anim.done( function() {
 | 
			
		||||
 | 
			
		||||
			/* eslint-enable no-loop-func */
 | 
			
		||||
				/* eslint-enable no-loop-func */
 | 
			
		||||
 | 
			
		||||
				// The final step of a "hide" animation is actually hiding the element
 | 
			
		||||
				if ( !hidden ) {
 | 
			
		||||
@@ -7588,7 +7593,7 @@ function Animation( elem, properties, options ) {
 | 
			
		||||
			tweens: [],
 | 
			
		||||
			createTween: function( prop, end ) {
 | 
			
		||||
				var tween = jQuery.Tween( elem, animation.opts, prop, end,
 | 
			
		||||
						animation.opts.specialEasing[ prop ] || animation.opts.easing );
 | 
			
		||||
					animation.opts.specialEasing[ prop ] || animation.opts.easing );
 | 
			
		||||
				animation.tweens.push( tween );
 | 
			
		||||
				return tween;
 | 
			
		||||
			},
 | 
			
		||||
@@ -7761,7 +7766,8 @@ jQuery.fn.extend( {
 | 
			
		||||
					anim.stop( true );
 | 
			
		||||
				}
 | 
			
		||||
			};
 | 
			
		||||
			doAnimation.finish = doAnimation;
 | 
			
		||||
 | 
			
		||||
		doAnimation.finish = doAnimation;
 | 
			
		||||
 | 
			
		||||
		return empty || optall.queue === false ?
 | 
			
		||||
			this.each( doAnimation ) :
 | 
			
		||||
@@ -8401,8 +8407,8 @@ jQuery.fn.extend( {
 | 
			
		||||
				if ( this.setAttribute ) {
 | 
			
		||||
					this.setAttribute( "class",
 | 
			
		||||
						className || value === false ?
 | 
			
		||||
						"" :
 | 
			
		||||
						dataPriv.get( this, "__className__" ) || ""
 | 
			
		||||
							"" :
 | 
			
		||||
							dataPriv.get( this, "__className__" ) || ""
 | 
			
		||||
					);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
@@ -8417,7 +8423,7 @@ jQuery.fn.extend( {
 | 
			
		||||
		while ( ( elem = this[ i++ ] ) ) {
 | 
			
		||||
			if ( elem.nodeType === 1 &&
 | 
			
		||||
				( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) {
 | 
			
		||||
					return true;
 | 
			
		||||
				return true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@@ -8707,9 +8713,7 @@ jQuery.extend( jQuery.event, {
 | 
			
		||||
				special.bindType || type;
 | 
			
		||||
 | 
			
		||||
			// jQuery handler
 | 
			
		||||
			handle = (
 | 
			
		||||
					dataPriv.get( cur, "events" ) || Object.create( null )
 | 
			
		||||
				)[ event.type ] &&
 | 
			
		||||
			handle = ( dataPriv.get( cur, "events" ) || Object.create( null ) )[ event.type ] &&
 | 
			
		||||
				dataPriv.get( cur, "handle" );
 | 
			
		||||
			if ( handle ) {
 | 
			
		||||
				handle.apply( cur, data );
 | 
			
		||||
@@ -8856,7 +8860,7 @@ var rquery = ( /\?/ );
 | 
			
		||||
 | 
			
		||||
// Cross-browser xml parsing
 | 
			
		||||
jQuery.parseXML = function( data ) {
 | 
			
		||||
	var xml;
 | 
			
		||||
	var xml, parserErrorElem;
 | 
			
		||||
	if ( !data || typeof data !== "string" ) {
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
@@ -8865,12 +8869,17 @@ jQuery.parseXML = function( data ) {
 | 
			
		||||
	// IE throws on parseFromString with invalid input.
 | 
			
		||||
	try {
 | 
			
		||||
		xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" );
 | 
			
		||||
	} catch ( e ) {
 | 
			
		||||
		xml = undefined;
 | 
			
		||||
	}
 | 
			
		||||
	} catch ( e ) {}
 | 
			
		||||
 | 
			
		||||
	if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
 | 
			
		||||
		jQuery.error( "Invalid XML: " + data );
 | 
			
		||||
	parserErrorElem = xml && xml.getElementsByTagName( "parsererror" )[ 0 ];
 | 
			
		||||
	if ( !xml || parserErrorElem ) {
 | 
			
		||||
		jQuery.error( "Invalid XML: " + (
 | 
			
		||||
			parserErrorElem ?
 | 
			
		||||
				jQuery.map( parserErrorElem.childNodes, function( el ) {
 | 
			
		||||
					return el.textContent;
 | 
			
		||||
				} ).join( "\n" ) :
 | 
			
		||||
				data
 | 
			
		||||
		) );
 | 
			
		||||
	}
 | 
			
		||||
	return xml;
 | 
			
		||||
};
 | 
			
		||||
@@ -8971,16 +8980,14 @@ jQuery.fn.extend( {
 | 
			
		||||
			// Can add propHook for "elements" to filter or add form elements
 | 
			
		||||
			var elements = jQuery.prop( this, "elements" );
 | 
			
		||||
			return elements ? jQuery.makeArray( elements ) : this;
 | 
			
		||||
		} )
 | 
			
		||||
		.filter( function() {
 | 
			
		||||
		} ).filter( function() {
 | 
			
		||||
			var type = this.type;
 | 
			
		||||
 | 
			
		||||
			// Use .is( ":disabled" ) so that fieldset[disabled] works
 | 
			
		||||
			return this.name && !jQuery( this ).is( ":disabled" ) &&
 | 
			
		||||
				rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
 | 
			
		||||
				( this.checked || !rcheckableType.test( type ) );
 | 
			
		||||
		} )
 | 
			
		||||
		.map( function( _i, elem ) {
 | 
			
		||||
		} ).map( function( _i, elem ) {
 | 
			
		||||
			var val = jQuery( this ).val();
 | 
			
		||||
 | 
			
		||||
			if ( val == null ) {
 | 
			
		||||
@@ -9033,7 +9040,8 @@ var
 | 
			
		||||
 | 
			
		||||
	// Anchor tag for parsing the document origin
 | 
			
		||||
	originAnchor = document.createElement( "a" );
 | 
			
		||||
	originAnchor.href = location.href;
 | 
			
		||||
 | 
			
		||||
originAnchor.href = location.href;
 | 
			
		||||
 | 
			
		||||
// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
 | 
			
		||||
function addToPrefiltersOrTransports( structure ) {
 | 
			
		||||
@@ -9414,8 +9422,8 @@ jQuery.extend( {
 | 
			
		||||
			// Context for global events is callbackContext if it is a DOM node or jQuery collection
 | 
			
		||||
			globalEventContext = s.context &&
 | 
			
		||||
				( callbackContext.nodeType || callbackContext.jquery ) ?
 | 
			
		||||
					jQuery( callbackContext ) :
 | 
			
		||||
					jQuery.event,
 | 
			
		||||
				jQuery( callbackContext ) :
 | 
			
		||||
				jQuery.event,
 | 
			
		||||
 | 
			
		||||
			// Deferreds
 | 
			
		||||
			deferred = jQuery.Deferred(),
 | 
			
		||||
@@ -9727,8 +9735,10 @@ jQuery.extend( {
 | 
			
		||||
				response = ajaxHandleResponses( s, jqXHR, responses );
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Use a noop converter for missing script
 | 
			
		||||
			if ( !isSuccess && jQuery.inArray( "script", s.dataTypes ) > -1 ) {
 | 
			
		||||
			// Use a noop converter for missing script but not if jsonp
 | 
			
		||||
			if ( !isSuccess &&
 | 
			
		||||
				jQuery.inArray( "script", s.dataTypes ) > -1 &&
 | 
			
		||||
				jQuery.inArray( "json", s.dataTypes ) < 0 ) {
 | 
			
		||||
				s.converters[ "text script" ] = function() {};
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -10466,12 +10476,6 @@ jQuery.offset = {
 | 
			
		||||
			options.using.call( elem, props );
 | 
			
		||||
 | 
			
		||||
		} else {
 | 
			
		||||
			if ( typeof props.top === "number" ) {
 | 
			
		||||
				props.top += "px";
 | 
			
		||||
			}
 | 
			
		||||
			if ( typeof props.left === "number" ) {
 | 
			
		||||
				props.left += "px";
 | 
			
		||||
			}
 | 
			
		||||
			curElem.css( props );
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -10640,8 +10644,11 @@ jQuery.each( [ "top", "left" ], function( _i, prop ) {
 | 
			
		||||
 | 
			
		||||
// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
 | 
			
		||||
jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
 | 
			
		||||
	jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name },
 | 
			
		||||
		function( defaultExtra, funcName ) {
 | 
			
		||||
	jQuery.each( {
 | 
			
		||||
		padding: "inner" + name,
 | 
			
		||||
		content: type,
 | 
			
		||||
		"": "outer" + name
 | 
			
		||||
	}, function( defaultExtra, funcName ) {
 | 
			
		||||
 | 
			
		||||
		// Margin is only for outerHeight, outerWidth
 | 
			
		||||
		jQuery.fn[ funcName ] = function( margin, value ) {
 | 
			
		||||
@@ -10726,7 +10733,8 @@ jQuery.fn.extend( {
 | 
			
		||||
	}
 | 
			
		||||
} );
 | 
			
		||||
 | 
			
		||||
jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " +
 | 
			
		||||
jQuery.each(
 | 
			
		||||
	( "blur focus focusin focusout resize scroll click dblclick " +
 | 
			
		||||
	"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
 | 
			
		||||
	"change select submit keydown keypress keyup contextmenu" ).split( " " ),
 | 
			
		||||
	function( _i, name ) {
 | 
			
		||||
@@ -10737,7 +10745,8 @@ jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " +
 | 
			
		||||
				this.on( name, null, data, fn ) :
 | 
			
		||||
				this.trigger( name );
 | 
			
		||||
		};
 | 
			
		||||
	} );
 | 
			
		||||
	}
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										4
									
								
								docs/_static/jquery.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
							
								
								
									
										100
									
								
								docs/_static/language_data.js
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -10,7 +10,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"];
 | 
			
		||||
var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Non-minified version is copied as a separate JS file, is available */
 | 
			
		||||
@@ -197,101 +197,3 @@ var Stemmer = function() {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
var splitChars = (function() {
 | 
			
		||||
    var result = {};
 | 
			
		||||
    var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648,
 | 
			
		||||
         1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702,
 | 
			
		||||
         2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971,
 | 
			
		||||
         2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345,
 | 
			
		||||
         3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761,
 | 
			
		||||
         3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823,
 | 
			
		||||
         4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125,
 | 
			
		||||
         8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695,
 | 
			
		||||
         11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587,
 | 
			
		||||
         43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141];
 | 
			
		||||
    var i, j, start, end;
 | 
			
		||||
    for (i = 0; i < singles.length; i++) {
 | 
			
		||||
        result[singles[i]] = true;
 | 
			
		||||
    }
 | 
			
		||||
    var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709],
 | 
			
		||||
         [722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161],
 | 
			
		||||
         [1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568],
 | 
			
		||||
         [1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807],
 | 
			
		||||
         [1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047],
 | 
			
		||||
         [2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383],
 | 
			
		||||
         [2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450],
 | 
			
		||||
         [2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547],
 | 
			
		||||
         [2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673],
 | 
			
		||||
         [2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820],
 | 
			
		||||
         [2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946],
 | 
			
		||||
         [2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023],
 | 
			
		||||
         [3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173],
 | 
			
		||||
         [3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332],
 | 
			
		||||
         [3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481],
 | 
			
		||||
         [3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718],
 | 
			
		||||
         [3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791],
 | 
			
		||||
         [3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095],
 | 
			
		||||
         [4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205],
 | 
			
		||||
         [4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687],
 | 
			
		||||
         [4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968],
 | 
			
		||||
         [4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869],
 | 
			
		||||
         [5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102],
 | 
			
		||||
         [6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271],
 | 
			
		||||
         [6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592],
 | 
			
		||||
         [6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822],
 | 
			
		||||
         [6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167],
 | 
			
		||||
         [7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959],
 | 
			
		||||
         [7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143],
 | 
			
		||||
         [8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318],
 | 
			
		||||
         [8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483],
 | 
			
		||||
         [8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101],
 | 
			
		||||
         [10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567],
 | 
			
		||||
         [11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292],
 | 
			
		||||
         [12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444],
 | 
			
		||||
         [12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783],
 | 
			
		||||
         [12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311],
 | 
			
		||||
         [19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511],
 | 
			
		||||
         [42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774],
 | 
			
		||||
         [42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071],
 | 
			
		||||
         [43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263],
 | 
			
		||||
         [43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519],
 | 
			
		||||
         [43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647],
 | 
			
		||||
         [43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967],
 | 
			
		||||
         [44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295],
 | 
			
		||||
         [57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274],
 | 
			
		||||
         [64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007],
 | 
			
		||||
         [65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381],
 | 
			
		||||
         [65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]];
 | 
			
		||||
    for (i = 0; i < ranges.length; i++) {
 | 
			
		||||
        start = ranges[i][0];
 | 
			
		||||
        end = ranges[i][1];
 | 
			
		||||
        for (j = start; j <= end; j++) {
 | 
			
		||||
            result[j] = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return result;
 | 
			
		||||
})();
 | 
			
		||||
 | 
			
		||||
function splitQuery(query) {
 | 
			
		||||
    var result = [];
 | 
			
		||||
    var start = -1;
 | 
			
		||||
    for (var i = 0; i < query.length; i++) {
 | 
			
		||||
        if (splitChars[query.charCodeAt(i)]) {
 | 
			
		||||
            if (start !== -1) {
 | 
			
		||||
                result.push(query.slice(start, i));
 | 
			
		||||
                start = -1;
 | 
			
		||||
            }
 | 
			
		||||
        } else if (start === -1) {
 | 
			
		||||
            start = i;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (start !== -1) {
 | 
			
		||||
        result.push(query.slice(start));
 | 
			
		||||
    }
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										772
									
								
								docs/_static/searchtools.js
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -8,18 +8,20 @@
 | 
			
		||||
 * :license: BSD, see LICENSE for details.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
"use strict";
 | 
			
		||||
 | 
			
		||||
if (!Scorer) {
 | 
			
		||||
  /**
 | 
			
		||||
   * Simple result scoring code.
 | 
			
		||||
   */
 | 
			
		||||
/**
 | 
			
		||||
 * Simple result scoring code.
 | 
			
		||||
 */
 | 
			
		||||
if (typeof Scorer === "undefined") {
 | 
			
		||||
  var Scorer = {
 | 
			
		||||
    // Implement the following function to further tweak the score for each result
 | 
			
		||||
    // The function takes a result array [filename, title, anchor, descr, score]
 | 
			
		||||
    // The function takes a result array [docname, title, anchor, descr, score, filename]
 | 
			
		||||
    // and returns the new score.
 | 
			
		||||
    /*
 | 
			
		||||
    score: function(result) {
 | 
			
		||||
      return result[4];
 | 
			
		||||
    score: result => {
 | 
			
		||||
      const [docname, title, anchor, descr, score, filename] = result
 | 
			
		||||
      return score
 | 
			
		||||
    },
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
@@ -28,9 +30,11 @@ if (!Scorer) {
 | 
			
		||||
    // or matches in the last dotted part of the object name
 | 
			
		||||
    objPartialMatch: 6,
 | 
			
		||||
    // Additive scores depending on the priority of the object
 | 
			
		||||
    objPrio: {0:  15,   // used to be importantResults
 | 
			
		||||
              1:  5,   // used to be objectResults
 | 
			
		||||
              2: -5},  // used to be unimportantResults
 | 
			
		||||
    objPrio: {
 | 
			
		||||
      0: 15, // used to be importantResults
 | 
			
		||||
      1: 5, // used to be objectResults
 | 
			
		||||
      2: -5, // used to be unimportantResults
 | 
			
		||||
    },
 | 
			
		||||
    //  Used when the priority is not in the mapping.
 | 
			
		||||
    objPrioDefault: 0,
 | 
			
		||||
 | 
			
		||||
@@ -39,456 +43,455 @@ if (!Scorer) {
 | 
			
		||||
    partialTitle: 7,
 | 
			
		||||
    // query found in terms
 | 
			
		||||
    term: 5,
 | 
			
		||||
    partialTerm: 2
 | 
			
		||||
    partialTerm: 2,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (!splitQuery) {
 | 
			
		||||
  function splitQuery(query) {
 | 
			
		||||
    return query.split(/\s+/);
 | 
			
		||||
const _removeChildren = (element) => {
 | 
			
		||||
  while (element && element.lastChild) element.removeChild(element.lastChild);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
 | 
			
		||||
 */
 | 
			
		||||
const _escapeRegExp = (string) =>
 | 
			
		||||
  string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
 | 
			
		||||
 | 
			
		||||
const _displayItem = (item, highlightTerms, searchTerms) => {
 | 
			
		||||
  const docBuilder = DOCUMENTATION_OPTIONS.BUILDER;
 | 
			
		||||
  const docUrlRoot = DOCUMENTATION_OPTIONS.URL_ROOT;
 | 
			
		||||
  const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX;
 | 
			
		||||
  const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX;
 | 
			
		||||
  const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY;
 | 
			
		||||
 | 
			
		||||
  const [docName, title, anchor, descr] = item;
 | 
			
		||||
 | 
			
		||||
  let listItem = document.createElement("li");
 | 
			
		||||
  let requestUrl;
 | 
			
		||||
  let linkUrl;
 | 
			
		||||
  if (docBuilder === "dirhtml") {
 | 
			
		||||
    // dirhtml builder
 | 
			
		||||
    let dirname = docName + "/";
 | 
			
		||||
    if (dirname.match(/\/index\/$/))
 | 
			
		||||
      dirname = dirname.substring(0, dirname.length - 6);
 | 
			
		||||
    else if (dirname === "index/") dirname = "";
 | 
			
		||||
    requestUrl = docUrlRoot + dirname;
 | 
			
		||||
    linkUrl = requestUrl;
 | 
			
		||||
  } else {
 | 
			
		||||
    // normal html builders
 | 
			
		||||
    requestUrl = docUrlRoot + docName + docFileSuffix;
 | 
			
		||||
    linkUrl = docName + docLinkSuffix;
 | 
			
		||||
  }
 | 
			
		||||
  const params = new URLSearchParams();
 | 
			
		||||
  params.set("highlight", [...highlightTerms].join(" "));
 | 
			
		||||
  let linkEl = listItem.appendChild(document.createElement("a"));
 | 
			
		||||
  linkEl.href = linkUrl + "?" + params.toString() + anchor;
 | 
			
		||||
  linkEl.innerHTML = title;
 | 
			
		||||
  if (descr)
 | 
			
		||||
    listItem.appendChild(document.createElement("span")).innerText =
 | 
			
		||||
      " (" + descr + ")";
 | 
			
		||||
  else if (showSearchSummary)
 | 
			
		||||
    fetch(requestUrl)
 | 
			
		||||
      .then((responseData) => responseData.text())
 | 
			
		||||
      .then((data) => {
 | 
			
		||||
        if (data)
 | 
			
		||||
          listItem.appendChild(
 | 
			
		||||
            Search.makeSearchSummary(data, searchTerms, highlightTerms)
 | 
			
		||||
          );
 | 
			
		||||
      });
 | 
			
		||||
  Search.output.appendChild(listItem);
 | 
			
		||||
};
 | 
			
		||||
const _finishSearch = (resultCount) => {
 | 
			
		||||
  Search.stopPulse();
 | 
			
		||||
  Search.title.innerText = _("Search Results");
 | 
			
		||||
  if (!resultCount)
 | 
			
		||||
    Search.status.innerText = Documentation.gettext(
 | 
			
		||||
      "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories."
 | 
			
		||||
    );
 | 
			
		||||
  else
 | 
			
		||||
    Search.status.innerText = _(
 | 
			
		||||
      `Search finished, found ${resultCount} page(s) matching the search query.`
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
const _displayNextItem = (
 | 
			
		||||
  results,
 | 
			
		||||
  resultCount,
 | 
			
		||||
  highlightTerms,
 | 
			
		||||
  searchTerms
 | 
			
		||||
) => {
 | 
			
		||||
  // results left, load the summary and display it
 | 
			
		||||
  // this is intended to be dynamic (don't sub resultsCount)
 | 
			
		||||
  if (results.length) {
 | 
			
		||||
    _displayItem(results.pop(), highlightTerms, searchTerms);
 | 
			
		||||
    setTimeout(
 | 
			
		||||
      () => _displayNextItem(results, resultCount, highlightTerms, searchTerms),
 | 
			
		||||
      5
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
  // search finished, update title and status message
 | 
			
		||||
  else _finishSearch(resultCount);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Default splitQuery function. Can be overridden in ``sphinx.search`` with a
 | 
			
		||||
 * custom function per language.
 | 
			
		||||
 *
 | 
			
		||||
 * The regular expression works by splitting the string on consecutive characters
 | 
			
		||||
 * that are not Unicode letters, numbers, underscores, or emoji characters.
 | 
			
		||||
 * This is the same as ``\W+`` in Python, preserving the surrogate pair area.
 | 
			
		||||
 */
 | 
			
		||||
if (typeof splitQuery === "undefined") {
 | 
			
		||||
  var splitQuery = (query) => query
 | 
			
		||||
      .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu)
 | 
			
		||||
      .filter(term => term)  // remove remaining empty strings
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Search Module
 | 
			
		||||
 */
 | 
			
		||||
var Search = {
 | 
			
		||||
const Search = {
 | 
			
		||||
  _index: null,
 | 
			
		||||
  _queued_query: null,
 | 
			
		||||
  _pulse_status: -1,
 | 
			
		||||
 | 
			
		||||
  _index : null,
 | 
			
		||||
  _queued_query : null,
 | 
			
		||||
  _pulse_status : -1,
 | 
			
		||||
 | 
			
		||||
  htmlToText : function(htmlString) {
 | 
			
		||||
      var virtualDocument = document.implementation.createHTMLDocument('virtual');
 | 
			
		||||
      var htmlElement = $(htmlString, virtualDocument);
 | 
			
		||||
      htmlElement.find('.headerlink').remove();
 | 
			
		||||
      docContent = htmlElement.find('[role=main]')[0];
 | 
			
		||||
      if(docContent === undefined) {
 | 
			
		||||
          console.warn("Content block not found. Sphinx search tries to obtain it " +
 | 
			
		||||
                       "via '[role=main]'. Could you check your theme or template.");
 | 
			
		||||
          return "";
 | 
			
		||||
      }
 | 
			
		||||
      return docContent.textContent || docContent.innerText;
 | 
			
		||||
  htmlToText: (htmlString) => {
 | 
			
		||||
    const htmlElement = document
 | 
			
		||||
      .createRange()
 | 
			
		||||
      .createContextualFragment(htmlString);
 | 
			
		||||
    _removeChildren(htmlElement.querySelectorAll(".headerlink"));
 | 
			
		||||
    const docContent = htmlElement.querySelector('[role="main"]');
 | 
			
		||||
    if (docContent !== undefined) return docContent.textContent;
 | 
			
		||||
    console.warn(
 | 
			
		||||
      "Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template."
 | 
			
		||||
    );
 | 
			
		||||
    return "";
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  init : function() {
 | 
			
		||||
      var params = $.getQueryParameters();
 | 
			
		||||
      if (params.q) {
 | 
			
		||||
          var query = params.q[0];
 | 
			
		||||
          $('input[name="q"]')[0].value = query;
 | 
			
		||||
          this.performSearch(query);
 | 
			
		||||
      }
 | 
			
		||||
  init: () => {
 | 
			
		||||
    const query = new URLSearchParams(window.location.search).get("q");
 | 
			
		||||
    document
 | 
			
		||||
      .querySelectorAll('input[name="q"]')
 | 
			
		||||
      .forEach((el) => (el.value = query));
 | 
			
		||||
    if (query) Search.performSearch(query);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  loadIndex : function(url) {
 | 
			
		||||
    $.ajax({type: "GET", url: url, data: null,
 | 
			
		||||
            dataType: "script", cache: true,
 | 
			
		||||
            complete: function(jqxhr, textstatus) {
 | 
			
		||||
              if (textstatus != "success") {
 | 
			
		||||
                document.getElementById("searchindexloader").src = url;
 | 
			
		||||
              }
 | 
			
		||||
            }});
 | 
			
		||||
  },
 | 
			
		||||
  loadIndex: (url) =>
 | 
			
		||||
    (document.body.appendChild(document.createElement("script")).src = url),
 | 
			
		||||
 | 
			
		||||
  setIndex : function(index) {
 | 
			
		||||
    var q;
 | 
			
		||||
    this._index = index;
 | 
			
		||||
    if ((q = this._queued_query) !== null) {
 | 
			
		||||
      this._queued_query = null;
 | 
			
		||||
      Search.query(q);
 | 
			
		||||
  setIndex: (index) => {
 | 
			
		||||
    Search._index = index;
 | 
			
		||||
    if (Search._queued_query !== null) {
 | 
			
		||||
      const query = Search._queued_query;
 | 
			
		||||
      Search._queued_query = null;
 | 
			
		||||
      Search.query(query);
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  hasIndex : function() {
 | 
			
		||||
      return this._index !== null;
 | 
			
		||||
  },
 | 
			
		||||
  hasIndex: () => Search._index !== null,
 | 
			
		||||
 | 
			
		||||
  deferQuery : function(query) {
 | 
			
		||||
      this._queued_query = query;
 | 
			
		||||
  },
 | 
			
		||||
  deferQuery: (query) => (Search._queued_query = query),
 | 
			
		||||
 | 
			
		||||
  stopPulse : function() {
 | 
			
		||||
      this._pulse_status = 0;
 | 
			
		||||
  },
 | 
			
		||||
  stopPulse: () => (Search._pulse_status = -1),
 | 
			
		||||
 | 
			
		||||
  startPulse : function() {
 | 
			
		||||
    if (this._pulse_status >= 0)
 | 
			
		||||
        return;
 | 
			
		||||
    function pulse() {
 | 
			
		||||
      var i;
 | 
			
		||||
  startPulse: () => {
 | 
			
		||||
    if (Search._pulse_status >= 0) return;
 | 
			
		||||
 | 
			
		||||
    const pulse = () => {
 | 
			
		||||
      Search._pulse_status = (Search._pulse_status + 1) % 4;
 | 
			
		||||
      var dotString = '';
 | 
			
		||||
      for (i = 0; i < Search._pulse_status; i++)
 | 
			
		||||
        dotString += '.';
 | 
			
		||||
      Search.dots.text(dotString);
 | 
			
		||||
      if (Search._pulse_status > -1)
 | 
			
		||||
        window.setTimeout(pulse, 500);
 | 
			
		||||
    }
 | 
			
		||||
      Search.dots.innerText = ".".repeat(Search._pulse_status);
 | 
			
		||||
      if (Search._pulse_status >= 0) window.setTimeout(pulse, 500);
 | 
			
		||||
    };
 | 
			
		||||
    pulse();
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * perform a search for something (or wait until index is loaded)
 | 
			
		||||
   */
 | 
			
		||||
  performSearch : function(query) {
 | 
			
		||||
  performSearch: (query) => {
 | 
			
		||||
    // create the required interface elements
 | 
			
		||||
    this.out = $('#search-results');
 | 
			
		||||
    this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out);
 | 
			
		||||
    this.dots = $('<span></span>').appendTo(this.title);
 | 
			
		||||
    this.status = $('<p class="search-summary"> </p>').appendTo(this.out);
 | 
			
		||||
    this.output = $('<ul class="search"/>').appendTo(this.out);
 | 
			
		||||
    const searchText = document.createElement("h2");
 | 
			
		||||
    searchText.textContent = _("Searching");
 | 
			
		||||
    const searchSummary = document.createElement("p");
 | 
			
		||||
    searchSummary.classList.add("search-summary");
 | 
			
		||||
    searchSummary.innerText = "";
 | 
			
		||||
    const searchList = document.createElement("ul");
 | 
			
		||||
    searchList.classList.add("search");
 | 
			
		||||
 | 
			
		||||
    $('#search-progress').text(_('Preparing search...'));
 | 
			
		||||
    this.startPulse();
 | 
			
		||||
    const out = document.getElementById("search-results");
 | 
			
		||||
    Search.title = out.appendChild(searchText);
 | 
			
		||||
    Search.dots = Search.title.appendChild(document.createElement("span"));
 | 
			
		||||
    Search.status = out.appendChild(searchSummary);
 | 
			
		||||
    Search.output = out.appendChild(searchList);
 | 
			
		||||
 | 
			
		||||
    const searchProgress = document.getElementById("search-progress");
 | 
			
		||||
    // Some themes don't use the search progress node
 | 
			
		||||
    if (searchProgress) {
 | 
			
		||||
      searchProgress.innerText = _("Preparing search...");
 | 
			
		||||
    }
 | 
			
		||||
    Search.startPulse();
 | 
			
		||||
 | 
			
		||||
    // index already loaded, the browser was quick!
 | 
			
		||||
    if (this.hasIndex())
 | 
			
		||||
      this.query(query);
 | 
			
		||||
    else
 | 
			
		||||
      this.deferQuery(query);
 | 
			
		||||
    if (Search.hasIndex()) Search.query(query);
 | 
			
		||||
    else Search.deferQuery(query);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * execute search (requires search index to be loaded)
 | 
			
		||||
   */
 | 
			
		||||
  query : function(query) {
 | 
			
		||||
    var i;
 | 
			
		||||
  query: (query) => {
 | 
			
		||||
    // stem the search terms and add them to the correct list
 | 
			
		||||
    const stemmer = new Stemmer();
 | 
			
		||||
    const searchTerms = new Set();
 | 
			
		||||
    const excludedTerms = new Set();
 | 
			
		||||
    const highlightTerms = new Set();
 | 
			
		||||
    const objectTerms = new Set(splitQuery(query.toLowerCase().trim()));
 | 
			
		||||
    splitQuery(query.trim()).forEach((queryTerm) => {
 | 
			
		||||
      const queryTermLower = queryTerm.toLowerCase();
 | 
			
		||||
 | 
			
		||||
    // stem the searchterms and add them to the correct list
 | 
			
		||||
    var stemmer = new Stemmer();
 | 
			
		||||
    var searchterms = [];
 | 
			
		||||
    var excluded = [];
 | 
			
		||||
    var hlterms = [];
 | 
			
		||||
    var tmp = splitQuery(query);
 | 
			
		||||
    var objectterms = [];
 | 
			
		||||
    for (i = 0; i < tmp.length; i++) {
 | 
			
		||||
      if (tmp[i] !== "") {
 | 
			
		||||
          objectterms.push(tmp[i].toLowerCase());
 | 
			
		||||
      }
 | 
			
		||||
      // maybe skip this "word"
 | 
			
		||||
      // stopwords array is from language_data.js
 | 
			
		||||
      if (
 | 
			
		||||
        stopwords.indexOf(queryTermLower) !== -1 ||
 | 
			
		||||
        queryTerm.match(/^\d+$/)
 | 
			
		||||
      )
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
      if ($u.indexOf(stopwords, tmp[i].toLowerCase()) != -1 || tmp[i] === "") {
 | 
			
		||||
        // skip this "word"
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
      // stem the word
 | 
			
		||||
      var word = stemmer.stemWord(tmp[i].toLowerCase());
 | 
			
		||||
      // prevent stemmer from cutting word smaller than two chars
 | 
			
		||||
      if(word.length < 3 && tmp[i].length >= 3) {
 | 
			
		||||
        word = tmp[i];
 | 
			
		||||
      }
 | 
			
		||||
      var toAppend;
 | 
			
		||||
      let word = stemmer.stemWord(queryTermLower);
 | 
			
		||||
      // select the correct list
 | 
			
		||||
      if (word[0] == '-') {
 | 
			
		||||
        toAppend = excluded;
 | 
			
		||||
        word = word.substr(1);
 | 
			
		||||
      }
 | 
			
		||||
      if (word[0] === "-") excludedTerms.add(word.substr(1));
 | 
			
		||||
      else {
 | 
			
		||||
        toAppend = searchterms;
 | 
			
		||||
        hlterms.push(tmp[i].toLowerCase());
 | 
			
		||||
        searchTerms.add(word);
 | 
			
		||||
        highlightTerms.add(queryTermLower);
 | 
			
		||||
      }
 | 
			
		||||
      // only add if not already in the list
 | 
			
		||||
      if (!$u.contains(toAppend, word))
 | 
			
		||||
        toAppend.push(word);
 | 
			
		||||
    }
 | 
			
		||||
    var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" "));
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // console.debug('SEARCH: searching for:');
 | 
			
		||||
    // console.info('required: ', searchterms);
 | 
			
		||||
    // console.info('excluded: ', excluded);
 | 
			
		||||
    // console.debug("SEARCH: searching for:");
 | 
			
		||||
    // console.info("required: ", [...searchTerms]);
 | 
			
		||||
    // console.info("excluded: ", [...excludedTerms]);
 | 
			
		||||
 | 
			
		||||
    // prepare search
 | 
			
		||||
    var terms = this._index.terms;
 | 
			
		||||
    var titleterms = this._index.titleterms;
 | 
			
		||||
 | 
			
		||||
    // array of [filename, title, anchor, descr, score]
 | 
			
		||||
    var results = [];
 | 
			
		||||
    $('#search-progress').empty();
 | 
			
		||||
    // array of [docname, title, anchor, descr, score, filename]
 | 
			
		||||
    let results = [];
 | 
			
		||||
    _removeChildren(document.getElementById("search-progress"));
 | 
			
		||||
 | 
			
		||||
    // lookup as object
 | 
			
		||||
    for (i = 0; i < objectterms.length; i++) {
 | 
			
		||||
      var others = [].concat(objectterms.slice(0, i),
 | 
			
		||||
                             objectterms.slice(i+1, objectterms.length));
 | 
			
		||||
      results = results.concat(this.performObjectSearch(objectterms[i], others));
 | 
			
		||||
    }
 | 
			
		||||
    objectTerms.forEach((term) =>
 | 
			
		||||
      results.push(...Search.performObjectSearch(term, objectTerms))
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // lookup as search terms in fulltext
 | 
			
		||||
    results = results.concat(this.performTermsSearch(searchterms, excluded, terms, titleterms));
 | 
			
		||||
    results.push(...Search.performTermsSearch(searchTerms, excludedTerms));
 | 
			
		||||
 | 
			
		||||
    // let the scorer override scores with a custom scoring function
 | 
			
		||||
    if (Scorer.score) {
 | 
			
		||||
      for (i = 0; i < results.length; i++)
 | 
			
		||||
        results[i][4] = Scorer.score(results[i]);
 | 
			
		||||
    }
 | 
			
		||||
    if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item)));
 | 
			
		||||
 | 
			
		||||
    // now sort the results by score (in opposite order of appearance, since the
 | 
			
		||||
    // display function below uses pop() to retrieve items) and then
 | 
			
		||||
    // alphabetically
 | 
			
		||||
    results.sort(function(a, b) {
 | 
			
		||||
      var left = a[4];
 | 
			
		||||
      var right = b[4];
 | 
			
		||||
      if (left > right) {
 | 
			
		||||
        return 1;
 | 
			
		||||
      } else if (left < right) {
 | 
			
		||||
        return -1;
 | 
			
		||||
      } else {
 | 
			
		||||
    results.sort((a, b) => {
 | 
			
		||||
      const leftScore = a[4];
 | 
			
		||||
      const rightScore = b[4];
 | 
			
		||||
      if (leftScore === rightScore) {
 | 
			
		||||
        // same score: sort alphabetically
 | 
			
		||||
        left = a[1].toLowerCase();
 | 
			
		||||
        right = b[1].toLowerCase();
 | 
			
		||||
        return (left > right) ? -1 : ((left < right) ? 1 : 0);
 | 
			
		||||
        const leftTitle = a[1].toLowerCase();
 | 
			
		||||
        const rightTitle = b[1].toLowerCase();
 | 
			
		||||
        if (leftTitle === rightTitle) return 0;
 | 
			
		||||
        return leftTitle > rightTitle ? -1 : 1; // inverted is intentional
 | 
			
		||||
      }
 | 
			
		||||
      return leftScore > rightScore ? 1 : -1;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // remove duplicate search results
 | 
			
		||||
    // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept
 | 
			
		||||
    let seen = new Set();
 | 
			
		||||
    results = results.reverse().reduce((acc, result) => {
 | 
			
		||||
      let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(',');
 | 
			
		||||
      if (!seen.has(resultStr)) {
 | 
			
		||||
        acc.push(result);
 | 
			
		||||
        seen.add(resultStr);
 | 
			
		||||
      }
 | 
			
		||||
      return acc;
 | 
			
		||||
    }, []);
 | 
			
		||||
 | 
			
		||||
    results = results.reverse();
 | 
			
		||||
 | 
			
		||||
    // for debugging
 | 
			
		||||
    //Search.lastresults = results.slice();  // a copy
 | 
			
		||||
    //console.info('search results:', Search.lastresults);
 | 
			
		||||
    // console.info("search results:", Search.lastresults);
 | 
			
		||||
 | 
			
		||||
    // print the results
 | 
			
		||||
    var resultCount = results.length;
 | 
			
		||||
    function displayNextItem() {
 | 
			
		||||
      // results left, load the summary and display it
 | 
			
		||||
      if (results.length) {
 | 
			
		||||
        var item = results.pop();
 | 
			
		||||
        var listItem = $('<li></li>');
 | 
			
		||||
        var requestUrl = "";
 | 
			
		||||
        var linkUrl = "";
 | 
			
		||||
        if (DOCUMENTATION_OPTIONS.BUILDER === 'dirhtml') {
 | 
			
		||||
          // dirhtml builder
 | 
			
		||||
          var dirname = item[0] + '/';
 | 
			
		||||
          if (dirname.match(/\/index\/$/)) {
 | 
			
		||||
            dirname = dirname.substring(0, dirname.length-6);
 | 
			
		||||
          } else if (dirname == 'index/') {
 | 
			
		||||
            dirname = '';
 | 
			
		||||
          }
 | 
			
		||||
          requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + dirname;
 | 
			
		||||
          linkUrl = requestUrl;
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
          // normal html builders
 | 
			
		||||
          requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX;
 | 
			
		||||
          linkUrl = item[0] + DOCUMENTATION_OPTIONS.LINK_SUFFIX;
 | 
			
		||||
        }
 | 
			
		||||
        listItem.append($('<a/>').attr('href',
 | 
			
		||||
            linkUrl +
 | 
			
		||||
            highlightstring + item[2]).html(item[1]));
 | 
			
		||||
        if (item[3]) {
 | 
			
		||||
          listItem.append($('<span> (' + item[3] + ')</span>'));
 | 
			
		||||
          Search.output.append(listItem);
 | 
			
		||||
          setTimeout(function() {
 | 
			
		||||
            displayNextItem();
 | 
			
		||||
          }, 5);
 | 
			
		||||
        } else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
 | 
			
		||||
          $.ajax({url: requestUrl,
 | 
			
		||||
                  dataType: "text",
 | 
			
		||||
                  complete: function(jqxhr, textstatus) {
 | 
			
		||||
                    var data = jqxhr.responseText;
 | 
			
		||||
                    if (data !== '' && data !== undefined) {
 | 
			
		||||
                      var summary = Search.makeSearchSummary(data, searchterms, hlterms);
 | 
			
		||||
                      if (summary) {
 | 
			
		||||
                        listItem.append(summary);
 | 
			
		||||
                      }
 | 
			
		||||
                    }
 | 
			
		||||
                    Search.output.append(listItem);
 | 
			
		||||
                    setTimeout(function() {
 | 
			
		||||
                      displayNextItem();
 | 
			
		||||
                    }, 5);
 | 
			
		||||
                  }});
 | 
			
		||||
        } else {
 | 
			
		||||
          // no source available, just display title
 | 
			
		||||
          Search.output.append(listItem);
 | 
			
		||||
          setTimeout(function() {
 | 
			
		||||
            displayNextItem();
 | 
			
		||||
          }, 5);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      // search finished, update title and status message
 | 
			
		||||
      else {
 | 
			
		||||
        Search.stopPulse();
 | 
			
		||||
        Search.title.text(_('Search Results'));
 | 
			
		||||
        if (!resultCount)
 | 
			
		||||
          Search.status.text(_('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.'));
 | 
			
		||||
        else
 | 
			
		||||
            Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount));
 | 
			
		||||
        Search.status.fadeIn(500);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    displayNextItem();
 | 
			
		||||
    _displayNextItem(results, results.length, highlightTerms, searchTerms);
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * search for object names
 | 
			
		||||
   */
 | 
			
		||||
  performObjectSearch : function(object, otherterms) {
 | 
			
		||||
    var filenames = this._index.filenames;
 | 
			
		||||
    var docnames = this._index.docnames;
 | 
			
		||||
    var objects = this._index.objects;
 | 
			
		||||
    var objnames = this._index.objnames;
 | 
			
		||||
    var titles = this._index.titles;
 | 
			
		||||
  performObjectSearch: (object, objectTerms) => {
 | 
			
		||||
    const filenames = Search._index.filenames;
 | 
			
		||||
    const docNames = Search._index.docnames;
 | 
			
		||||
    const objects = Search._index.objects;
 | 
			
		||||
    const objNames = Search._index.objnames;
 | 
			
		||||
    const titles = Search._index.titles;
 | 
			
		||||
 | 
			
		||||
    var i;
 | 
			
		||||
    var results = [];
 | 
			
		||||
    const results = [];
 | 
			
		||||
 | 
			
		||||
    for (var prefix in objects) {
 | 
			
		||||
      for (var iMatch = 0; iMatch != objects[prefix].length; ++iMatch) {
 | 
			
		||||
        var match = objects[prefix][iMatch];
 | 
			
		||||
        var name = match[4];
 | 
			
		||||
        var fullname = (prefix ? prefix + '.' : '') + name;
 | 
			
		||||
        var fullnameLower = fullname.toLowerCase()
 | 
			
		||||
        if (fullnameLower.indexOf(object) > -1) {
 | 
			
		||||
          var score = 0;
 | 
			
		||||
          var parts = fullnameLower.split('.');
 | 
			
		||||
          // check for different match types: exact matches of full name or
 | 
			
		||||
          // "last name" (i.e. last dotted part)
 | 
			
		||||
          if (fullnameLower == object || parts[parts.length - 1] == object) {
 | 
			
		||||
            score += Scorer.objNameMatch;
 | 
			
		||||
          // matches in last name
 | 
			
		||||
          } else if (parts[parts.length - 1].indexOf(object) > -1) {
 | 
			
		||||
            score += Scorer.objPartialMatch;
 | 
			
		||||
          }
 | 
			
		||||
          var objname = objnames[match[1]][2];
 | 
			
		||||
          var title = titles[match[0]];
 | 
			
		||||
          // If more than one term searched for, we require other words to be
 | 
			
		||||
          // found in the name/title/description
 | 
			
		||||
          if (otherterms.length > 0) {
 | 
			
		||||
            var haystack = (prefix + ' ' + name + ' ' +
 | 
			
		||||
                            objname + ' ' + title).toLowerCase();
 | 
			
		||||
            var allfound = true;
 | 
			
		||||
            for (i = 0; i < otherterms.length; i++) {
 | 
			
		||||
              if (haystack.indexOf(otherterms[i]) == -1) {
 | 
			
		||||
                allfound = false;
 | 
			
		||||
                break;
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            if (!allfound) {
 | 
			
		||||
              continue;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          var descr = objname + _(', in ') + title;
 | 
			
		||||
    const objectSearchCallback = (prefix, match) => {
 | 
			
		||||
      const name = match[4]
 | 
			
		||||
      const fullname = (prefix ? prefix + "." : "") + name;
 | 
			
		||||
      const fullnameLower = fullname.toLowerCase();
 | 
			
		||||
      if (fullnameLower.indexOf(object) < 0) return;
 | 
			
		||||
 | 
			
		||||
          var anchor = match[3];
 | 
			
		||||
          if (anchor === '')
 | 
			
		||||
            anchor = fullname;
 | 
			
		||||
          else if (anchor == '-')
 | 
			
		||||
            anchor = objnames[match[1]][1] + '-' + fullname;
 | 
			
		||||
          // add custom score for some objects according to scorer
 | 
			
		||||
          if (Scorer.objPrio.hasOwnProperty(match[2])) {
 | 
			
		||||
            score += Scorer.objPrio[match[2]];
 | 
			
		||||
          } else {
 | 
			
		||||
            score += Scorer.objPrioDefault;
 | 
			
		||||
          }
 | 
			
		||||
          results.push([docnames[match[0]], fullname, '#'+anchor, descr, score, filenames[match[0]]]);
 | 
			
		||||
        }
 | 
			
		||||
      let score = 0;
 | 
			
		||||
      const parts = fullnameLower.split(".");
 | 
			
		||||
 | 
			
		||||
      // check for different match types: exact matches of full name or
 | 
			
		||||
      // "last name" (i.e. last dotted part)
 | 
			
		||||
      if (fullnameLower === object || parts.slice(-1)[0] === object)
 | 
			
		||||
        score += Scorer.objNameMatch;
 | 
			
		||||
      else if (parts.slice(-1)[0].indexOf(object) > -1)
 | 
			
		||||
        score += Scorer.objPartialMatch; // matches in last name
 | 
			
		||||
 | 
			
		||||
      const objName = objNames[match[1]][2];
 | 
			
		||||
      const title = titles[match[0]];
 | 
			
		||||
 | 
			
		||||
      // If more than one term searched for, we require other words to be
 | 
			
		||||
      // found in the name/title/description
 | 
			
		||||
      const otherTerms = new Set(objectTerms);
 | 
			
		||||
      otherTerms.delete(object);
 | 
			
		||||
      if (otherTerms.size > 0) {
 | 
			
		||||
        const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase();
 | 
			
		||||
        if (
 | 
			
		||||
          [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0)
 | 
			
		||||
        )
 | 
			
		||||
          return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
      let anchor = match[3];
 | 
			
		||||
      if (anchor === "") anchor = fullname;
 | 
			
		||||
      else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname;
 | 
			
		||||
 | 
			
		||||
      const descr = objName + _(", in ") + title;
 | 
			
		||||
 | 
			
		||||
      // add custom score for some objects according to scorer
 | 
			
		||||
      if (Scorer.objPrio.hasOwnProperty(match[2]))
 | 
			
		||||
        score += Scorer.objPrio[match[2]];
 | 
			
		||||
      else score += Scorer.objPrioDefault;
 | 
			
		||||
 | 
			
		||||
      results.push([
 | 
			
		||||
        docNames[match[0]],
 | 
			
		||||
        fullname,
 | 
			
		||||
        "#" + anchor,
 | 
			
		||||
        descr,
 | 
			
		||||
        score,
 | 
			
		||||
        filenames[match[0]],
 | 
			
		||||
      ]);
 | 
			
		||||
    };
 | 
			
		||||
    Object.keys(objects).forEach((prefix) =>
 | 
			
		||||
      objects[prefix].forEach((array) =>
 | 
			
		||||
        objectSearchCallback(prefix, array)
 | 
			
		||||
      )
 | 
			
		||||
    );
 | 
			
		||||
    return results;
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
 | 
			
		||||
   */
 | 
			
		||||
  escapeRegExp : function(string) {
 | 
			
		||||
    return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * search for full-text terms in the index
 | 
			
		||||
   */
 | 
			
		||||
  performTermsSearch : function(searchterms, excluded, terms, titleterms) {
 | 
			
		||||
    var docnames = this._index.docnames;
 | 
			
		||||
    var filenames = this._index.filenames;
 | 
			
		||||
    var titles = this._index.titles;
 | 
			
		||||
  performTermsSearch: (searchTerms, excludedTerms) => {
 | 
			
		||||
    // prepare search
 | 
			
		||||
    const terms = Search._index.terms;
 | 
			
		||||
    const titleTerms = Search._index.titleterms;
 | 
			
		||||
    const docNames = Search._index.docnames;
 | 
			
		||||
    const filenames = Search._index.filenames;
 | 
			
		||||
    const titles = Search._index.titles;
 | 
			
		||||
 | 
			
		||||
    var i, j, file;
 | 
			
		||||
    var fileMap = {};
 | 
			
		||||
    var scoreMap = {};
 | 
			
		||||
    var results = [];
 | 
			
		||||
    const scoreMap = new Map();
 | 
			
		||||
    const fileMap = new Map();
 | 
			
		||||
 | 
			
		||||
    // perform the search on the required terms
 | 
			
		||||
    for (i = 0; i < searchterms.length; i++) {
 | 
			
		||||
      var word = searchterms[i];
 | 
			
		||||
      var files = [];
 | 
			
		||||
      var _o = [
 | 
			
		||||
        {files: terms[word], score: Scorer.term},
 | 
			
		||||
        {files: titleterms[word], score: Scorer.title}
 | 
			
		||||
    searchTerms.forEach((word) => {
 | 
			
		||||
      const files = [];
 | 
			
		||||
      const arr = [
 | 
			
		||||
        { files: terms[word], score: Scorer.term },
 | 
			
		||||
        { files: titleTerms[word], score: Scorer.title },
 | 
			
		||||
      ];
 | 
			
		||||
      // add support for partial matches
 | 
			
		||||
      if (word.length > 2) {
 | 
			
		||||
        var word_regex = this.escapeRegExp(word);
 | 
			
		||||
        for (var w in terms) {
 | 
			
		||||
          if (w.match(word_regex) && !terms[word]) {
 | 
			
		||||
            _o.push({files: terms[w], score: Scorer.partialTerm})
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        for (var w in titleterms) {
 | 
			
		||||
          if (w.match(word_regex) && !titleterms[word]) {
 | 
			
		||||
              _o.push({files: titleterms[w], score: Scorer.partialTitle})
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        const escapedWord = _escapeRegExp(word);
 | 
			
		||||
        Object.keys(terms).forEach((term) => {
 | 
			
		||||
          if (term.match(escapedWord) && !terms[word])
 | 
			
		||||
            arr.push({ files: terms[term], score: Scorer.partialTerm });
 | 
			
		||||
        });
 | 
			
		||||
        Object.keys(titleTerms).forEach((term) => {
 | 
			
		||||
          if (term.match(escapedWord) && !titleTerms[word])
 | 
			
		||||
            arr.push({ files: titleTerms[word], score: Scorer.partialTitle });
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // no match but word was a required one
 | 
			
		||||
      if ($u.every(_o, function(o){return o.files === undefined;})) {
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      if (arr.every((record) => record.files === undefined)) return;
 | 
			
		||||
 | 
			
		||||
      // found search word in contents
 | 
			
		||||
      $u.each(_o, function(o) {
 | 
			
		||||
        var _files = o.files;
 | 
			
		||||
        if (_files === undefined)
 | 
			
		||||
          return
 | 
			
		||||
      arr.forEach((record) => {
 | 
			
		||||
        if (record.files === undefined) return;
 | 
			
		||||
 | 
			
		||||
        if (_files.length === undefined)
 | 
			
		||||
          _files = [_files];
 | 
			
		||||
        files = files.concat(_files);
 | 
			
		||||
        let recordFiles = record.files;
 | 
			
		||||
        if (recordFiles.length === undefined) recordFiles = [recordFiles];
 | 
			
		||||
        files.push(...recordFiles);
 | 
			
		||||
 | 
			
		||||
        // set score for the word in each file to Scorer.term
 | 
			
		||||
        for (j = 0; j < _files.length; j++) {
 | 
			
		||||
          file = _files[j];
 | 
			
		||||
          if (!(file in scoreMap))
 | 
			
		||||
            scoreMap[file] = {};
 | 
			
		||||
          scoreMap[file][word] = o.score;
 | 
			
		||||
        }
 | 
			
		||||
        // set score for the word in each file
 | 
			
		||||
        recordFiles.forEach((file) => {
 | 
			
		||||
          if (!scoreMap.has(file)) scoreMap.set(file, {});
 | 
			
		||||
          scoreMap.get(file)[word] = record.score;
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // create the mapping
 | 
			
		||||
      for (j = 0; j < files.length; j++) {
 | 
			
		||||
        file = files[j];
 | 
			
		||||
        if (file in fileMap && fileMap[file].indexOf(word) === -1)
 | 
			
		||||
          fileMap[file].push(word);
 | 
			
		||||
        else
 | 
			
		||||
          fileMap[file] = [word];
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
      files.forEach((file) => {
 | 
			
		||||
        if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1)
 | 
			
		||||
          fileMap.get(file).push(word);
 | 
			
		||||
        else fileMap.set(file, [word]);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // now check if the files don't contain excluded terms
 | 
			
		||||
    for (file in fileMap) {
 | 
			
		||||
      var valid = true;
 | 
			
		||||
 | 
			
		||||
    const results = [];
 | 
			
		||||
    for (const [file, wordList] of fileMap) {
 | 
			
		||||
      // check if all requirements are matched
 | 
			
		||||
      var filteredTermCount = // as search terms with length < 3 are discarded: ignore
 | 
			
		||||
        searchterms.filter(function(term){return term.length > 2}).length
 | 
			
		||||
 | 
			
		||||
      // as search terms with length < 3 are discarded
 | 
			
		||||
      const filteredTermCount = [...searchTerms].filter(
 | 
			
		||||
        (term) => term.length > 2
 | 
			
		||||
      ).length;
 | 
			
		||||
      if (
 | 
			
		||||
        fileMap[file].length != searchterms.length &&
 | 
			
		||||
        fileMap[file].length != filteredTermCount
 | 
			
		||||
      ) continue;
 | 
			
		||||
        wordList.length !== searchTerms.size &&
 | 
			
		||||
        wordList.length !== filteredTermCount
 | 
			
		||||
      )
 | 
			
		||||
        continue;
 | 
			
		||||
 | 
			
		||||
      // ensure that none of the excluded terms is in the search result
 | 
			
		||||
      for (i = 0; i < excluded.length; i++) {
 | 
			
		||||
        if (terms[excluded[i]] == file ||
 | 
			
		||||
            titleterms[excluded[i]] == file ||
 | 
			
		||||
            $u.contains(terms[excluded[i]] || [], file) ||
 | 
			
		||||
            $u.contains(titleterms[excluded[i]] || [], file)) {
 | 
			
		||||
          valid = false;
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (
 | 
			
		||||
        [...excludedTerms].some(
 | 
			
		||||
          (term) =>
 | 
			
		||||
            terms[term] === file ||
 | 
			
		||||
            titleTerms[term] === file ||
 | 
			
		||||
            (terms[term] || []).includes(file) ||
 | 
			
		||||
            (titleTerms[term] || []).includes(file)
 | 
			
		||||
        )
 | 
			
		||||
      )
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
      // if we have still a valid result we can add it to the result list
 | 
			
		||||
      if (valid) {
 | 
			
		||||
        // select one (max) score for the file.
 | 
			
		||||
        // for better ranking, we should calculate ranking by using words statistics like basic tf-idf...
 | 
			
		||||
        var score = $u.max($u.map(fileMap[file], function(w){return scoreMap[file][w]}));
 | 
			
		||||
        results.push([docnames[file], titles[file], '', null, score, filenames[file]]);
 | 
			
		||||
      }
 | 
			
		||||
      // select one (max) score for the file.
 | 
			
		||||
      const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w]));
 | 
			
		||||
      // add result to the result list
 | 
			
		||||
      results.push([
 | 
			
		||||
        docNames[file],
 | 
			
		||||
        titles[file],
 | 
			
		||||
        "",
 | 
			
		||||
        null,
 | 
			
		||||
        score,
 | 
			
		||||
        filenames[file],
 | 
			
		||||
      ]);
 | 
			
		||||
    }
 | 
			
		||||
    return results;
 | 
			
		||||
  },
 | 
			
		||||
@@ -496,34 +499,33 @@ var Search = {
 | 
			
		||||
  /**
 | 
			
		||||
   * helper function to return a node containing the
 | 
			
		||||
   * search summary for a given text. keywords is a list
 | 
			
		||||
   * of stemmed words, hlwords is the list of normal, unstemmed
 | 
			
		||||
   * of stemmed words, highlightWords is the list of normal, unstemmed
 | 
			
		||||
   * words. the first one is used to find the occurrence, the
 | 
			
		||||
   * latter for highlighting it.
 | 
			
		||||
   */
 | 
			
		||||
  makeSearchSummary : function(htmlText, keywords, hlwords) {
 | 
			
		||||
    var text = Search.htmlToText(htmlText);
 | 
			
		||||
    if (text == "") {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
    var textLower = text.toLowerCase();
 | 
			
		||||
    var start = 0;
 | 
			
		||||
    $.each(keywords, function() {
 | 
			
		||||
      var i = textLower.indexOf(this.toLowerCase());
 | 
			
		||||
      if (i > -1)
 | 
			
		||||
        start = i;
 | 
			
		||||
    });
 | 
			
		||||
    start = Math.max(start - 120, 0);
 | 
			
		||||
    var excerpt = ((start > 0) ? '...' : '') +
 | 
			
		||||
      $.trim(text.substr(start, 240)) +
 | 
			
		||||
      ((start + 240 - text.length) ? '...' : '');
 | 
			
		||||
    var rv = $('<p class="context"></p>').text(excerpt);
 | 
			
		||||
    $.each(hlwords, function() {
 | 
			
		||||
      rv = rv.highlightText(this, 'highlighted');
 | 
			
		||||
    });
 | 
			
		||||
    return rv;
 | 
			
		||||
  }
 | 
			
		||||
  makeSearchSummary: (htmlText, keywords, highlightWords) => {
 | 
			
		||||
    const text = Search.htmlToText(htmlText).toLowerCase();
 | 
			
		||||
    if (text === "") return null;
 | 
			
		||||
 | 
			
		||||
    const actualStartPosition = [...keywords]
 | 
			
		||||
      .map((k) => text.indexOf(k.toLowerCase()))
 | 
			
		||||
      .filter((i) => i > -1)
 | 
			
		||||
      .slice(-1)[0];
 | 
			
		||||
    const startWithContext = Math.max(actualStartPosition - 120, 0);
 | 
			
		||||
 | 
			
		||||
    const top = startWithContext === 0 ? "" : "...";
 | 
			
		||||
    const tail = startWithContext + 240 < text.length ? "..." : "";
 | 
			
		||||
 | 
			
		||||
    let summary = document.createElement("div");
 | 
			
		||||
    summary.classList.add("context");
 | 
			
		||||
    summary.innerText = top + text.substr(startWithContext, 240).trim() + tail;
 | 
			
		||||
 | 
			
		||||
    highlightWords.forEach((highlightWord) =>
 | 
			
		||||
      _highlightText(summary, highlightWord, "highlighted")
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return summary;
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
$(document).ready(function() {
 | 
			
		||||
  Search.init();
 | 
			
		||||
});
 | 
			
		||||
_ready(Search.init);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
 | 
			
		||||
    <title>Authentication — FitTrackee 0.5.7
 | 
			
		||||
    <title>Authentication — FitTrackee 0.6.10
 | 
			
		||||
 documentation</title>
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
    <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
 | 
			
		||||
    <script src="../_static/jquery.js"></script>
 | 
			
		||||
    <script src="../_static/underscore.js"></script>
 | 
			
		||||
    <script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
 | 
			
		||||
    <script src="../_static/doctools.js"></script>
 | 
			
		||||
    <link rel="index" title="Index" href="../genindex.html" />
 | 
			
		||||
    <link rel="search" title="Search" href="../search.html" />
 | 
			
		||||
@@ -40,7 +41,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
        <a class="navbar-brand" href="../index.html">
 | 
			
		||||
          FitTrackee</a>
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.5.7
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.6.10
 | 
			
		||||
</b></span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@@ -61,6 +62,7 @@
 | 
			
		||||
      aria-labelledby="dLabelGlobalToc"><ul class="current">
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../features.html">Features</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../installation.html">Installation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../cli.html">Command line interface</a></li>
 | 
			
		||||
<li class="toctree-l1 current"><a class="reference internal" href="index.html">API documentation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../troubleshooting/index.html">Troubleshooting</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../changelog.html">Change log</a></li>
 | 
			
		||||
@@ -126,11 +128,13 @@
 | 
			
		||||
    <div class="body col-md-12 content" role="main">
 | 
			
		||||
      
 | 
			
		||||
  <section id="authentication">
 | 
			
		||||
<h1>Authentication<a class="headerlink" href="#authentication" title="Permalink to this headline">¶</a></h1>
 | 
			
		||||
<h1>Authentication<a class="headerlink" href="#authentication" title="Permalink to this heading">¶</a></h1>
 | 
			
		||||
<dl class="http post">
 | 
			
		||||
<dt class="sig sig-object http" id="post--api-auth-register">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">POST</span> </span><span class="sig-name descname"><span class="pre">/api/auth/register</span></span><a class="headerlink" href="#post--api-auth-register" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>register a user</p>
 | 
			
		||||
<dd><p>register a user and send confirmation email.</p>
 | 
			
		||||
<p>The newly created account is inactive. The user must confirm his email
 | 
			
		||||
to activate it.</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">POST</span> <span class="nn">/api/auth/register</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
@@ -138,14 +142,12 @@
 | 
			
		||||
</div>
 | 
			
		||||
<p><strong>Example responses</strong>:</p>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>successful registration</p></li>
 | 
			
		||||
<li><p>success</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">201</span> <span class="ne">CREATED</span>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">200</span> <span class="ne">SUCCESS</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
 | 
			
		||||
<span class="p">{</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"auth_token"</span><span class="p">:</span><span class="w"> </span><span class="s2">"JSON Web Token"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"successfully registered"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w"></span>
 | 
			
		||||
<span class="p">}</span><span class="w"></span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
@@ -163,25 +165,28 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Request JSON Object</dt>
 | 
			
		||||
<dt class="field-odd">Request JSON Object<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>username</strong> (<em>string</em>) – user name (3 to 12 characters required)</p></li>
 | 
			
		||||
<li><p><strong>username</strong> (<em>string</em>) – username (3 to 30 characters required)</p></li>
 | 
			
		||||
<li><p><strong>email</strong> (<em>string</em>) – user email</p></li>
 | 
			
		||||
<li><p><strong>password</strong> (<em>string</em>) – password (8 characters required)</p></li>
 | 
			
		||||
<li><p><strong>password_conf</strong> (<em>string</em>) – password confirmation</p></li>
 | 
			
		||||
<li><p><strong>lang</strong> (<em>string</em>) – user language preferences (if not provided or invalid,
 | 
			
		||||
fallback to ‘en’ (english))</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes</dt>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.2">201 Created</a></span> – successfully registered</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – <ul>
 | 
			
		||||
<li><p>invalid payload</p></li>
 | 
			
		||||
<li><p>sorry, that user already exists</p></li>
 | 
			
		||||
<li><p>sorry, that username is already taken</p></li>
 | 
			
		||||
<li><dl class="simple">
 | 
			
		||||
<dt>Errors:</dt><dd><ul>
 | 
			
		||||
<li><p>username: 3 to 12 characters required</p></li>
 | 
			
		||||
<li><p>username: 3 to 30 characters required</p></li>
 | 
			
		||||
<li><p>username:
 | 
			
		||||
only alphanumeric characters and the underscore
 | 
			
		||||
character “_” allowed</p></li>
 | 
			
		||||
<li><p>email: valid email must be provided</p></li>
 | 
			
		||||
<li><p>password: password and password confirmation don’t match</p></li>
 | 
			
		||||
<li><p>password: 8 characters required</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
@@ -196,10 +201,84 @@
 | 
			
		||||
</dl>
 | 
			
		||||
</dd></dl>
 | 
			
		||||
 | 
			
		||||
<dl class="http post">
 | 
			
		||||
<dt class="sig sig-object http" id="post--api-auth-account-confirm">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">POST</span> </span><span class="sig-name descname"><span class="pre">/api/auth/account/confirm</span></span><a class="headerlink" href="#post--api-auth-account-confirm" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>activate user account after registration</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">POST</span> <span class="nn">/api/auth/account/confirm</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<p><strong>Example response</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">200</span> <span class="ne">OK</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
 | 
			
		||||
<span class="p">{</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"auth_token"</span><span class="p">:</span><span class="w"> </span><span class="s2">"JSON Web Token"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"account confirmation successful"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w"></span>
 | 
			
		||||
<span class="p">}</span><span class="w"></span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Request JSON Object<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>token</strong> (<em>string</em>) – confirmation token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – account confirmation successful</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – invalid payload</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1">500 Internal Server Error</a></span> – error, please try again or contact the administrator</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
</dd></dl>
 | 
			
		||||
 | 
			
		||||
<dl class="http post">
 | 
			
		||||
<dt class="sig sig-object http" id="post--api-auth-account-resend-confirmation">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">POST</span> </span><span class="sig-name descname"><span class="pre">/api/auth/account/resend-confirmation</span></span><a class="headerlink" href="#post--api-auth-account-resend-confirmation" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>resend email with instructions to confirm account</p>
 | 
			
		||||
<p>If email sending is disabled, this endpoint is not available</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">POST</span> <span class="nn">/api/auth/account/resend-confirmation</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<p><strong>Example response</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">200</span> <span class="ne">OK</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
 | 
			
		||||
<span class="p">{</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"confirmation email resent"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w"></span>
 | 
			
		||||
<span class="p">}</span><span class="w"></span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Request JSON Object<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>email</strong> (<em>string</em>) – user email</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – confirmation email resent</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – invalid payload</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5">404 Not Found</a></span> – the requested URL was not found on the server</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1">500 Internal Server Error</a></span> – error, please try again or contact the administrator</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
</dd></dl>
 | 
			
		||||
 | 
			
		||||
<dl class="http post">
 | 
			
		||||
<dt class="sig sig-object http" id="post--api-auth-login">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">POST</span> </span><span class="sig-name descname"><span class="pre">/api/auth/login</span></span><a class="headerlink" href="#post--api-auth-login" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>user login</p>
 | 
			
		||||
<p>Only user with an active account can log in.</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">POST</span> <span class="nn">/api/auth/login</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
@@ -222,7 +301,7 @@
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>error on login</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">404</span> <span class="ne">NOT FOUND</span>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">401</span> <span class="ne">UNAUTHORIZED</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
 | 
			
		||||
<span class="p">{</span><span class="w"></span>
 | 
			
		||||
@@ -232,13 +311,13 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Request JSON Object</dt>
 | 
			
		||||
<dt class="field-odd">Request JSON Object<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>email</strong> (<em>string</em>) – user email</p></li>
 | 
			
		||||
<li><p><strong>password_conf</strong> (<em>string</em>) – password confirmation</p></li>
 | 
			
		||||
<li><p><strong>password</strong> (<em>string</em>) – password</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes</dt>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – successfully logged in</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – invalid payload</p></li>
 | 
			
		||||
@@ -249,59 +328,10 @@
 | 
			
		||||
</dl>
 | 
			
		||||
</dd></dl>
 | 
			
		||||
 | 
			
		||||
<dl class="http get">
 | 
			
		||||
<dt class="sig sig-object http" id="get--api-auth-logout">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">GET</span> </span><span class="sig-name descname"><span class="pre">/api/auth/logout</span></span><a class="headerlink" href="#get--api-auth-logout" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>user logout</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">GET</span> <span class="nn">/api/auth/logout</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<p><strong>Example responses</strong>:</p>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>successful logout</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">200</span> <span class="ne">OK</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
 | 
			
		||||
<span class="p">{</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"successfully logged out"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w"></span>
 | 
			
		||||
<span class="p">}</span><span class="w"></span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>error on login</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">401</span> <span class="ne">UNAUTHORIZED</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
 | 
			
		||||
<span class="p">{</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"provide a valid auth token"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"error"</span><span class="w"></span>
 | 
			
		||||
<span class="p">}</span><span class="w"></span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Request Headers</dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes</dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – successfully logged out</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – provide a valid auth token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
</dd></dl>
 | 
			
		||||
 | 
			
		||||
<dl class="http get">
 | 
			
		||||
<dt class="sig sig-object http" id="get--api-auth-profile">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">GET</span> </span><span class="sig-name descname"><span class="pre">/api/auth/profile</span></span><a class="headerlink" href="#get--api-auth-profile" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>get authenticated user info</p>
 | 
			
		||||
<dd><p>get authenticated user info (profile, account, preferences)</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">GET</span> <span class="nn">/api/auth/profile</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
@@ -320,6 +350,7 @@
 | 
			
		||||
<span class="w">    </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sam@example.com"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"first_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"imperial_units"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"is_active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"language"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"last_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"location"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
@@ -380,12 +411,12 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Request Headers</dt>
 | 
			
		||||
<dt class="field-odd">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes</dt>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success.</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -402,7 +433,7 @@
 | 
			
		||||
<dl class="http post">
 | 
			
		||||
<dt class="sig sig-object http" id="post--api-auth-profile-edit">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">POST</span> </span><span class="sig-name descname"><span class="pre">/api/auth/profile/edit</span></span><a class="headerlink" href="#post--api-auth-profile-edit" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>edit authenticated user</p>
 | 
			
		||||
<dd><p>edit authenticated user profile</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">POST</span> <span class="nn">/api/auth/profile/edit</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
@@ -421,6 +452,7 @@
 | 
			
		||||
<span class="w">    </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sam@example.com"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"first_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"imperial_units"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"is_active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"language"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"last_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"location"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
@@ -482,28 +514,25 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Request JSON Object</dt>
 | 
			
		||||
<dt class="field-odd">Request JSON Object<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>first_name</strong> (<em>string</em>) – user first name</p></li>
 | 
			
		||||
<li><p><strong>last_name</strong> (<em>string</em>) – user last name</p></li>
 | 
			
		||||
<li><p><strong>location</strong> (<em>string</em>) – user location</p></li>
 | 
			
		||||
<li><p><strong>bio</strong> (<em>string</em>) – user biography</p></li>
 | 
			
		||||
<li><p><strong>birth_date</strong> (<em>string</em>) – user birth date (format: <code class="docutils literal notranslate"><span class="pre">%Y-%m-%d</span></code>)</p></li>
 | 
			
		||||
<li><p><strong>password</strong> (<em>string</em>) – user password</p></li>
 | 
			
		||||
<li><p><strong>password_conf</strong> (<em>string</em>) – user password confirmation</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers</dt>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – user profile updated</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – <ul>
 | 
			
		||||
<li><p>invalid payload</p></li>
 | 
			
		||||
<li><p>password: password and password confirmation don’t match</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -540,6 +569,7 @@
 | 
			
		||||
<span class="w">    </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sam@example.com"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"first_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"imperial_units"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"is_active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"language"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"last_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"location"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
@@ -601,19 +631,20 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Request JSON Object</dt>
 | 
			
		||||
<dt class="field-odd">Request JSON Object<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>timezone</strong> (<em>string</em>) – user time zone</p></li>
 | 
			
		||||
<li><p><strong>weekm</strong> (<em>string</em>) – does week start on Monday?</p></li>
 | 
			
		||||
<li><p><strong>weekm</strong> (<em>boolean</em>) – does week start on Monday?</p></li>
 | 
			
		||||
<li><p><strong>language</strong> (<em>string</em>) – language preferences</p></li>
 | 
			
		||||
<li><p><strong>imperial_units</strong> (<em>boolean</em>) – display distance in imperial units</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers</dt>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – user preferences updated</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – <ul>
 | 
			
		||||
@@ -660,19 +691,19 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Request JSON Object</dt>
 | 
			
		||||
<dt class="field-odd">Request JSON Object<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>color</strong> (<em>string</em>) – valid hexadecimal color</p></li>
 | 
			
		||||
<li><p><strong>is_active</strong> (<em>boolean</em>) – is sport available when adding a workout</p></li>
 | 
			
		||||
<li><p><strong>stopped_speed_threshold</strong> (<em>float</em>) – stopped speed threshold used by gpxpy</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers</dt>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – user preferences updated</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – <ul>
 | 
			
		||||
@@ -711,17 +742,17 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>sport_id</strong> (<em>string</em>) – sport id</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers</dt>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.5">204 No Content</a></span> – user preferences deleted</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -760,17 +791,17 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Form Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Form Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>file</strong> – image file (allowed extensions: .jpg, .png, .gif)</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers</dt>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – user picture updated</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – <ul>
 | 
			
		||||
@@ -808,12 +839,12 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Request Headers</dt>
 | 
			
		||||
<dt class="field-odd">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes</dt>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.5">204 No Content</a></span> – picture deleted</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -832,6 +863,7 @@
 | 
			
		||||
<dt class="sig sig-object http" id="post--api-auth-password-reset-request">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">POST</span> </span><span class="sig-name descname"><span class="pre">/api/auth/password/reset-request</span></span><a class="headerlink" href="#post--api-auth-password-reset-request" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>handle password reset request</p>
 | 
			
		||||
<p>If email sending is disabled, this endpoint is not available</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">POST</span> <span class="nn">/api/auth/password/reset-request</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
@@ -848,15 +880,146 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Request JSON Object</dt>
 | 
			
		||||
<dt class="field-odd">Request JSON Object<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>email</strong> (<em>string</em>) – user email</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes</dt>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – password reset request processed</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – invalid payload</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5">404 Not Found</a></span> – the requested URL was not found on the server</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
</dd></dl>
 | 
			
		||||
 | 
			
		||||
<dl class="http patch">
 | 
			
		||||
<dt class="sig sig-object http" id="patch--api-auth-profile-edit-account">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">PATCH</span> </span><span class="sig-name descname"><span class="pre">/api/auth/profile/edit/account</span></span><a class="headerlink" href="#patch--api-auth-profile-edit-account" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>update authenticated user email and password</p>
 | 
			
		||||
<p>It sends emails if sending is enabled:</p>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>Password change</p></li>
 | 
			
		||||
<li><p>Email change:</p>
 | 
			
		||||
<ul>
 | 
			
		||||
<li><p>one to the current address to inform user</p></li>
 | 
			
		||||
<li><p>another one to the new address to confirm it.</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</li>
 | 
			
		||||
</ul>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">PATCH</span> <span class="nn">/api/auth/profile/edit/account</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<p><strong>Example response</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">200</span> <span class="ne">OK</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
 | 
			
		||||
<span class="p">{</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"admin"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"bio"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"birth_date"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"created_at"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Sun, 14 Jul 2019 14:09:58 GMT"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sam@example.com"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"first_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"imperial_units"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"is_active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"language"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"last_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"location"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"nb_sports"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"nb_workouts"</span><span class="p">:</span><span class="w"> </span><span class="mi">6</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"picture"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"records"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="p">{</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">9</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"record_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"AS"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"sport_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"user"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sam"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"value"</span><span class="p">:</span><span class="w"> </span><span class="mi">18</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"workout_date"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Sun, 07 Jul 2019 08:00:00 GMT"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"workout_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"hvYBqYBRa7wwXpaStWR4V2"</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="p">},</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="p">{</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"record_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"FD"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"sport_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"user"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sam"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"value"</span><span class="p">:</span><span class="w"> </span><span class="mi">18</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"workout_date"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Sun, 07 Jul 2019 08:00:00 GMT"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"workout_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"hvYBqYBRa7wwXpaStWR4V2"</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="p">},</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="p">{</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">11</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"record_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"LD"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"sport_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"user"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sam"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1:01:00"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"workout_date"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Sun, 07 Jul 2019 08:00:00 GMT"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"workout_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"hvYBqYBRa7wwXpaStWR4V2"</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="p">},</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="p">{</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">12</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"record_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MS"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"sport_id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"user"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sam"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"value"</span><span class="p">:</span><span class="w"> </span><span class="mi">18</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"workout_date"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Sun, 07 Jul 2019 08:00:00 GMT"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"workout_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"hvYBqYBRa7wwXpaStWR4V2"</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="p">}</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="p">],</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"sports_list"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="mi">1</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="mi">4</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="mi">6</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="p">],</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"timezone"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Europe/Paris"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"total_distance"</span><span class="p">:</span><span class="w"> </span><span class="mf">67.895</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"total_duration"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6:50:27"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"username"</span><span class="p">:</span><span class="w"> </span><span class="nt">"sam"</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"weekm"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="p">},</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"user account updated"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w"></span>
 | 
			
		||||
<span class="p">}</span><span class="w"></span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Request JSON Object<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>email</strong> (<em>string</em>) – user email</p></li>
 | 
			
		||||
<li><p><strong>password</strong> (<em>string</em>) – user current password</p></li>
 | 
			
		||||
<li><p><strong>new_password</strong> (<em>string</em>) – user new password</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – user account updated</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – <ul>
 | 
			
		||||
<li><p>invalid payload</p></li>
 | 
			
		||||
<li><p>email is missing</p></li>
 | 
			
		||||
<li><p>current password is missing</p></li>
 | 
			
		||||
<li><p>email: valid email must be provided</p></li>
 | 
			
		||||
<li><p>password: 8 characters required</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
<li><p>provide a valid auth token</p></li>
 | 
			
		||||
<li><p>signature expired, please log in again</p></li>
 | 
			
		||||
<li><p>invalid token, please log in again</p></li>
 | 
			
		||||
<li><p>invalid credentials</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1">500 Internal Server Error</a></span> – error, please try again or contact the administrator</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
@@ -865,7 +1028,8 @@
 | 
			
		||||
<dl class="http post">
 | 
			
		||||
<dt class="sig sig-object http" id="post--api-auth-password-update">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">POST</span> </span><span class="sig-name descname"><span class="pre">/api/auth/password/update</span></span><a class="headerlink" href="#post--api-auth-password-update" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>update user password</p>
 | 
			
		||||
<dd><p>update user password after password reset request</p>
 | 
			
		||||
<p>It sends emails if sending is enabled</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">POST</span> <span class="nn">/api/auth/password/update</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
@@ -882,14 +1046,13 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Request JSON Object</dt>
 | 
			
		||||
<dt class="field-odd">Request JSON Object<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>password</strong> (<em>string</em>) – password (8 characters required)</p></li>
 | 
			
		||||
<li><p><strong>password_conf</strong> (<em>string</em>) – password confirmation</p></li>
 | 
			
		||||
<li><p><strong>token</strong> (<em>string</em>) – password reset token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes</dt>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – password updated</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – invalid payload</p></li>
 | 
			
		||||
@@ -900,6 +1063,41 @@
 | 
			
		||||
</dl>
 | 
			
		||||
</dd></dl>
 | 
			
		||||
 | 
			
		||||
<dl class="http post">
 | 
			
		||||
<dt class="sig sig-object http" id="post--api-auth-email-update">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">POST</span> </span><span class="sig-name descname"><span class="pre">/api/auth/email/update</span></span><a class="headerlink" href="#post--api-auth-email-update" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>update user email after confirmation</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">POST</span> <span class="nn">/api/auth/email/update</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<p><strong>Example response</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span> <span class="m">200</span> <span class="ne">OK</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
 | 
			
		||||
<span class="p">{</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"email updated"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w"></span>
 | 
			
		||||
<span class="p">}</span><span class="w"></span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Request JSON Object<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>token</strong> (<em>string</em>) – password reset token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – email updated</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – invalid payload</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1">500 Internal Server Error</a></span> – error, please try again or contact the administrator</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
</dd></dl>
 | 
			
		||||
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -915,7 +1113,7 @@
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        © Copyright 2018 - 2022, SamR1.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 4.4.0.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.0.2.<br/>
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</footer>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
 | 
			
		||||
    <title>Configuration — FitTrackee 0.5.7
 | 
			
		||||
    <title>Configuration — FitTrackee 0.6.10
 | 
			
		||||
 documentation</title>
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
    <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
 | 
			
		||||
    <script src="../_static/jquery.js"></script>
 | 
			
		||||
    <script src="../_static/underscore.js"></script>
 | 
			
		||||
    <script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
 | 
			
		||||
    <script src="../_static/doctools.js"></script>
 | 
			
		||||
    <link rel="index" title="Index" href="../genindex.html" />
 | 
			
		||||
    <link rel="search" title="Search" href="../search.html" />
 | 
			
		||||
@@ -40,7 +41,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
        <a class="navbar-brand" href="../index.html">
 | 
			
		||||
          FitTrackee</a>
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.5.7
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.6.10
 | 
			
		||||
</b></span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@@ -61,6 +62,7 @@
 | 
			
		||||
      aria-labelledby="dLabelGlobalToc"><ul class="current">
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../features.html">Features</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../installation.html">Installation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../cli.html">Command line interface</a></li>
 | 
			
		||||
<li class="toctree-l1 current"><a class="reference internal" href="index.html">API documentation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../troubleshooting/index.html">Troubleshooting</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../changelog.html">Change log</a></li>
 | 
			
		||||
@@ -126,7 +128,7 @@
 | 
			
		||||
    <div class="body col-md-12 content" role="main">
 | 
			
		||||
      
 | 
			
		||||
  <section id="configuration">
 | 
			
		||||
<h1>Configuration<a class="headerlink" href="#configuration" title="Permalink to this headline">¶</a></h1>
 | 
			
		||||
<h1>Configuration<a class="headerlink" href="#configuration" title="Permalink to this heading">¶</a></h1>
 | 
			
		||||
<dl class="http get">
 | 
			
		||||
<dt class="sig sig-object http" id="get--api-config">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">GET</span> </span><span class="sig-name descname"><span class="pre">/api/config</span></span><a class="headerlink" href="#get--api-config" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
@@ -142,20 +144,22 @@
 | 
			
		||||
 | 
			
		||||
<span class="p">{</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"admin_contact"</span><span class="p">:</span><span class="w"> </span><span class="s2">"admin@example.com"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"gpx_limit_import"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"is_email_sending_enabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"is_registration_enabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"max_single_file_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">1048576</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"max_zip_file_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">10485760</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"max_users"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"max_zip_file_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">10485760</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"map_attribution"</span><span class="p">:</span><span class="w"> </span><span class="nt">"&copy; <a href=http://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors"</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0.5.7"</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0.6.10"</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="p">},</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w"></span>
 | 
			
		||||
<span class="p">}</span><span class="w"></span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.1">500 Internal Server Error</a></span> – error on getting configuration</p></li>
 | 
			
		||||
@@ -180,32 +184,37 @@
 | 
			
		||||
 | 
			
		||||
<span class="p">{</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"admin_contact"</span><span class="p">:</span><span class="w"> </span><span class="s2">"admin@example.com"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"gpx_limit_import"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"is_registration_enabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"is_email_sending_enabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"is_registration_enabled"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"max_single_file_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">1048576</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"max_users"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"max_zip_file_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">10485760</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"max_users"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"map_attribution"</span><span class="p">:</span><span class="w"> </span><span class="nt">"&copy; <a href=http://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors"</span><span class="w"></span>
 | 
			
		||||
<span class="w">    </span><span class="nt">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0.6.10"</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="p">},</span><span class="w"></span>
 | 
			
		||||
<span class="w">  </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w"></span>
 | 
			
		||||
<span class="p">}</span><span class="w"></span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Request JSON Object</dt>
 | 
			
		||||
<dt class="field-odd">Request JSON Object<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>admin_contact</strong> (<em>string</em>) – email to contact the administrator</p></li>
 | 
			
		||||
<li><p><strong>gpx_limit_import</strong> (<em>integer</em>) – max number of files in zip archive</p></li>
 | 
			
		||||
<li><p><strong>is_registration_enabled</strong> (<em>boolean</em>) – is registration enabled ?</p></li>
 | 
			
		||||
<li><p><strong>max_single_file_size</strong> (<em>integer</em>) – max size of a single file</p></li>
 | 
			
		||||
<li><p><strong>max_zip_file_size</strong> (<em>integer</em>) – max size of a zip archive</p></li>
 | 
			
		||||
<li><p><strong>max_users</strong> (<em>integer</em>) – max users allowed to register on instance</p></li>
 | 
			
		||||
<li><p><strong>max_zip_file_size</strong> (<em>integer</em>) – max size of a zip archive</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers</dt>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – invalid payload</p></li>
 | 
			
		||||
@@ -213,6 +222,7 @@
 | 
			
		||||
<li><p>provide a valid auth token</p></li>
 | 
			
		||||
<li><p>signature expired, please log in again</p></li>
 | 
			
		||||
<li><p>invalid token, please log in again</p></li>
 | 
			
		||||
<li><p>valid email must be provided for admin contact</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4">403 Forbidden</a></span> – you do not have permissions</p></li>
 | 
			
		||||
@@ -242,7 +252,7 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
@@ -265,7 +275,7 @@
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        © Copyright 2018 - 2022, SamR1.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 4.4.0.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.0.2.<br/>
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</footer>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
 | 
			
		||||
    <title>API documentation — FitTrackee 0.5.7
 | 
			
		||||
    <title>API documentation — FitTrackee 0.6.10
 | 
			
		||||
 documentation</title>
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
 | 
			
		||||
@@ -13,11 +13,12 @@
 | 
			
		||||
    <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
 | 
			
		||||
    <script src="../_static/jquery.js"></script>
 | 
			
		||||
    <script src="../_static/underscore.js"></script>
 | 
			
		||||
    <script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
 | 
			
		||||
    <script src="../_static/doctools.js"></script>
 | 
			
		||||
    <link rel="index" title="Index" href="../genindex.html" />
 | 
			
		||||
    <link rel="search" title="Search" href="../search.html" />
 | 
			
		||||
    <link rel="next" title="Authentication" href="auth.html" />
 | 
			
		||||
    <link rel="prev" title="Installation" href="../installation.html" />
 | 
			
		||||
    <link rel="prev" title="Command line interface" href="../cli.html" />
 | 
			
		||||
<meta charset='utf-8'>
 | 
			
		||||
<meta http-equiv='X-UA-Compatible' content='IE=edge,chrome=1'>
 | 
			
		||||
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1'>
 | 
			
		||||
@@ -40,7 +41,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
        <a class="navbar-brand" href="../index.html">
 | 
			
		||||
          FitTrackee</a>
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.5.7
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.6.10
 | 
			
		||||
</b></span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@@ -61,6 +62,7 @@
 | 
			
		||||
      aria-labelledby="dLabelGlobalToc"><ul class="current">
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../features.html">Features</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../installation.html">Installation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../cli.html">Command line interface</a></li>
 | 
			
		||||
<li class="toctree-l1 current"><a class="current reference internal" href="#">API documentation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../troubleshooting/index.html">Troubleshooting</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../changelog.html">Change log</a></li>
 | 
			
		||||
@@ -87,7 +89,7 @@
 | 
			
		||||
              
 | 
			
		||||
                
 | 
			
		||||
  <li>
 | 
			
		||||
    <a href="../installation.html" title="Previous Chapter: Installation"><span class="glyphicon glyphicon-chevron-left visible-sm"></span><span class="hidden-sm hidden-tablet">« Installation</span>
 | 
			
		||||
    <a href="../cli.html" title="Previous Chapter: Command line interface"><span class="glyphicon glyphicon-chevron-left visible-sm"></span><span class="hidden-sm hidden-tablet">« Command line ...</span>
 | 
			
		||||
    </a>
 | 
			
		||||
  </li>
 | 
			
		||||
  <li>
 | 
			
		||||
@@ -126,7 +128,7 @@
 | 
			
		||||
    <div class="body col-md-12 content" role="main">
 | 
			
		||||
      
 | 
			
		||||
  <section id="api-documentation">
 | 
			
		||||
<h1>API documentation<a class="headerlink" href="#api-documentation" title="Permalink to this headline">¶</a></h1>
 | 
			
		||||
<h1>API documentation<a class="headerlink" href="#api-documentation" title="Permalink to this heading">¶</a></h1>
 | 
			
		||||
<div class="toctree-wrapper compound">
 | 
			
		||||
<p class="caption" role="heading"><span class="caption-text">Endpoints:</span></p>
 | 
			
		||||
<ul>
 | 
			
		||||
@@ -154,7 +156,7 @@
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        © Copyright 2018 - 2022, SamR1.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 4.4.0.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.0.2.<br/>
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</footer>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
 | 
			
		||||
    <title>Records — FitTrackee 0.5.7
 | 
			
		||||
    <title>Records — FitTrackee 0.6.10
 | 
			
		||||
 documentation</title>
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
    <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
 | 
			
		||||
    <script src="../_static/jquery.js"></script>
 | 
			
		||||
    <script src="../_static/underscore.js"></script>
 | 
			
		||||
    <script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
 | 
			
		||||
    <script src="../_static/doctools.js"></script>
 | 
			
		||||
    <link rel="index" title="Index" href="../genindex.html" />
 | 
			
		||||
    <link rel="search" title="Search" href="../search.html" />
 | 
			
		||||
@@ -40,7 +41,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
        <a class="navbar-brand" href="../index.html">
 | 
			
		||||
          FitTrackee</a>
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.5.7
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.6.10
 | 
			
		||||
</b></span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@@ -61,6 +62,7 @@
 | 
			
		||||
      aria-labelledby="dLabelGlobalToc"><ul class="current">
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../features.html">Features</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../installation.html">Installation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../cli.html">Command line interface</a></li>
 | 
			
		||||
<li class="toctree-l1 current"><a class="reference internal" href="index.html">API documentation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../troubleshooting/index.html">Troubleshooting</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../changelog.html">Change log</a></li>
 | 
			
		||||
@@ -126,7 +128,7 @@
 | 
			
		||||
    <div class="body col-md-12 content" role="main">
 | 
			
		||||
      
 | 
			
		||||
  <section id="records">
 | 
			
		||||
<h1>Records<a class="headerlink" href="#records" title="Permalink to this headline">¶</a></h1>
 | 
			
		||||
<h1>Records<a class="headerlink" href="#records" title="Permalink to this heading">¶</a></h1>
 | 
			
		||||
<dl class="http get">
 | 
			
		||||
<dt class="sig sig-object http" id="get--api-records">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">GET</span> </span><span class="sig-name descname"><span class="pre">/api/records</span></span><a class="headerlink" href="#get--api-records" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
@@ -212,12 +214,12 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Request Headers</dt>
 | 
			
		||||
<dt class="field-odd">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes</dt>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -246,7 +248,7 @@
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        © Copyright 2018 - 2022, SamR1.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 4.4.0.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.0.2.<br/>
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</footer>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
 | 
			
		||||
    <title>Sports — FitTrackee 0.5.7
 | 
			
		||||
    <title>Sports — FitTrackee 0.6.10
 | 
			
		||||
 documentation</title>
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
    <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
 | 
			
		||||
    <script src="../_static/jquery.js"></script>
 | 
			
		||||
    <script src="../_static/underscore.js"></script>
 | 
			
		||||
    <script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
 | 
			
		||||
    <script src="../_static/doctools.js"></script>
 | 
			
		||||
    <link rel="index" title="Index" href="../genindex.html" />
 | 
			
		||||
    <link rel="search" title="Search" href="../search.html" />
 | 
			
		||||
@@ -40,7 +41,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
        <a class="navbar-brand" href="../index.html">
 | 
			
		||||
          FitTrackee</a>
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.5.7
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.6.10
 | 
			
		||||
</b></span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@@ -61,6 +62,7 @@
 | 
			
		||||
      aria-labelledby="dLabelGlobalToc"><ul class="current">
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../features.html">Features</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../installation.html">Installation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../cli.html">Command line interface</a></li>
 | 
			
		||||
<li class="toctree-l1 current"><a class="reference internal" href="index.html">API documentation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../troubleshooting/index.html">Troubleshooting</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../changelog.html">Change log</a></li>
 | 
			
		||||
@@ -126,7 +128,7 @@
 | 
			
		||||
    <div class="body col-md-12 content" role="main">
 | 
			
		||||
      
 | 
			
		||||
  <section id="sports">
 | 
			
		||||
<h1>Sports<a class="headerlink" href="#sports" title="Permalink to this headline">¶</a></h1>
 | 
			
		||||
<h1>Sports<a class="headerlink" href="#sports" title="Permalink to this heading">¶</a></h1>
 | 
			
		||||
<dl class="http get">
 | 
			
		||||
<dt class="sig sig-object http" id="get--api-sports">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">GET</span> </span><span class="sig-name descname"><span class="pre">/api/sports</span></span><a class="headerlink" href="#get--api-sports" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
@@ -270,12 +272,12 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Request Headers</dt>
 | 
			
		||||
<dt class="field-odd">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes</dt>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -361,17 +363,17 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>sport_id</strong> (<em>integer</em>) – sport id</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers</dt>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -436,22 +438,22 @@ Authenticated user must be an admin</p>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>sport_id</strong> (<em>integer</em>) – sport id</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request JSON Object</dt>
 | 
			
		||||
<dt class="field-even">Request JSON Object<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><strong>is_active</strong> (<em>string</em>) – sport active status</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Request Headers</dt>
 | 
			
		||||
<dt class="field-odd">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes</dt>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – sport updated</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – invalid payload</p></li>
 | 
			
		||||
@@ -484,7 +486,7 @@ Authenticated user must be an admin</p>
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        © Copyright 2018 - 2022, SamR1.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 4.4.0.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.0.2.<br/>
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</footer>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
 | 
			
		||||
    <title>Statistics — FitTrackee 0.5.7
 | 
			
		||||
    <title>Statistics — FitTrackee 0.6.10
 | 
			
		||||
 documentation</title>
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
    <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
 | 
			
		||||
    <script src="../_static/jquery.js"></script>
 | 
			
		||||
    <script src="../_static/underscore.js"></script>
 | 
			
		||||
    <script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
 | 
			
		||||
    <script src="../_static/doctools.js"></script>
 | 
			
		||||
    <link rel="index" title="Index" href="../genindex.html" />
 | 
			
		||||
    <link rel="search" title="Search" href="../search.html" />
 | 
			
		||||
@@ -40,7 +41,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
        <a class="navbar-brand" href="../index.html">
 | 
			
		||||
          FitTrackee</a>
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.5.7
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.6.10
 | 
			
		||||
</b></span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@@ -61,6 +62,7 @@
 | 
			
		||||
      aria-labelledby="dLabelGlobalToc"><ul class="current">
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../features.html">Features</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../installation.html">Installation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../cli.html">Command line interface</a></li>
 | 
			
		||||
<li class="toctree-l1 current"><a class="reference internal" href="index.html">API documentation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../troubleshooting/index.html">Troubleshooting</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../changelog.html">Change log</a></li>
 | 
			
		||||
@@ -126,7 +128,7 @@
 | 
			
		||||
    <div class="body col-md-12 content" role="main">
 | 
			
		||||
      
 | 
			
		||||
  <section id="statistics">
 | 
			
		||||
<h1>Statistics<a class="headerlink" href="#statistics" title="Permalink to this headline">¶</a></h1>
 | 
			
		||||
<h1>Statistics<a class="headerlink" href="#statistics" title="Permalink to this heading">¶</a></h1>
 | 
			
		||||
<dl class="http get">
 | 
			
		||||
<dt class="sig sig-object http" id="get--api-stats-(user_name)-by_time">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">GET</span> </span><span class="sig-name descname"><span class="pre">/api/stats/</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="pre">user_name</span></em><span class="sig-paren">)</span><span class="sig-name descname"><span class="pre">/by_time</span></span><a class="headerlink" href="#get--api-stats-(user_name)-by_time" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
@@ -204,12 +206,12 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>user_name</strong> (<em>integer</em>) – user name</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Query Parameters</dt>
 | 
			
		||||
<dt class="field-even">Query Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><strong>from</strong> (<em>string</em>) – start date (format: <code class="docutils literal notranslate"><span class="pre">%Y-%m-%d</span></code>)</p></li>
 | 
			
		||||
<li><p><strong>to</strong> (<em>string</em>) – end date (format: <code class="docutils literal notranslate"><span class="pre">%Y-%m-%d</span></code>)</p></li>
 | 
			
		||||
@@ -223,12 +225,12 @@
 | 
			
		||||
</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Request Headers</dt>
 | 
			
		||||
<dt class="field-odd">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes</dt>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -318,22 +320,22 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>user_name</strong> (<em>integer</em>) – user name</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Query Parameters</dt>
 | 
			
		||||
<dt class="field-even">Query Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><strong>sport_id</strong> (<em>integer</em>) – sport id</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Request Headers</dt>
 | 
			
		||||
<dt class="field-odd">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes</dt>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -376,12 +378,12 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Request Headers</dt>
 | 
			
		||||
<dt class="field-odd">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes</dt>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -411,7 +413,7 @@
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        © Copyright 2018 - 2022, SamR1.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 4.4.0.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.0.2.<br/>
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</footer>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
 | 
			
		||||
    <title>Users — FitTrackee 0.5.7
 | 
			
		||||
    <title>Users — FitTrackee 0.6.10
 | 
			
		||||
 documentation</title>
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
    <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
 | 
			
		||||
    <script src="../_static/jquery.js"></script>
 | 
			
		||||
    <script src="../_static/underscore.js"></script>
 | 
			
		||||
    <script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
 | 
			
		||||
    <script src="../_static/doctools.js"></script>
 | 
			
		||||
    <link rel="index" title="Index" href="../genindex.html" />
 | 
			
		||||
    <link rel="search" title="Search" href="../search.html" />
 | 
			
		||||
@@ -40,7 +41,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
        <a class="navbar-brand" href="../index.html">
 | 
			
		||||
          FitTrackee</a>
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.5.7
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.6.10
 | 
			
		||||
</b></span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@@ -61,6 +62,7 @@
 | 
			
		||||
      aria-labelledby="dLabelGlobalToc"><ul class="current">
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../features.html">Features</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../installation.html">Installation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../cli.html">Command line interface</a></li>
 | 
			
		||||
<li class="toctree-l1 current"><a class="reference internal" href="index.html">API documentation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../troubleshooting/index.html">Troubleshooting</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../changelog.html">Change log</a></li>
 | 
			
		||||
@@ -126,11 +128,13 @@
 | 
			
		||||
    <div class="body col-md-12 content" role="main">
 | 
			
		||||
      
 | 
			
		||||
  <section id="users">
 | 
			
		||||
<h1>Users<a class="headerlink" href="#users" title="Permalink to this headline">¶</a></h1>
 | 
			
		||||
<h1>Users<a class="headerlink" href="#users" title="Permalink to this heading">¶</a></h1>
 | 
			
		||||
<dl class="http get">
 | 
			
		||||
<dt class="sig sig-object http" id="get--api-users">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">GET</span> </span><span class="sig-name descname"><span class="pre">/api/users</span></span><a class="headerlink" href="#get--api-users" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>Get all users</p>
 | 
			
		||||
<dd><p>Get all users (regardless their account status), if authenticated user
 | 
			
		||||
has admin rights</p>
 | 
			
		||||
<p>It returns user preferences only for authenticated user.</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>without parameters</p></li>
 | 
			
		||||
@@ -160,6 +164,7 @@
 | 
			
		||||
<span class="w">        </span><span class="nt">"created_at"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Sun, 14 Jul 2019 14:09:58 GMT"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"admin@example.com"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"first_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"is_admin"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"imperial_units"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"language"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"last_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
@@ -213,7 +218,8 @@
 | 
			
		||||
<span class="w">        </span><span class="nt">"timezone"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Europe/Paris"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"total_distance"</span><span class="p">:</span><span class="w"> </span><span class="mf">67.895</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"total_duration"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6:50:27"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"username"</span><span class="p">:</span><span class="w"> </span><span class="s2">"admin"</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"username"</span><span class="p">:</span><span class="w"> </span><span class="s2">"admin"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"weekm"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="p">},</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="p">{</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"admin"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
@@ -222,6 +228,7 @@
 | 
			
		||||
<span class="w">        </span><span class="nt">"created_at"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Sat, 20 Jul 2019 11:27:03 GMT"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sam@example.com"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"first_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"is_admin"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"language"</span><span class="p">:</span><span class="w"> </span><span class="s2">"fr"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"last_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">        </span><span class="nt">"location"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
@@ -242,22 +249,22 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Query Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Query Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>page</strong> (<em>integer</em>) – page if using pagination (default: 1)</p></li>
 | 
			
		||||
<li><p><strong>per_page</strong> (<em>integer</em>) – number of users per page (default: 10, max: 50)</p></li>
 | 
			
		||||
<li><p><strong>q</strong> (<em>string</em>) – query on user name</p></li>
 | 
			
		||||
<li><p><strong>order_by</strong> (<em>string</em>) – sorting criteria (<code class="docutils literal notranslate"><span class="pre">username</span></code>, <code class="docutils literal notranslate"><span class="pre">created_at</span></code>,
 | 
			
		||||
<code class="docutils literal notranslate"><span class="pre">workouts_count</span></code>, <code class="docutils literal notranslate"><span class="pre">admin</span></code>)</p></li>
 | 
			
		||||
<code class="docutils literal notranslate"><span class="pre">workouts_count</span></code>, <code class="docutils literal notranslate"><span class="pre">admin</span></code>, <code class="docutils literal notranslate"><span class="pre">is_active</span></code>)</p></li>
 | 
			
		||||
<li><p><strong>order</strong> (<em>string</em>) – sorting order (default: <code class="docutils literal notranslate"><span class="pre">asc</span></code>)</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers</dt>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -274,7 +281,9 @@
 | 
			
		||||
<dl class="http get">
 | 
			
		||||
<dt class="sig sig-object http" id="get--api-users-(user_name)">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">GET</span> </span><span class="sig-name descname"><span class="pre">/api/users/</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="pre">user_name</span></em><span class="sig-paren">)</span><a class="headerlink" href="#get--api-users-(user_name)" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>Get single user details</p>
 | 
			
		||||
<dd><p>Get single user details. Only user with admin rights can get other users
 | 
			
		||||
details.</p>
 | 
			
		||||
<p>It returns user preferences only for authenticated user.</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">GET</span> <span class="nn">/api/users/admin</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
@@ -294,6 +303,7 @@
 | 
			
		||||
<span class="w">      </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"admin@example.com"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="nt">"first_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="nt">"imperial_units"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="nt">"is_admin"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="nt">"language"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="nt">"last_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="nt">"location"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
@@ -354,17 +364,17 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>user_name</strong> (<em>integer</em>) – user name</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers</dt>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -397,12 +407,12 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>user_name</strong> (<em>integer</em>) – user name</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes</dt>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5">404 Not Found</a></span> – <ul>
 | 
			
		||||
@@ -418,10 +428,17 @@
 | 
			
		||||
<dl class="http patch">
 | 
			
		||||
<dt class="sig sig-object http" id="patch--api-users-(user_name)">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">PATCH</span> </span><span class="sig-name descname"><span class="pre">/api/users/</span></span><span class="sig-paren">(</span><em class="sig-param"><span class="pre">user_name</span></em><span class="sig-paren">)</span><a class="headerlink" href="#patch--api-users-(user_name)" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>Update user to add admin rights</p>
 | 
			
		||||
<dd><p>Update user account</p>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>add/remove admin rights (regardless user account status)</p></li>
 | 
			
		||||
<li><p>reset password (and send email to update user password,
 | 
			
		||||
if sending enabled)</p></li>
 | 
			
		||||
<li><p>update user email (and send email to new user email, if sending enabled)</p></li>
 | 
			
		||||
<li><p>activate account for an inactive user</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<p>Only user with admin rights can modify another user</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">PATCH</span> <span class="nn">api/users/<user_name></span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">PATCH</span> <span class="nn">/api/users/<user_name></span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -439,6 +456,7 @@
 | 
			
		||||
<span class="w">      </span><span class="nt">"email"</span><span class="p">:</span><span class="w"> </span><span class="s2">"admin@example.com"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="nt">"first_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="nt">"imperial_units"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="nt">"is_active"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="nt">"language"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en"</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="nt">"last_name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
<span class="w">      </span><span class="nt">"location"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"></span>
 | 
			
		||||
@@ -499,24 +517,33 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>user_name</strong> (<em>string</em>) – user name</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request JSON Object</dt>
 | 
			
		||||
<dt class="field-even">Request JSON Object<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><strong>activate</strong> (<em>boolean</em>) – activate user account</p></li>
 | 
			
		||||
<li><p><strong>admin</strong> (<em>boolean</em>) – does the user have administrator rights</p></li>
 | 
			
		||||
<li><p><strong>new_email</strong> (<em>boolean</em>) – new user email</p></li>
 | 
			
		||||
<li><p><strong>reset_password</strong> (<em>boolean</em>) – reset user password</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Request Headers</dt>
 | 
			
		||||
<dt class="field-odd">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes</dt>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – <ul>
 | 
			
		||||
<li><p>invalid payload</p></li>
 | 
			
		||||
<li><p>valid email must be provided</p></li>
 | 
			
		||||
<li><p>new email must be different than curent email</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
<li><p>provide a valid auth token</p></li>
 | 
			
		||||
<li><p>signature expired, please log in again</p></li>
 | 
			
		||||
@@ -552,17 +579,17 @@ one admin</p>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>user_name</strong> (<em>string</em>) – user name</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers</dt>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.5">204 No Content</a></span> – user account deleted</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -601,7 +628,7 @@ one admin</p>
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        © Copyright 2018 - 2022, SamR1.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 4.4.0.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.0.2.<br/>
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</footer>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
 | 
			
		||||
    <title>Workouts — FitTrackee 0.5.7
 | 
			
		||||
    <title>Workouts — FitTrackee 0.6.10
 | 
			
		||||
 documentation</title>
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
    <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
 | 
			
		||||
    <script src="../_static/jquery.js"></script>
 | 
			
		||||
    <script src="../_static/underscore.js"></script>
 | 
			
		||||
    <script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
 | 
			
		||||
    <script src="../_static/doctools.js"></script>
 | 
			
		||||
    <link rel="index" title="Index" href="../genindex.html" />
 | 
			
		||||
    <link rel="search" title="Search" href="../search.html" />
 | 
			
		||||
@@ -40,7 +41,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
        <a class="navbar-brand" href="../index.html">
 | 
			
		||||
          FitTrackee</a>
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.5.7
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.6.10
 | 
			
		||||
</b></span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@@ -61,6 +62,7 @@
 | 
			
		||||
      aria-labelledby="dLabelGlobalToc"><ul class="current">
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../features.html">Features</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../installation.html">Installation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../cli.html">Command line interface</a></li>
 | 
			
		||||
<li class="toctree-l1 current"><a class="reference internal" href="index.html">API documentation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../troubleshooting/index.html">Troubleshooting</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../changelog.html">Change log</a></li>
 | 
			
		||||
@@ -126,7 +128,7 @@
 | 
			
		||||
    <div class="body col-md-12 content" role="main">
 | 
			
		||||
      
 | 
			
		||||
  <section id="workouts">
 | 
			
		||||
<h1>Workouts<a class="headerlink" href="#workouts" title="Permalink to this headline">¶</a></h1>
 | 
			
		||||
<h1>Workouts<a class="headerlink" href="#workouts" title="Permalink to this heading">¶</a></h1>
 | 
			
		||||
<dl class="http get">
 | 
			
		||||
<dt class="sig sig-object http" id="get--api-workouts">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">GET</span> </span><span class="sig-name descname"><span class="pre">/api/workouts</span></span><a class="headerlink" href="#get--api-workouts" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
@@ -241,7 +243,7 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Query Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Query Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>page</strong> (<em>integer</em>) – page if using pagination (default: 1)</p></li>
 | 
			
		||||
<li><p><strong>per_page</strong> (<em>integer</em>) – number of workouts per page
 | 
			
		||||
@@ -260,12 +262,12 @@
 | 
			
		||||
<li><p><strong>order</strong> (<em>string</em>) – sorting order (default: <code class="docutils literal notranslate"><span class="pre">desc</span></code>)</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers</dt>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -283,7 +285,7 @@
 | 
			
		||||
<dl class="http get">
 | 
			
		||||
<dt class="sig sig-object http" id="get--api-workouts-(string-workout_short_id)">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">GET</span> </span><span class="sig-name descname"><span class="pre">/api/workouts/</span></span><span class="sig-paren">(</span><em class="property"><span class="pre">string:</span> </em><em class="sig-param"><span class="pre">workout_short_id</span></em><span class="sig-paren">)</span><a class="headerlink" href="#get--api-workouts-(string-workout_short_id)" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>Get an workout</p>
 | 
			
		||||
<dd><p>Get a workout</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">GET</span> <span class="nn">/api/workouts/kjxavSTUrJvoAh2wvCeGEF</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
@@ -348,17 +350,17 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>workout_short_id</strong> (<em>string</em>) – workout short id</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers</dt>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -377,7 +379,7 @@
 | 
			
		||||
<dl class="http get">
 | 
			
		||||
<dt class="sig sig-object http" id="get--api-workouts-(string-workout_short_id)-gpx">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">GET</span> </span><span class="sig-name descname"><span class="pre">/api/workouts/</span></span><span class="sig-paren">(</span><em class="property"><span class="pre">string:</span> </em><em class="sig-param"><span class="pre">workout_short_id</span></em><span class="sig-paren">)</span><span class="sig-name descname"><span class="pre">/gpx</span></span><a class="headerlink" href="#get--api-workouts-(string-workout_short_id)-gpx" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>Get gpx file for an workout displayed on map with Leaflet</p>
 | 
			
		||||
<dd><p>Get gpx file for a workout displayed on map with Leaflet</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">GET</span> <span class="nn">/api/workouts/kjxavSTUrJvoAh2wvCeGEF/gpx</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
@@ -397,17 +399,17 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>workout_short_id</strong> (<em>string</em>) – workout short id</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers</dt>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -430,7 +432,7 @@
 | 
			
		||||
<dl class="http get">
 | 
			
		||||
<dt class="sig sig-object http" id="get--api-workouts-(string-workout_short_id)-chart_data">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">GET</span> </span><span class="sig-name descname"><span class="pre">/api/workouts/</span></span><span class="sig-paren">(</span><em class="property"><span class="pre">string:</span> </em><em class="sig-param"><span class="pre">workout_short_id</span></em><span class="sig-paren">)</span><span class="sig-name descname"><span class="pre">/chart_data</span></span><a class="headerlink" href="#get--api-workouts-(string-workout_short_id)-chart_data" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>Get chart data from an workout gpx file, to display it with Recharts</p>
 | 
			
		||||
<dd><p>Get chart data from a workout gpx file, to display it with Recharts</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">GET</span> <span class="nn">/api/workouts/kjxavSTUrJvoAh2wvCeGEF/chart</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
@@ -469,17 +471,17 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>workout_short_id</strong> (<em>string</em>) – workout short id</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers</dt>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -502,7 +504,7 @@
 | 
			
		||||
<dl class="http get">
 | 
			
		||||
<dt class="sig sig-object http" id="get--api-workouts-(string-workout_short_id)-chart_data-segment-(int-segment_id)">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">GET</span> </span><span class="sig-name descname"><span class="pre">/api/workouts/</span></span><span class="sig-paren">(</span><em class="property"><span class="pre">string:</span> </em><em class="sig-param"><span class="pre">workout_short_id</span></em><span class="sig-paren">)</span><span class="sig-name descname"><span class="pre">/chart_data/segment/</span></span><span class="sig-paren">(</span><em class="property"><span class="pre">int:</span> </em><em class="sig-param"><span class="pre">segment_id</span></em><span class="sig-paren">)</span><a class="headerlink" href="#get--api-workouts-(string-workout_short_id)-chart_data-segment-(int-segment_id)" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>Get chart data from an workout gpx file, to display it with Recharts</p>
 | 
			
		||||
<dd><p>Get chart data from a workout gpx file, to display it with Recharts</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">GET</span> <span class="nn">/api/workouts/kjxavSTUrJvoAh2wvCeGEF/chart/segment/0</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
@@ -541,18 +543,18 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>workout_short_id</strong> (<em>string</em>) – workout short id</p></li>
 | 
			
		||||
<li><p><strong>segment_id</strong> (<em>integer</em>) – segment id</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers</dt>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – no gpx file for this workout</p></li>
 | 
			
		||||
@@ -572,7 +574,7 @@
 | 
			
		||||
<dl class="http get">
 | 
			
		||||
<dt class="sig sig-object http" id="get--api-workouts-(string-workout_short_id)-gpx-segment-(int-segment_id)">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">GET</span> </span><span class="sig-name descname"><span class="pre">/api/workouts/</span></span><span class="sig-paren">(</span><em class="property"><span class="pre">string:</span> </em><em class="sig-param"><span class="pre">workout_short_id</span></em><span class="sig-paren">)</span><span class="sig-name descname"><span class="pre">/gpx/segment/</span></span><span class="sig-paren">(</span><em class="property"><span class="pre">int:</span> </em><em class="sig-param"><span class="pre">segment_id</span></em><span class="sig-paren">)</span><a class="headerlink" href="#get--api-workouts-(string-workout_short_id)-gpx-segment-(int-segment_id)" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>Get gpx file for an workout segment displayed on map with Leaflet</p>
 | 
			
		||||
<dd><p>Get gpx file for a workout segment displayed on map with Leaflet</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">GET</span> <span class="nn">/api/workouts/kjxavSTUrJvoAh2wvCeGEF/gpx/segment/0</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
@@ -592,18 +594,18 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>workout_short_id</strong> (<em>string</em>) – workout short id</p></li>
 | 
			
		||||
<li><p><strong>segment_id</strong> (<em>integer</em>) – segment id</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers</dt>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – no gpx file for this workout</p></li>
 | 
			
		||||
@@ -635,12 +637,12 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>map_id</strong> (<em>string</em>) – workout map id</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes</dt>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -670,7 +672,7 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>s</strong> (<em>string</em>) – subdomain</p></li>
 | 
			
		||||
<li><p><strong>z</strong> (<em>string</em>) – zoom</p></li>
 | 
			
		||||
@@ -696,12 +698,12 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>workout_short_id</strong> (<em>string</em>) – workout short id</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes</dt>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – success</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -723,7 +725,7 @@
 | 
			
		||||
<dl class="http post">
 | 
			
		||||
<dt class="sig sig-object http" id="post--api-workouts">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">POST</span> </span><span class="sig-name descname"><span class="pre">/api/workouts</span></span><a class="headerlink" href="#post--api-workouts" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>Post an workout with a gpx file</p>
 | 
			
		||||
<dd><p>Post a workout with a gpx file</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">POST</span> <span class="nn">/api/workouts/</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">multipart/form-data</span>
 | 
			
		||||
@@ -809,18 +811,18 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Form Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Form Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>file</strong> – gpx file (allowed extensions: .gpx, .zip)</p></li>
 | 
			
		||||
<li><p><strong>data</strong> – sport id and notes (example: <code class="docutils literal notranslate"><span class="pre">{"sport_id":</span> <span class="pre">1,</span> <span class="pre">"notes":</span> <span class="pre">""}</span></code>)</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers</dt>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.2">201 Created</a></span> – workout created</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – <ul>
 | 
			
		||||
@@ -846,7 +848,7 @@
 | 
			
		||||
<dl class="http post">
 | 
			
		||||
<dt class="sig sig-object http" id="post--api-workouts-no_gpx">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">POST</span> </span><span class="sig-name descname"><span class="pre">/api/workouts/no_gpx</span></span><a class="headerlink" href="#post--api-workouts-no_gpx" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>Post an workout without gpx file</p>
 | 
			
		||||
<dd><p>Post a workout without gpx file</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">POST</span> <span class="nn">/api/workouts/no_gpx</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
@@ -932,9 +934,10 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Request JSON Object</dt>
 | 
			
		||||
<dt class="field-odd">Request JSON Object<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>workout_date</strong> (<em>string</em>) – workout date  (format: <code class="docutils literal notranslate"><span class="pre">%Y-%m-%d</span> <span class="pre">%H:%M</span></code>)</p></li>
 | 
			
		||||
<li><p><strong>workout_date</strong> (<em>string</em>) – workout date, in user timezone
 | 
			
		||||
(format: <code class="docutils literal notranslate"><span class="pre">%Y-%m-%d</span> <span class="pre">%H:%M</span></code>)</p></li>
 | 
			
		||||
<li><p><strong>distance</strong> (<em>float</em>) – workout distance in km</p></li>
 | 
			
		||||
<li><p><strong>duration</strong> (<em>integer</em>) – workout duration in seconds</p></li>
 | 
			
		||||
<li><p><strong>notes</strong> (<em>string</em>) – notes (not mandatory)</p></li>
 | 
			
		||||
@@ -942,12 +945,12 @@
 | 
			
		||||
<li><p><strong>title</strong> (<em>string</em>) – workout title</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers</dt>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.2">201 Created</a></span> – workout created</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – invalid payload</p></li>
 | 
			
		||||
@@ -966,7 +969,7 @@
 | 
			
		||||
<dl class="http patch">
 | 
			
		||||
<dt class="sig sig-object http" id="patch--api-workouts-(string-workout_short_id)">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">PATCH</span> </span><span class="sig-name descname"><span class="pre">/api/workouts/</span></span><span class="sig-paren">(</span><em class="property"><span class="pre">string:</span> </em><em class="sig-param"><span class="pre">workout_short_id</span></em><span class="sig-paren">)</span><a class="headerlink" href="#patch--api-workouts-(string-workout_short_id)" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>Update an workout</p>
 | 
			
		||||
<dd><p>Update a workout</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">PATCH</span> <span class="nn">/api/workouts/1</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
@@ -1052,14 +1055,15 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>workout_short_id</strong> (<em>string</em>) – workout short id</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request JSON Object</dt>
 | 
			
		||||
<dt class="field-even">Request JSON Object<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><strong>workout_date</strong> (<em>string</em>) – workout date  (format: <code class="docutils literal notranslate"><span class="pre">%Y-%m-%d</span> <span class="pre">%H:%M</span></code>)
 | 
			
		||||
<li><p><strong>workout_date</strong> (<em>string</em>) – workout date in user timezone
 | 
			
		||||
(format: <code class="docutils literal notranslate"><span class="pre">%Y-%m-%d</span> <span class="pre">%H:%M</span></code>)
 | 
			
		||||
(only for workout without gpx)</p></li>
 | 
			
		||||
<li><p><strong>distance</strong> (<em>float</em>) – workout distance in km
 | 
			
		||||
(only for workout without gpx)</p></li>
 | 
			
		||||
@@ -1070,12 +1074,12 @@
 | 
			
		||||
<li><p><strong>title</strong> (<em>string</em>) – workout title</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Request Headers</dt>
 | 
			
		||||
<dt class="field-odd">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Status Codes</dt>
 | 
			
		||||
<dt class="field-even">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.1">200 OK</a></span> – workout updated</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1">400 Bad Request</a></span> – invalid payload</p></li>
 | 
			
		||||
@@ -1095,7 +1099,7 @@
 | 
			
		||||
<dl class="http delete">
 | 
			
		||||
<dt class="sig sig-object http" id="delete--api-workouts-(string-workout_short_id)">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">DELETE</span> </span><span class="sig-name descname"><span class="pre">/api/workouts/</span></span><span class="sig-paren">(</span><em class="property"><span class="pre">string:</span> </em><em class="sig-param"><span class="pre">workout_short_id</span></em><span class="sig-paren">)</span><a class="headerlink" href="#delete--api-workouts-(string-workout_short_id)" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>Delete an workout</p>
 | 
			
		||||
<dd><p>Delete a workout</p>
 | 
			
		||||
<p><strong>Example request</strong>:</p>
 | 
			
		||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">DELETE</span> <span class="nn">/api/workouts/kjxavSTUrJvoAh2wvCeGEF</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
 | 
			
		||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
 | 
			
		||||
@@ -1107,17 +1111,17 @@
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Parameters</dt>
 | 
			
		||||
<dt class="field-odd">Parameters<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><strong>workout_short_id</strong> (<em>string</em>) – workout short id</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-even">Request Headers</dt>
 | 
			
		||||
<dt class="field-even">Request Headers<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-even"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://tools.ietf.org/html/rfc7235#section-4.2">Authorization</a></span> – OAuth 2.0 Bearer Token</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
<dt class="field-odd">Status Codes</dt>
 | 
			
		||||
<dt class="field-odd">Status Codes<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><ul class="simple">
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.5">204 No Content</a></span> – workout deleted</p></li>
 | 
			
		||||
<li><p><span><a class="reference external" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">401 Unauthorized</a></span> – <ul>
 | 
			
		||||
@@ -1148,7 +1152,7 @@
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        © Copyright 2018 - 2022, SamR1.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 4.4.0.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.0.2.<br/>
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</footer>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										237
									
								
								docs/cli.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,237 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
 | 
			
		||||
    <title>Command line interface — FitTrackee 0.6.10
 | 
			
		||||
 documentation</title>
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="_static/custom.css" />
 | 
			
		||||
    <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
 | 
			
		||||
    <script src="_static/jquery.js"></script>
 | 
			
		||||
    <script src="_static/underscore.js"></script>
 | 
			
		||||
    <script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
 | 
			
		||||
    <script src="_static/doctools.js"></script>
 | 
			
		||||
    <link rel="index" title="Index" href="genindex.html" />
 | 
			
		||||
    <link rel="search" title="Search" href="search.html" />
 | 
			
		||||
    <link rel="next" title="API documentation" href="api/index.html" />
 | 
			
		||||
    <link rel="prev" title="Installation" href="installation.html" />
 | 
			
		||||
<meta charset='utf-8'>
 | 
			
		||||
<meta http-equiv='X-UA-Compatible' content='IE=edge,chrome=1'>
 | 
			
		||||
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1'>
 | 
			
		||||
<meta name="apple-mobile-web-app-capable" content="yes">
 | 
			
		||||
<script type="text/javascript" src="_static/js/jquery-1.12.4.min.js"></script>
 | 
			
		||||
<script type="text/javascript" src="_static/js/jquery-fix.js"></script>
 | 
			
		||||
<script type="text/javascript" src="_static/bootstrap-3.4.1/js/bootstrap.min.js"></script>
 | 
			
		||||
<script type="text/javascript" src="_static/bootstrap-sphinx.js"></script>
 | 
			
		||||
 | 
			
		||||
  </head><body>
 | 
			
		||||
 | 
			
		||||
  <div id="navbar" class="navbar navbar-default navbar-fixed-top">
 | 
			
		||||
    <div class="container">
 | 
			
		||||
      <div class="navbar-header">
 | 
			
		||||
        <!-- .btn-navbar is used as the toggle for collapsed navbar content -->
 | 
			
		||||
        <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".nav-collapse">
 | 
			
		||||
          <span class="icon-bar"></span>
 | 
			
		||||
          <span class="icon-bar"></span>
 | 
			
		||||
          <span class="icon-bar"></span>
 | 
			
		||||
        </button>
 | 
			
		||||
        <a class="navbar-brand" href="index.html">
 | 
			
		||||
          FitTrackee</a>
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.6.10
 | 
			
		||||
</b></span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
        <div class="collapse navbar-collapse nav-collapse">
 | 
			
		||||
          <ul class="nav navbar-nav">
 | 
			
		||||
            
 | 
			
		||||
                <li><a href="https://github.com/SamR1/FitTrackee">GitHub</a></li>
 | 
			
		||||
            
 | 
			
		||||
            
 | 
			
		||||
              <li class="dropdown globaltoc-container">
 | 
			
		||||
  <a role="button"
 | 
			
		||||
     id="dLabelGlobalToc"
 | 
			
		||||
     data-toggle="dropdown"
 | 
			
		||||
     data-target="#"
 | 
			
		||||
     href="index.html">Docs <b class="caret"></b></a>
 | 
			
		||||
  <ul class="dropdown-menu globaltoc"
 | 
			
		||||
      role="menu"
 | 
			
		||||
      aria-labelledby="dLabelGlobalToc"><ul class="current">
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="features.html">Features</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
 | 
			
		||||
<li class="toctree-l1 current"><a class="current reference internal" href="#">Command line interface</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="api/index.html">API documentation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="troubleshooting/index.html">Troubleshooting</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="changelog.html">Change log</a></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</ul>
 | 
			
		||||
</li>
 | 
			
		||||
              
 | 
			
		||||
                <li class="dropdown">
 | 
			
		||||
  <a role="button"
 | 
			
		||||
     id="dLabelLocalToc"
 | 
			
		||||
     data-toggle="dropdown"
 | 
			
		||||
     data-target="#"
 | 
			
		||||
     href="#">Page <b class="caret"></b></a>
 | 
			
		||||
  <ul class="dropdown-menu localtoc"
 | 
			
		||||
      role="menu"
 | 
			
		||||
      aria-labelledby="dLabelLocalToc"><ul>
 | 
			
		||||
<li><a class="reference internal" href="#">Command line interface</a><ul>
 | 
			
		||||
<li><a class="reference internal" href="#database">Database</a><ul>
 | 
			
		||||
<li><a class="reference internal" href="#ftcli-db-upgrade"><code class="docutils literal notranslate"><span class="pre">ftcli</span> <span class="pre">db</span> <span class="pre">upgrade</span></code></a></li>
 | 
			
		||||
<li><a class="reference internal" href="#ftcli-db-drop"><code class="docutils literal notranslate"><span class="pre">ftcli</span> <span class="pre">db</span> <span class="pre">drop</span></code></a></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</li>
 | 
			
		||||
<li><a class="reference internal" href="#users">Users</a><ul>
 | 
			
		||||
<li><a class="reference internal" href="#ftcli-users-update"><code class="docutils literal notranslate"><span class="pre">ftcli</span> <span class="pre">users</span> <span class="pre">update</span></code></a></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</li>
 | 
			
		||||
</ul>
 | 
			
		||||
</li>
 | 
			
		||||
</ul>
 | 
			
		||||
</ul>
 | 
			
		||||
</li>
 | 
			
		||||
              
 | 
			
		||||
            
 | 
			
		||||
            
 | 
			
		||||
              
 | 
			
		||||
                
 | 
			
		||||
  <li>
 | 
			
		||||
    <a href="installation.html" title="Previous Chapter: Installation"><span class="glyphicon glyphicon-chevron-left visible-sm"></span><span class="hidden-sm hidden-tablet">« Installation</span>
 | 
			
		||||
    </a>
 | 
			
		||||
  </li>
 | 
			
		||||
  <li>
 | 
			
		||||
    <a href="api/index.html" title="Next Chapter: API documentation"><span class="glyphicon glyphicon-chevron-right visible-sm"></span><span class="hidden-sm hidden-tablet">API documentation »</span>
 | 
			
		||||
    </a>
 | 
			
		||||
  </li>
 | 
			
		||||
              
 | 
			
		||||
            
 | 
			
		||||
            
 | 
			
		||||
            
 | 
			
		||||
            
 | 
			
		||||
              <li class="hidden-sm">
 | 
			
		||||
<div id="sourcelink">
 | 
			
		||||
  <a href="_sources/cli.rst.txt"
 | 
			
		||||
     rel="nofollow">Source</a>
 | 
			
		||||
</div></li>
 | 
			
		||||
            
 | 
			
		||||
          </ul>
 | 
			
		||||
 | 
			
		||||
          
 | 
			
		||||
            
 | 
			
		||||
<form class="navbar-form navbar-right" action="search.html" method="get">
 | 
			
		||||
 <div class="form-group">
 | 
			
		||||
  <input type="text" name="q" class="form-control" placeholder="Search" />
 | 
			
		||||
 </div>
 | 
			
		||||
  <input type="hidden" name="check_keywords" value="yes" />
 | 
			
		||||
  <input type="hidden" name="area" value="default" />
 | 
			
		||||
</form>
 | 
			
		||||
          
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
<div class="container">
 | 
			
		||||
  <div class="row">
 | 
			
		||||
    <div class="body col-md-12 content" role="main">
 | 
			
		||||
      
 | 
			
		||||
  <section id="command-line-interface">
 | 
			
		||||
<h1>Command line interface<a class="headerlink" href="#command-line-interface" title="Permalink to this heading">¶</a></h1>
 | 
			
		||||
<p>A command line interface (CLI) is available to manage database and users.</p>
 | 
			
		||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ ftcli
 | 
			
		||||
Usage: ftcli <span class="o">[</span>OPTIONS<span class="o">]</span> COMMAND <span class="o">[</span>ARGS<span class="o">]</span>...
 | 
			
		||||
 | 
			
		||||
  FitTrackee Command Line Interface
 | 
			
		||||
 | 
			
		||||
Options:
 | 
			
		||||
  --help  Show this message and exit.
 | 
			
		||||
 | 
			
		||||
Commands:
 | 
			
		||||
  db     Manage database.
 | 
			
		||||
  users  Manage users.
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="admonition warning">
 | 
			
		||||
<p class="admonition-title">Warning</p>
 | 
			
		||||
<div class="line-block">
 | 
			
		||||
<div class="line">The following commands are now deprecated and will be removed in a next version:</div>
 | 
			
		||||
<div class="line">- <code class="docutils literal notranslate"><span class="pre">fittrackee_set_admin</span></code></div>
 | 
			
		||||
<div class="line">- <code class="docutils literal notranslate"><span class="pre">fittrackee_upgrade_db</span></code></div>
 | 
			
		||||
</div>
 | 
			
		||||
</div>
 | 
			
		||||
<section id="database">
 | 
			
		||||
<h2>Database<a class="headerlink" href="#database" title="Permalink to this heading">¶</a></h2>
 | 
			
		||||
<section id="ftcli-db-upgrade">
 | 
			
		||||
<h3><code class="docutils literal notranslate"><span class="pre">ftcli</span> <span class="pre">db</span> <span class="pre">upgrade</span></code><a class="headerlink" href="#ftcli-db-upgrade" title="Permalink to this heading">¶</a></h3>
 | 
			
		||||
<div class="versionadded">
 | 
			
		||||
<p><span class="versionmodified added">New in version 0.6.5.</span></p>
 | 
			
		||||
</div>
 | 
			
		||||
<p>Apply migrations.</p>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="ftcli-db-drop">
 | 
			
		||||
<h3><code class="docutils literal notranslate"><span class="pre">ftcli</span> <span class="pre">db</span> <span class="pre">drop</span></code><a class="headerlink" href="#ftcli-db-drop" title="Permalink to this heading">¶</a></h3>
 | 
			
		||||
<div class="versionadded">
 | 
			
		||||
<p><span class="versionmodified added">New in version 0.6.5.</span></p>
 | 
			
		||||
</div>
 | 
			
		||||
<p>Empty database and delete uploaded files, only on development environments.</p>
 | 
			
		||||
</section>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="users">
 | 
			
		||||
<h2>Users<a class="headerlink" href="#users" title="Permalink to this heading">¶</a></h2>
 | 
			
		||||
<section id="ftcli-users-update">
 | 
			
		||||
<h3><code class="docutils literal notranslate"><span class="pre">ftcli</span> <span class="pre">users</span> <span class="pre">update</span></code><a class="headerlink" href="#ftcli-users-update" title="Permalink to this heading">¶</a></h3>
 | 
			
		||||
<div class="versionadded">
 | 
			
		||||
<p><span class="versionmodified added">New in version 0.6.5.</span></p>
 | 
			
		||||
</div>
 | 
			
		||||
<p>Modify a user account (admin rights, active status, email and password).</p>
 | 
			
		||||
<table class="table-bordered docutils align-default">
 | 
			
		||||
<colgroup>
 | 
			
		||||
<col style="width: 33%" />
 | 
			
		||||
<col style="width: 67%" />
 | 
			
		||||
</colgroup>
 | 
			
		||||
<thead>
 | 
			
		||||
<tr class="row-odd"><th class="head"><p>Options</p></th>
 | 
			
		||||
<th class="head"><p>Description</p></th>
 | 
			
		||||
</tr>
 | 
			
		||||
</thead>
 | 
			
		||||
<tbody>
 | 
			
		||||
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">--set-admin</span> <span class="pre">BOOLEAN</span></code></p></td>
 | 
			
		||||
<td><p>Add/remove admin rights (when adding admin rights, it also activates user account if not active).</p></td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">--activate</span></code></p></td>
 | 
			
		||||
<td><p>Activate user account.</p></td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr class="row-even"><td><p><code class="docutils literal notranslate"><span class="pre">--reset-password</span></code></p></td>
 | 
			
		||||
<td><p>Reset user password (a new password will be displayed).</p></td>
 | 
			
		||||
</tr>
 | 
			
		||||
<tr class="row-odd"><td><p><code class="docutils literal notranslate"><span class="pre">--update-email</span> <span class="pre">EMAIL</span></code></p></td>
 | 
			
		||||
<td><p>Update user email.</p></td>
 | 
			
		||||
</tr>
 | 
			
		||||
</tbody>
 | 
			
		||||
</table>
 | 
			
		||||
</section>
 | 
			
		||||
</section>
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
      
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
<footer class="footer">
 | 
			
		||||
  <div class="container">
 | 
			
		||||
    <p class="pull-right">
 | 
			
		||||
      <a href="#">Back to top</a>
 | 
			
		||||
      
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        © Copyright 2018 - 2022, SamR1.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.0.2.<br/>
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</footer>
 | 
			
		||||
  </body>
 | 
			
		||||
</html>
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
 | 
			
		||||
    <title>Features — FitTrackee 0.5.7
 | 
			
		||||
    <title>Features — FitTrackee 0.6.10
 | 
			
		||||
 documentation</title>
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
    <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
 | 
			
		||||
    <script src="_static/jquery.js"></script>
 | 
			
		||||
    <script src="_static/underscore.js"></script>
 | 
			
		||||
    <script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
 | 
			
		||||
    <script src="_static/doctools.js"></script>
 | 
			
		||||
    <link rel="index" title="Index" href="genindex.html" />
 | 
			
		||||
    <link rel="search" title="Search" href="search.html" />
 | 
			
		||||
@@ -40,7 +41,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
        <a class="navbar-brand" href="index.html">
 | 
			
		||||
          FitTrackee</a>
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.5.7
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.6.10
 | 
			
		||||
</b></span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@@ -61,6 +62,7 @@
 | 
			
		||||
      aria-labelledby="dLabelGlobalToc"><ul class="current">
 | 
			
		||||
<li class="toctree-l1 current"><a class="current reference internal" href="#">Features</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="cli.html">Command line interface</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="api/index.html">API documentation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="troubleshooting/index.html">Troubleshooting</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="changelog.html">Change log</a></li>
 | 
			
		||||
@@ -140,9 +142,9 @@
 | 
			
		||||
    <div class="body col-md-12 content" role="main">
 | 
			
		||||
      
 | 
			
		||||
  <section id="features">
 | 
			
		||||
<h1>Features<a class="headerlink" href="#features" title="Permalink to this headline">¶</a></h1>
 | 
			
		||||
<h1>Features<a class="headerlink" href="#features" title="Permalink to this heading">¶</a></h1>
 | 
			
		||||
<section id="workouts">
 | 
			
		||||
<h2>Workouts<a class="headerlink" href="#workouts" title="Permalink to this headline">¶</a></h2>
 | 
			
		||||
<h2>Workouts<a class="headerlink" href="#workouts" title="Permalink to this heading">¶</a></h2>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><dl class="simple">
 | 
			
		||||
<dt>11 sports are supported:</dt><dd><ul>
 | 
			
		||||
@@ -209,13 +211,19 @@
 | 
			
		||||
<li><dl class="simple">
 | 
			
		||||
<dt>User records by sports:</dt><dd><ul class="simple">
 | 
			
		||||
<li><p>average speed</p></li>
 | 
			
		||||
<li><p>farest distance</p></li>
 | 
			
		||||
<li><p>farthest distance</p></li>
 | 
			
		||||
<li><p>longest duration</p></li>
 | 
			
		||||
<li><p>maximum speed</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
</li>
 | 
			
		||||
</ul>
 | 
			
		||||
<div class="admonition note">
 | 
			
		||||
<p class="admonition-title">Note</p>
 | 
			
		||||
<p>Records may differ from records displayed by the application that originally generated the gpx files.</p>
 | 
			
		||||
</div>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>Workouts list and filter. Only sports with workouts are displayed in sport dropdown.</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<div class="admonition note">
 | 
			
		||||
@@ -224,11 +232,21 @@
 | 
			
		||||
</div>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="account-preferences">
 | 
			
		||||
<h2>Account & preferences<a class="headerlink" href="#account-preferences" title="Permalink to this headline">¶</a></h2>
 | 
			
		||||
<h2>Account & preferences<a class="headerlink" href="#account-preferences" title="Permalink to this heading">¶</a></h2>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>A user can create, update and deleted his account.</p></li>
 | 
			
		||||
<li><p>On registration, the user account is created with selected language in dropdown as user preference (<em>new in 0.6.9</em>).</p></li>
 | 
			
		||||
<li><p>After registration, the user account is inactive and an email with confirmation instructions is sent to activate it.
 | 
			
		||||
A user with an inactive account cannot log in. (<em>new in 0.6.0</em>)</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<div class="admonition note">
 | 
			
		||||
<p class="admonition-title">Note</p>
 | 
			
		||||
<p>In case email sending is not configured, a <a class="reference external" href="cli.html#ftcli-users-update">command line</a> allows to activate users account.</p>
 | 
			
		||||
</div>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>A user can create, update and deleted his account</p></li>
 | 
			
		||||
<li><p>A user can set language, timezone and first day of week.</p></li>
 | 
			
		||||
<li><p>A user can reset his password (<em>new in 0.3.0</em>)</p></li>
 | 
			
		||||
<li><p>A user can change his email address (<em>new in 0.6.0</em>)</p></li>
 | 
			
		||||
<li><p>A user can choose between metric system and imperial system for distance, elevation and speed display (<em>new in 0.5.0</em>)</p></li>
 | 
			
		||||
<li><dl class="simple">
 | 
			
		||||
<dt>A user can set sport preferences (<em>new in 0.5.0</em>):</dt><dd><ul>
 | 
			
		||||
@@ -250,7 +268,7 @@
 | 
			
		||||
</div>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="administration">
 | 
			
		||||
<h2>Administration<a class="headerlink" href="#administration" title="Permalink to this headline">¶</a></h2>
 | 
			
		||||
<h2>Administration<a class="headerlink" href="#administration" title="Permalink to this heading">¶</a></h2>
 | 
			
		||||
<p>(<em>new in 0.3.0</em>)</p>
 | 
			
		||||
<ul>
 | 
			
		||||
<li><p><strong>Application</strong></p>
 | 
			
		||||
@@ -260,16 +278,30 @@
 | 
			
		||||
<li><p>maximum size of uploaded files</p></li>
 | 
			
		||||
<li><p>maximum size of zip archive</p></li>
 | 
			
		||||
<li><p>maximum number of files in the zip archive. If an archive contains more files, only the configured number of files is processed, without raising errors.</p></li>
 | 
			
		||||
<li><p>administrator email for contact (<em>new in 0.6.0</em>)</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<div class="admonition warning">
 | 
			
		||||
<p class="admonition-title">Warning</p>
 | 
			
		||||
<p>Updating server configuration may be necessary to handle large files (like <a class="reference external" href="https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size">nginx</a> for instance).</p>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="admonition note">
 | 
			
		||||
<p class="admonition-title">Note</p>
 | 
			
		||||
<p>If email sending is disabled, a warning is displayed.</p>
 | 
			
		||||
</div>
 | 
			
		||||
</li>
 | 
			
		||||
<li><p><strong>Users</strong></p>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>display users list and details</p></li>
 | 
			
		||||
<li><p>edit a user to add/remove administration rights</p></li>
 | 
			
		||||
<li><p>display and filter users list</p></li>
 | 
			
		||||
<li><dl class="simple">
 | 
			
		||||
<dt>edit a user to:</dt><dd><ul>
 | 
			
		||||
<li><p>add/remove administration rights</p></li>
 | 
			
		||||
<li><p>activate his account (<em>new in 0.6.0</em>)</p></li>
 | 
			
		||||
<li><p>update his email (in case his account is locked) (<em>new in 0.6.0</em>)</p></li>
 | 
			
		||||
<li><p>reset his password (in case his account is locked) (<em>new in 0.6.0</em>). If email sending is disabled, it is only possible via CLI.</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
</li>
 | 
			
		||||
<li><p>delete a user</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</li>
 | 
			
		||||
@@ -281,37 +313,39 @@
 | 
			
		||||
</ul>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="translations">
 | 
			
		||||
<h2>Translations<a class="headerlink" href="#translations" title="Permalink to this headline">¶</a></h2>
 | 
			
		||||
<p>FitTrackee is available in English and French (which can be saved in the user preferences).</p>
 | 
			
		||||
<h2>Translations<a class="headerlink" href="#translations" title="Permalink to this heading">¶</a></h2>
 | 
			
		||||
<p>FitTrackee is available in the following languages (which can be saved in the user preferences):</p>
 | 
			
		||||
<figure class="align-default">
 | 
			
		||||
<img alt="https://hosted.weblate.org/widgets/fittrackee/-/multi-auto.svg" src="https://hosted.weblate.org/widgets/fittrackee/-/multi-auto.svg" /></figure>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="screenshots">
 | 
			
		||||
<h2>Screenshots<a class="headerlink" href="#screenshots" title="Permalink to this headline">¶</a></h2>
 | 
			
		||||
<h2>Screenshots<a class="headerlink" href="#screenshots" title="Permalink to this heading">¶</a></h2>
 | 
			
		||||
<section id="dashboard">
 | 
			
		||||
<h3>Dashboard<a class="headerlink" href="#dashboard" title="Permalink to this headline">¶</a></h3>
 | 
			
		||||
<h3>Dashboard<a class="headerlink" href="#dashboard" title="Permalink to this heading">¶</a></h3>
 | 
			
		||||
<figure class="align-default">
 | 
			
		||||
<img alt="FitTrackee Dashboard" src="_images/fittrackee_screenshot-01.png" />
 | 
			
		||||
</figure>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="workout-detail">
 | 
			
		||||
<h3>Workout detail<a class="headerlink" href="#workout-detail" title="Permalink to this headline">¶</a></h3>
 | 
			
		||||
<h3>Workout detail<a class="headerlink" href="#workout-detail" title="Permalink to this heading">¶</a></h3>
 | 
			
		||||
<figure class="align-default">
 | 
			
		||||
<img alt="FitTrackee Workout" src="_images/fittrackee_screenshot-02.png" />
 | 
			
		||||
</figure>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="workouts-list">
 | 
			
		||||
<h3>Workouts list<a class="headerlink" href="#workouts-list" title="Permalink to this headline">¶</a></h3>
 | 
			
		||||
<h3>Workouts list<a class="headerlink" href="#workouts-list" title="Permalink to this heading">¶</a></h3>
 | 
			
		||||
<figure class="align-default">
 | 
			
		||||
<img alt="FitTrackee Workouts" src="_images/fittrackee_screenshot-03.png" />
 | 
			
		||||
</figure>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="statistics">
 | 
			
		||||
<h3>Statistics<a class="headerlink" href="#statistics" title="Permalink to this headline">¶</a></h3>
 | 
			
		||||
<h3>Statistics<a class="headerlink" href="#statistics" title="Permalink to this heading">¶</a></h3>
 | 
			
		||||
<figure class="align-default">
 | 
			
		||||
<img alt="FitTrackee Statistics" src="_images/fittrackee_screenshot-04.png" />
 | 
			
		||||
</figure>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="id1">
 | 
			
		||||
<h3>Administration<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h3>
 | 
			
		||||
<h3>Administration<a class="headerlink" href="#id1" title="Permalink to this heading">¶</a></h3>
 | 
			
		||||
<figure class="align-default">
 | 
			
		||||
<img alt="FitTrackee Administration" src="_images/fittrackee_screenshot-05.png" />
 | 
			
		||||
</figure>
 | 
			
		||||
@@ -335,7 +369,7 @@
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        © Copyright 2018 - 2022, SamR1.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 4.4.0.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.0.2.<br/>
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</footer>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <title>Index — FitTrackee 0.5.7
 | 
			
		||||
    <title>Index — FitTrackee 0.6.10
 | 
			
		||||
 documentation</title>
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
 | 
			
		||||
@@ -12,6 +12,7 @@
 | 
			
		||||
    <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
 | 
			
		||||
    <script src="_static/jquery.js"></script>
 | 
			
		||||
    <script src="_static/underscore.js"></script>
 | 
			
		||||
    <script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
 | 
			
		||||
    <script src="_static/doctools.js"></script>
 | 
			
		||||
    <link rel="index" title="Index" href="#" />
 | 
			
		||||
    <link rel="search" title="Search" href="search.html" />
 | 
			
		||||
@@ -37,7 +38,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
        <a class="navbar-brand" href="index.html">
 | 
			
		||||
          FitTrackee</a>
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.5.7
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.6.10
 | 
			
		||||
</b></span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@@ -58,6 +59,7 @@
 | 
			
		||||
      aria-labelledby="dLabelGlobalToc"><ul>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="features.html">Features</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="cli.html">Command line interface</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="api/index.html">API documentation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="troubleshooting/index.html">Troubleshooting</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="changelog.html">Change log</a></li>
 | 
			
		||||
@@ -133,7 +135,7 @@
 | 
			
		||||
</li>
 | 
			
		||||
        <li><a href="installation.html#envvar-DATABASE_URL">DATABASE_URL</a>
 | 
			
		||||
</li>
 | 
			
		||||
        <li><a href="installation.html#envvar-DEFAULT_STATICMAP">DEFAULT_STATICMAP 🆕</a>
 | 
			
		||||
        <li><a href="installation.html#envvar-DEFAULT_STATICMAP">DEFAULT_STATICMAP</a>
 | 
			
		||||
</li>
 | 
			
		||||
        <li><a href="installation.html#envvar-EMAIL_URL">EMAIL_URL</a>
 | 
			
		||||
</li>
 | 
			
		||||
@@ -148,6 +150,8 @@
 | 
			
		||||
        <li><a href="installation.html#envvar-REDIS_URL">REDIS_URL</a>
 | 
			
		||||
</li>
 | 
			
		||||
        <li><a href="installation.html#envvar-SENDER_EMAIL">SENDER_EMAIL</a>
 | 
			
		||||
</li>
 | 
			
		||||
        <li><a href="installation.html#envvar-STATICMAP_SUBDOMAINS">STATICMAP_SUBDOMAINS 🆕</a>
 | 
			
		||||
</li>
 | 
			
		||||
        <li><a href="installation.html#envvar-TILE_SERVER_URL">TILE_SERVER_URL</a>
 | 
			
		||||
</li>
 | 
			
		||||
@@ -179,7 +183,7 @@
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        © Copyright 2018 - 2022, SamR1.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 4.4.0.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.0.2.<br/>
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</footer>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <title>HTTP Routing Table — FitTrackee 0.5.7
 | 
			
		||||
    <title>HTTP Routing Table — FitTrackee 0.6.10
 | 
			
		||||
 documentation</title>
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
 | 
			
		||||
@@ -12,6 +12,7 @@
 | 
			
		||||
    <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
 | 
			
		||||
    <script src="_static/jquery.js"></script>
 | 
			
		||||
    <script src="_static/underscore.js"></script>
 | 
			
		||||
    <script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
 | 
			
		||||
    <script src="_static/doctools.js"></script>
 | 
			
		||||
    <link rel="index" title="Index" href="genindex.html" />
 | 
			
		||||
    <link rel="search" title="Search" href="search.html" />
 | 
			
		||||
@@ -44,7 +45,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
        <a class="navbar-brand" href="index.html">
 | 
			
		||||
          FitTrackee</a>
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.5.7
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.6.10
 | 
			
		||||
</b></span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@@ -65,6 +66,7 @@
 | 
			
		||||
      aria-labelledby="dLabelGlobalToc"><ul>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="features.html">Features</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="cli.html">Command line interface</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="api/index.html">API documentation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="troubleshooting/index.html">Troubleshooting</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="changelog.html">Change log</a></li>
 | 
			
		||||
@@ -125,11 +127,6 @@
 | 
			
		||||
     <tr class="pcap"><td></td><td> </td><td></td></tr>
 | 
			
		||||
     <tr class="cap" id="cap-/api"><td></td><td>
 | 
			
		||||
       <strong>/api</strong></td><td></td></tr>
 | 
			
		||||
     <tr>
 | 
			
		||||
       <td></td>
 | 
			
		||||
       <td>
 | 
			
		||||
       <a href="api/auth.html#get--api-auth-logout"><code class="xref">GET /api/auth/logout</code></a></td><td>
 | 
			
		||||
       <em></em></td></tr>
 | 
			
		||||
     <tr>
 | 
			
		||||
       <td></td>
 | 
			
		||||
       <td>
 | 
			
		||||
@@ -235,6 +232,21 @@
 | 
			
		||||
       <td>
 | 
			
		||||
       <a href="api/workouts.html#get--api-workouts-map_tile-(s)-(z)-(x)-(y).png"><code class="xref">GET /api/workouts/map_tile/(s)/(z)/(x)/(y).png</code></a></td><td>
 | 
			
		||||
       <em></em></td></tr>
 | 
			
		||||
     <tr>
 | 
			
		||||
       <td></td>
 | 
			
		||||
       <td>
 | 
			
		||||
       <a href="api/auth.html#post--api-auth-account-confirm"><code class="xref">POST /api/auth/account/confirm</code></a></td><td>
 | 
			
		||||
       <em></em></td></tr>
 | 
			
		||||
     <tr>
 | 
			
		||||
       <td></td>
 | 
			
		||||
       <td>
 | 
			
		||||
       <a href="api/auth.html#post--api-auth-account-resend-confirmation"><code class="xref">POST /api/auth/account/resend-confirmation</code></a></td><td>
 | 
			
		||||
       <em></em></td></tr>
 | 
			
		||||
     <tr>
 | 
			
		||||
       <td></td>
 | 
			
		||||
       <td>
 | 
			
		||||
       <a href="api/auth.html#post--api-auth-email-update"><code class="xref">POST /api/auth/email/update</code></a></td><td>
 | 
			
		||||
       <em></em></td></tr>
 | 
			
		||||
     <tr>
 | 
			
		||||
       <td></td>
 | 
			
		||||
       <td>
 | 
			
		||||
@@ -305,6 +317,11 @@
 | 
			
		||||
       <td>
 | 
			
		||||
       <a href="api/workouts.html#delete--api-workouts-(string-workout_short_id)"><code class="xref">DELETE /api/workouts/(string:workout_short_id)</code></a></td><td>
 | 
			
		||||
       <em></em></td></tr>
 | 
			
		||||
     <tr>
 | 
			
		||||
       <td></td>
 | 
			
		||||
       <td>
 | 
			
		||||
       <a href="api/auth.html#patch--api-auth-profile-edit-account"><code class="xref">PATCH /api/auth/profile/edit/account</code></a></td><td>
 | 
			
		||||
       <em></em></td></tr>
 | 
			
		||||
     <tr>
 | 
			
		||||
       <td></td>
 | 
			
		||||
       <td>
 | 
			
		||||
@@ -340,7 +357,7 @@
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        © Copyright 2018 - 2022, SamR1.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 4.4.0.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.0.2.<br/>
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</footer>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
 | 
			
		||||
    <title>FitTrackee — FitTrackee 0.5.7
 | 
			
		||||
    <title>FitTrackee — FitTrackee 0.6.10
 | 
			
		||||
 documentation</title>
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
    <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
 | 
			
		||||
    <script src="_static/jquery.js"></script>
 | 
			
		||||
    <script src="_static/underscore.js"></script>
 | 
			
		||||
    <script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
 | 
			
		||||
    <script src="_static/doctools.js"></script>
 | 
			
		||||
    <link rel="index" title="Index" href="genindex.html" />
 | 
			
		||||
    <link rel="search" title="Search" href="search.html" />
 | 
			
		||||
@@ -39,7 +40,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
        <a class="navbar-brand" href="#">
 | 
			
		||||
          FitTrackee</a>
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.5.7
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.6.10
 | 
			
		||||
</b></span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@@ -60,6 +61,7 @@
 | 
			
		||||
      aria-labelledby="dLabelGlobalToc"><ul>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="features.html">Features</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="cli.html">Command line interface</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="api/index.html">API documentation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="troubleshooting/index.html">Troubleshooting</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="changelog.html">Change log</a></li>
 | 
			
		||||
@@ -122,7 +124,7 @@
 | 
			
		||||
    <div class="body col-md-12 content" role="main">
 | 
			
		||||
      
 | 
			
		||||
  <section id="fittrackee">
 | 
			
		||||
<h1>FitTrackee<a class="headerlink" href="#fittrackee" title="Permalink to this headline">¶</a></h1>
 | 
			
		||||
<h1>FitTrackee<a class="headerlink" href="#fittrackee" title="Permalink to this heading">¶</a></h1>
 | 
			
		||||
<div class="line-block">
 | 
			
		||||
<div class="line">This web application allows you to track your outdoor activities (workouts)
 | 
			
		||||
from gpx files and keep your data on your own server.</div>
 | 
			
		||||
@@ -148,11 +150,12 @@ Map</a>.</div>
 | 
			
		||||
</figure>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="table-of-contents">
 | 
			
		||||
<h1>Table of contents<a class="headerlink" href="#table-of-contents" title="Permalink to this headline">¶</a></h1>
 | 
			
		||||
<h1>Table of contents<a class="headerlink" href="#table-of-contents" title="Permalink to this heading">¶</a></h1>
 | 
			
		||||
<div class="toctree-wrapper compound">
 | 
			
		||||
<ul>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="features.html">Features</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="cli.html">Command line interface</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="api/index.html">API documentation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="troubleshooting/index.html">Troubleshooting</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="changelog.html">Change log</a></li>
 | 
			
		||||
@@ -173,7 +176,7 @@ Map</a>.</div>
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        © Copyright 2018 - 2022, SamR1.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 4.4.0.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.0.2.<br/>
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</footer>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
 | 
			
		||||
    <title>Installation — FitTrackee 0.5.7
 | 
			
		||||
    <title>Installation — FitTrackee 0.6.10
 | 
			
		||||
 documentation</title>
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
 | 
			
		||||
@@ -13,10 +13,11 @@
 | 
			
		||||
    <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
 | 
			
		||||
    <script src="_static/jquery.js"></script>
 | 
			
		||||
    <script src="_static/underscore.js"></script>
 | 
			
		||||
    <script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
 | 
			
		||||
    <script src="_static/doctools.js"></script>
 | 
			
		||||
    <link rel="index" title="Index" href="genindex.html" />
 | 
			
		||||
    <link rel="search" title="Search" href="search.html" />
 | 
			
		||||
    <link rel="next" title="API documentation" href="api/index.html" />
 | 
			
		||||
    <link rel="next" title="Command line interface" href="cli.html" />
 | 
			
		||||
    <link rel="prev" title="Features" href="features.html" />
 | 
			
		||||
<meta charset='utf-8'>
 | 
			
		||||
<meta http-equiv='X-UA-Compatible' content='IE=edge,chrome=1'>
 | 
			
		||||
@@ -40,7 +41,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
        <a class="navbar-brand" href="index.html">
 | 
			
		||||
          FitTrackee</a>
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.5.7
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.6.10
 | 
			
		||||
</b></span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@@ -61,6 +62,7 @@
 | 
			
		||||
      aria-labelledby="dLabelGlobalToc"><ul class="current">
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="features.html">Features</a></li>
 | 
			
		||||
<li class="toctree-l1 current"><a class="current reference internal" href="#">Installation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="cli.html">Command line interface</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="api/index.html">API documentation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="troubleshooting/index.html">Troubleshooting</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="changelog.html">Change log</a></li>
 | 
			
		||||
@@ -123,7 +125,7 @@
 | 
			
		||||
    </a>
 | 
			
		||||
  </li>
 | 
			
		||||
  <li>
 | 
			
		||||
    <a href="api/index.html" title="Next Chapter: API documentation"><span class="glyphicon glyphicon-chevron-right visible-sm"></span><span class="hidden-sm hidden-tablet">API documentation »</span>
 | 
			
		||||
    <a href="cli.html" title="Next Chapter: Command line interface"><span class="glyphicon glyphicon-chevron-right visible-sm"></span><span class="hidden-sm hidden-tablet">Command line ... »</span>
 | 
			
		||||
    </a>
 | 
			
		||||
  </li>
 | 
			
		||||
              
 | 
			
		||||
@@ -158,7 +160,7 @@
 | 
			
		||||
    <div class="body col-md-12 content" role="main">
 | 
			
		||||
      
 | 
			
		||||
  <section id="installation">
 | 
			
		||||
<h1>Installation<a class="headerlink" href="#installation" title="Permalink to this headline">¶</a></h1>
 | 
			
		||||
<h1>Installation<a class="headerlink" href="#installation" title="Permalink to this heading">¶</a></h1>
 | 
			
		||||
<p>This application is written in Python (API) and Typescript (client):</p>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><dl class="simple">
 | 
			
		||||
@@ -182,16 +184,18 @@
 | 
			
		||||
</dl>
 | 
			
		||||
</li>
 | 
			
		||||
</ul>
 | 
			
		||||
<p>Logo, sports and weather icons are made by <a class="reference external" href="https://www.freepik.com/">Freepik</a> from <a class="reference external" href="https://www.flaticon.com/">www.flaticon.com</a>.</p>
 | 
			
		||||
<div class="line-block">
 | 
			
		||||
<div class="line">Logo, some sports and weather icons are made by <a class="reference external" href="https://www.freepik.com/">Freepik</a> from <a class="reference external" href="https://www.flaticon.com/">www.flaticon.com</a>.</div>
 | 
			
		||||
<div class="line">FitTrackee also uses icons from <a class="reference external" href="https://forkaweso.me">Fork Awesome</a>.</div>
 | 
			
		||||
</div>
 | 
			
		||||
<section id="prerequisites">
 | 
			
		||||
<h2>Prerequisites<a class="headerlink" href="#prerequisites" title="Permalink to this headline">¶</a></h2>
 | 
			
		||||
<h2>Prerequisites<a class="headerlink" href="#prerequisites" title="Permalink to this heading">¶</a></h2>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>PostgreSQL database (10+)</p></li>
 | 
			
		||||
<li><p>Redis for task queue</p></li>
 | 
			
		||||
<li><p>Python 3.7+</p></li>
 | 
			
		||||
<li><p>PostgreSQL database (10+)</p></li>
 | 
			
		||||
<li><p>SMTP provider and Redis for task queue (if email sending is enabled)</p></li>
 | 
			
		||||
<li><p>API key from <a class="reference external" href="https://darksky.net/dev">Dark Sky</a> (not mandatory)</p></li>
 | 
			
		||||
<li><p><a class="reference external" href="https://poetry.eustace.io">Poetry</a> (for installation from sources only)</p></li>
 | 
			
		||||
<li><p>API key from <a class="reference external" href="https://darksky.net/dev">Dark Sky</a> [not mandatory]</p></li>
 | 
			
		||||
<li><p>SMTP provider</p></li>
 | 
			
		||||
<li><p><a class="reference external" href="https://yarnpkg.com">Yarn</a> (for development only)</p></li>
 | 
			
		||||
<li><p>Docker and Docker Compose (for development or evaluation purposes)</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
@@ -206,7 +210,7 @@ necessary.</div>
 | 
			
		||||
</div>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="environment-variables">
 | 
			
		||||
<h2>Environment variables<a class="headerlink" href="#environment-variables" title="Permalink to this headline">¶</a></h2>
 | 
			
		||||
<h2>Environment variables<a class="headerlink" href="#environment-variables" title="Permalink to this heading">¶</a></h2>
 | 
			
		||||
<div class="admonition warning">
 | 
			
		||||
<p class="admonition-title">Warning</p>
 | 
			
		||||
<div class="line-block">
 | 
			
		||||
@@ -230,7 +234,7 @@ deployment method.</p>
 | 
			
		||||
<span class="sig-name descname"><span class="pre">HOST</span></span><a class="headerlink" href="#envvar-HOST" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p><strong>FitTrackee</strong> host.</p>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Default</dt>
 | 
			
		||||
<dt class="field-odd">Default<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><p>0.0.0.0</p>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
@@ -241,7 +245,7 @@ deployment method.</p>
 | 
			
		||||
<span class="sig-name descname"><span class="pre">PORT</span></span><a class="headerlink" href="#envvar-PORT" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p><strong>FitTrackee</strong> port.</p>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Default</dt>
 | 
			
		||||
<dt class="field-odd">Default<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><p>5000</p>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
@@ -252,7 +256,7 @@ deployment method.</p>
 | 
			
		||||
<span class="sig-name descname"><span class="pre">APP_SETTINGS</span></span><a class="headerlink" href="#envvar-APP_SETTINGS" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p><strong>FitTrackee</strong> configuration.</p>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Default</dt>
 | 
			
		||||
<dt class="field-odd">Default<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><p>fittrackee.config.ProductionConfig</p>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
@@ -269,7 +273,7 @@ deployment method.</p>
 | 
			
		||||
<span class="sig-name descname"><span class="pre">APP_WORKERS</span></span><a class="headerlink" href="#envvar-APP_WORKERS" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><p>Number of workers spawned by <strong>Gunicorn</strong>.</p>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Default</dt>
 | 
			
		||||
<dt class="field-odd">Default<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><p>1</p>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
@@ -290,10 +294,10 @@ deployment method.</p>
 | 
			
		||||
<dd><div class="versionadded">
 | 
			
		||||
<p><span class="versionmodified added">New in version 0.4.0.</span></p>
 | 
			
		||||
</div>
 | 
			
		||||
<p>Directory containing uploaded files.</p>
 | 
			
		||||
<p><strong>Absolute path</strong> to the directory where <cite>uploads</cite> folder will be created.</p>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Default</dt>
 | 
			
		||||
<dd class="field-odd"><p><cite>fittrackee/uploads/</cite></p>
 | 
			
		||||
<dt class="field-odd">Default<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><p><cite><application_directory>/fittrackee</cite></p>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
<div class="admonition danger">
 | 
			
		||||
@@ -312,8 +316,8 @@ deployment method.</p>
 | 
			
		||||
<div class="line">Database URL with username and password, must be initialized in production environment.</div>
 | 
			
		||||
<div class="line">For example in dev environment : <code class="docutils literal notranslate"><span class="pre">postgresql://fittrackee:fittrackee@localhost:5432/fittrackee</span></code></div>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="admonition danger">
 | 
			
		||||
<p class="admonition-title">Danger</p>
 | 
			
		||||
<div class="admonition warning">
 | 
			
		||||
<p class="admonition-title">Warning</p>
 | 
			
		||||
<div class="line-block">
 | 
			
		||||
<div class="line">Since <a class="reference external" href="https://docs.sqlalchemy.org/en/14/changelog/changelog_14.html#change-3687655465c25a39b968b4f5f6e9170b">SQLAlchemy update (1.4+)</a>,
 | 
			
		||||
engine URL should begin with <cite>postgresql://</cite>.</div>
 | 
			
		||||
@@ -330,7 +334,7 @@ engine URL should begin with <cite>postgresql://</cite>.</div>
 | 
			
		||||
<p>Disable pooling if needed (when starting application with <strong>FitTrackee</strong> entry point and not directly with <strong>Gunicorn</strong>),
 | 
			
		||||
see <a class="reference external" href="https://docs.sqlalchemy.org/en/13/core/pooling.html#using-connection-pools-with-multiprocessing-or-os-fork">SqlAlchemy documentation</a>.</p>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Default</dt>
 | 
			
		||||
<dt class="field-odd">Default<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><p>false</p>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
@@ -349,6 +353,18 @@ see <a class="reference external" href="https://docs.sqlalchemy.org/en/13/core/p
 | 
			
		||||
<p><span class="versionmodified added">New in version 0.3.0.</span></p>
 | 
			
		||||
</div>
 | 
			
		||||
<p>Email URL with credentials, see <a class="reference external" href="installation.html#emails">Emails</a>.</p>
 | 
			
		||||
<div class="versionchanged">
 | 
			
		||||
<p><span class="versionmodified changed">Changed in version 0.6.5.</span></p>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Default<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><p>empty string</p>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
<div class="admonition danger">
 | 
			
		||||
<p class="admonition-title">Danger</p>
 | 
			
		||||
<p>If the email URL is empty, email sending will be disabled.</p>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="admonition warning">
 | 
			
		||||
<p class="admonition-title">Warning</p>
 | 
			
		||||
<p>If the email URL is invalid, the application may not start.</p>
 | 
			
		||||
@@ -372,7 +388,7 @@ see <a class="reference external" href="https://docs.sqlalchemy.org/en/13/core/p
 | 
			
		||||
</div>
 | 
			
		||||
<p>Redis instance used by <strong>Dramatiq</strong>.</p>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Default</dt>
 | 
			
		||||
<dt class="field-odd">Default<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><p>local Redis instance (<code class="docutils literal notranslate"><span class="pre">redis://</span></code>)</p>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
@@ -398,12 +414,29 @@ see <a class="reference external" href="https://docs.sqlalchemy.org/en/13/core/p
 | 
			
		||||
<div class="line">Since <strong>0.4.9</strong>, it’s also used to generate static maps (to keep default server, see <a class="reference external" href="installation.html#envvar-DEFAULT_STATICMAP">DEFAULT_STATICMAP</a>)</div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Default</dt>
 | 
			
		||||
<dt class="field-odd">Default<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><p><cite>https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png</cite></p>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
</dd></dl>
 | 
			
		||||
 | 
			
		||||
<dl class="std envvar">
 | 
			
		||||
<dt class="sig sig-object std" id="envvar-STATICMAP_SUBDOMAINS">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">STATICMAP_SUBDOMAINS</span> <span class="pre">🆕</span></span><a class="headerlink" href="#envvar-STATICMAP_SUBDOMAINS" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><div class="versionadded">
 | 
			
		||||
<p><span class="versionmodified added">New in version 0.6.10.</span></p>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="line-block">
 | 
			
		||||
<div class="line">Some tile servers require a subdomain, see <a class="reference external" href="installation.html#map-tile-server">Map tile server</a>.</div>
 | 
			
		||||
<div class="line">For instance: “a,b,c” for OSM France.</div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Default<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><p>empty string</p>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
</dd></dl>
 | 
			
		||||
 | 
			
		||||
<dl class="std envvar">
 | 
			
		||||
<dt class="sig sig-object std" id="envvar-MAP_ATTRIBUTION">
 | 
			
		||||
<span class="sig-name descname"><span class="pre">MAP_ATTRIBUTION</span></span><a class="headerlink" href="#envvar-MAP_ATTRIBUTION" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
@@ -412,7 +445,7 @@ see <a class="reference external" href="https://docs.sqlalchemy.org/en/13/core/p
 | 
			
		||||
</div>
 | 
			
		||||
<p>Map attribution (if using another tile server), see <a class="reference external" href="installation.html#map-tile-server">Map tile server</a>.</p>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Default</dt>
 | 
			
		||||
<dt class="field-odd">Default<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><p><cite>&copy; <a href=”http://www.openstreetmap.org/copyright” target=”_blank” rel=”noopener noreferrer”>OpenStreetMap</a> contributors</cite></p>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
@@ -420,13 +453,23 @@ see <a class="reference external" href="https://docs.sqlalchemy.org/en/13/core/p
 | 
			
		||||
 | 
			
		||||
<dl class="std envvar">
 | 
			
		||||
<dt class="sig sig-object std" id="envvar-DEFAULT_STATICMAP">
 | 
			
		||||
<span id="envvar-DEFAULT_STATICMAP 🆕"></span><span class="sig-name descname"><span class="pre">DEFAULT_STATICMAP</span> <span class="pre">🆕</span></span><a class="headerlink" href="#envvar-DEFAULT_STATICMAP" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<span class="sig-name descname"><span class="pre">DEFAULT_STATICMAP</span></span><a class="headerlink" href="#envvar-DEFAULT_STATICMAP" title="Permalink to this definition">¶</a></dt>
 | 
			
		||||
<dd><div class="versionadded">
 | 
			
		||||
<p><span class="versionmodified added">New in version 0.4.9.</span></p>
 | 
			
		||||
</div>
 | 
			
		||||
<p>If <cite>True</cite>, it keeps using default tile server to generate static maps.</p>
 | 
			
		||||
<div class="line-block">
 | 
			
		||||
<div class="line">If <cite>True</cite>, it keeps using default tile server to generate static maps (Komoot.de tile server).</div>
 | 
			
		||||
<div class="line">Otherwise, it uses the tile server set in <a class="reference external" href="installation.html#envvar-TILE_SERVER_URL">TILE_SERVER_URL</a>.</div>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="versionchanged">
 | 
			
		||||
<p><span class="versionmodified changed">Changed in version 0.6.10.</span></p>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="line-block">
 | 
			
		||||
<div class="line">This variable is now case-insensitive.</div>
 | 
			
		||||
<div class="line">If <cite>False</cite>, depending on tile server, <a class="reference external" href="installation.html#envvar-STATICMAP_SUBDOMAINS">subdomains</a> may be mandatory.</div>
 | 
			
		||||
</div>
 | 
			
		||||
<dl class="field-list simple">
 | 
			
		||||
<dt class="field-odd">Default</dt>
 | 
			
		||||
<dt class="field-odd">Default<span class="colon">:</span></dt>
 | 
			
		||||
<dd class="field-odd"><p>False</p>
 | 
			
		||||
</dd>
 | 
			
		||||
</dl>
 | 
			
		||||
@@ -448,7 +491,7 @@ see <a class="reference external" href="https://docs.sqlalchemy.org/en/13/core/p
 | 
			
		||||
</dd></dl>
 | 
			
		||||
 | 
			
		||||
<section id="emails">
 | 
			
		||||
<h3>Emails<a class="headerlink" href="#emails" title="Permalink to this headline">¶</a></h3>
 | 
			
		||||
<h3>Emails<a class="headerlink" href="#emails" title="Permalink to this heading">¶</a></h3>
 | 
			
		||||
<div class="versionadded">
 | 
			
		||||
<p><span class="versionmodified added">New in version 0.3.0.</span></p>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -458,16 +501,43 @@ see <a class="reference external" href="https://docs.sqlalchemy.org/en/13/core/p
 | 
			
		||||
<li><p>with SSL: <code class="docutils literal notranslate"><span class="pre">smtp://username:password@smtp.example.com:465/?ssl=True</span></code></p></li>
 | 
			
		||||
<li><p>with STARTTLS: <code class="docutils literal notranslate"><span class="pre">smtp://username:password@smtp.example.com:587/?tls=True</span></code></p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<div class="versionadded">
 | 
			
		||||
<p><span class="versionmodified added">New in version 0.5.3.</span></p>
 | 
			
		||||
<div class="admonition warning">
 | 
			
		||||
<p class="admonition-title">Warning</p>
 | 
			
		||||
<div class="line-block">
 | 
			
		||||
<div class="line">- If the email URL is invalid, the application may not start.</div>
 | 
			
		||||
<div class="line">- Sending emails with Office365 may not work if SMTP auth is disabled.</div>
 | 
			
		||||
</div>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="versionchanged">
 | 
			
		||||
<p><span class="versionmodified changed">Changed in version 0.5.3.</span></p>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="line-block">
 | 
			
		||||
<div class="line">Credentials can be omitted: <code class="docutils literal notranslate"><span class="pre">smtp://smtp.example.com:25</span></code>.</div>
 | 
			
		||||
<div class="line">If <code class="docutils literal notranslate"><span class="pre">:<port></span></code> is omitted, the port defaults to 25.</div>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="admonition warning">
 | 
			
		||||
<p class="admonition-title">Warning</p>
 | 
			
		||||
<div class="line-block">
 | 
			
		||||
<div class="line">Since 0.6.0, newly created accounts must be confirmed (an email with confirmation instructions is sent after registration).</div>
 | 
			
		||||
</div>
 | 
			
		||||
</div>
 | 
			
		||||
<p>Emails sent by FitTrackee are:</p>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>account confirmation instructions</p></li>
 | 
			
		||||
<li><p>password reset request</p></li>
 | 
			
		||||
<li><p>email change (to old and new email adresses)</p></li>
 | 
			
		||||
<li><p>password change</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<div class="versionchanged">
 | 
			
		||||
<p><span class="versionmodified changed">Changed in version 0.6.5.</span></p>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="line-block">
 | 
			
		||||
<div class="line">For single-user instance, it is possible to disable email sending with an empty <code class="docutils literal notranslate"><span class="pre">EMAIL_URL</span></code> (in this case, no need to start dramatiq workers).</div>
 | 
			
		||||
<div class="line">A <a class="reference external" href="cli.html#ftcli-users-update">CLI</a> is available to activate account and modify email and password.</div>
 | 
			
		||||
</div>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="map-tile-server">
 | 
			
		||||
<h3>Map tile server<a class="headerlink" href="#map-tile-server" title="Permalink to this headline">¶</a></h3>
 | 
			
		||||
<h3>Map tile server<a class="headerlink" href="#map-tile-server" title="Permalink to this heading">¶</a></h3>
 | 
			
		||||
<div class="versionadded">
 | 
			
		||||
<p><span class="versionmodified added">New in version 0.4.0.</span></p>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -484,10 +554,21 @@ The tile server can be changed by updating <code class="docutils literal notrans
 | 
			
		||||
<div class="line">Check the terms of service of tile provider for map attribution</div>
 | 
			
		||||
</div>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="versionchanged">
 | 
			
		||||
<p><span class="versionmodified changed">Changed in version 0.6.10.</span></p>
 | 
			
		||||
</div>
 | 
			
		||||
<p>Since the tile server can be used for static map generation, some servers require a subdomain.</p>
 | 
			
		||||
<p>For instance, to set OSM France tile server, the expected values are:</p>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p><code class="docutils literal notranslate"><span class="pre">TILE_SERVER_URL=https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png</span></code></p></li>
 | 
			
		||||
<li><p><code class="docutils literal notranslate"><span class="pre">MAP_ATTRIBUTION='fond</span> <span class="pre">de</span> <span class="pre">carte</span> <span class="pre">par</span> <span class="pre"><a</span> <span class="pre">href="http://www.openstreetmap.fr/mentions-legales/"</span> <span class="pre">target="_blank"</span> <span class="pre">rel="nofollow</span> <span class="pre">noopener">OpenStreetMap</span> <span class="pre">France</a>,</span> <span class="pre">sous&nbsp;<a</span> <span class="pre">href="http://creativecommons.org/licenses/by-sa/2.0/fr/"</span> <span class="pre">target="_blank"</span> <span class="pre">rel="nofollow</span> <span class="pre">noopener">licence</span> <span class="pre">CC</span> <span class="pre">BY-SA</a>'</span></code></p></li>
 | 
			
		||||
<li><p><code class="docutils literal notranslate"><span class="pre">STATICMAP_SUBDOMAINS=a,b,c</span></code></p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<p>The subdomain will be chosen randomly.</p>
 | 
			
		||||
</section>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="id1">
 | 
			
		||||
<h2>Installation<a class="headerlink" href="#id1" title="Permalink to this headline">¶</a></h2>
 | 
			
		||||
<h2>Installation<a class="headerlink" href="#id1" title="Permalink to this heading">¶</a></h2>
 | 
			
		||||
<div class="admonition warning">
 | 
			
		||||
<p class="admonition-title">Warning</p>
 | 
			
		||||
<div class="line-block">
 | 
			
		||||
@@ -495,7 +576,7 @@ The tile server can be changed by updating <code class="docutils literal notrans
 | 
			
		||||
</div>
 | 
			
		||||
</div>
 | 
			
		||||
<section id="from-pypi">
 | 
			
		||||
<h3>From PyPI<a class="headerlink" href="#from-pypi" title="Permalink to this headline">¶</a></h3>
 | 
			
		||||
<h3>From PyPI<a class="headerlink" href="#from-pypi" title="Permalink to this heading">¶</a></h3>
 | 
			
		||||
<div class="admonition note">
 | 
			
		||||
<p class="admonition-title">Note</p>
 | 
			
		||||
<div class="line-block">
 | 
			
		||||
@@ -529,7 +610,7 @@ $ <span class="nb">source</span> .env
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>Initialize database schema</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ fittrackee_upgrade_db
 | 
			
		||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ ftcli db upgrade
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
@@ -539,7 +620,7 @@ $ <span class="nb">source</span> .env
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>Start task queue workers</p></li>
 | 
			
		||||
<li><p>Start task queue workers if email sending is enabled.</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ fittrackee_worker --processes <span class="m">2</span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
@@ -552,14 +633,18 @@ $ <span class="nb">source</span> .env
 | 
			
		||||
</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>
 | 
			
		||||
<li><p>To set admin rights to the newly created account, use the following command line:</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ fittrackee_set_admin <username>
 | 
			
		||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ ftcli users update <username> --set-admin <span class="nb">true</span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="admonition note">
 | 
			
		||||
<p class="admonition-title">Note</p>
 | 
			
		||||
<p>If the user account is inactive, it activates it.</p>
 | 
			
		||||
</div>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="from-sources">
 | 
			
		||||
<h3>From sources<a class="headerlink" href="#from-sources" title="Permalink to this headline">¶</a></h3>
 | 
			
		||||
<h3>From sources<a class="headerlink" href="#from-sources" title="Permalink to this heading">¶</a></h3>
 | 
			
		||||
<div class="admonition warning">
 | 
			
		||||
<p class="admonition-title">Warning</p>
 | 
			
		||||
<div class="line-block">
 | 
			
		||||
@@ -577,7 +662,7 @@ $ poetry config virtualenvs.in-project <span class="nb">true</span>
 | 
			
		||||
<p>For other OS, see <a class="reference external" href="https://python-poetry.org/docs/#installation">Poetry Documentation</a></p>
 | 
			
		||||
</div>
 | 
			
		||||
<section id="dev-environment">
 | 
			
		||||
<h4>Dev environment<a class="headerlink" href="#dev-environment" title="Permalink to this headline">¶</a></h4>
 | 
			
		||||
<h4>Dev environment<a class="headerlink" href="#dev-environment" title="Permalink to this heading">¶</a></h4>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>Clone this repo:</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
@@ -609,14 +694,18 @@ $ make install-db
 | 
			
		||||
</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>
 | 
			
		||||
<li><p>To set admin rights to the newly created account, use the following command line:</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>
 | 
			
		||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ make user-set-admin <span class="nv">USERNAME</span><span class="o">=</span><username>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="admonition note">
 | 
			
		||||
<p class="admonition-title">Note</p>
 | 
			
		||||
<p>If the user account is inactive, it activates it.</p>
 | 
			
		||||
</div>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="production-environment">
 | 
			
		||||
<h4>Production environment<a class="headerlink" href="#production-environment" title="Permalink to this headline">¶</a></h4>
 | 
			
		||||
<h4>Production environment<a class="headerlink" href="#production-environment" title="Permalink to this heading">¶</a></h4>
 | 
			
		||||
<div class="admonition warning">
 | 
			
		||||
<p class="admonition-title">Warning</p>
 | 
			
		||||
<div class="line-block">
 | 
			
		||||
@@ -624,11 +713,11 @@ $ make install-db
 | 
			
		||||
</div>
 | 
			
		||||
</div>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>Download the last release (for now, it is the release v0.5.7):</p></li>
 | 
			
		||||
<li><p>Download the last release (for now, it is the release v0.6.10):</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ wget https://github.com/SamR1/FitTrackee/archive/v0.5.7.tar.gz
 | 
			
		||||
$ tar -xzf v0.5.7.tar.gz
 | 
			
		||||
$ mv FitTrackee-0.5.7 FitTrackee
 | 
			
		||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.10.tar.gz
 | 
			
		||||
$ tar -xzf v0.6.10.tar.gz
 | 
			
		||||
$ mv FitTrackee-0.6.10 FitTrackee
 | 
			
		||||
$ <span class="nb">cd</span> FitTrackee
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -653,18 +742,26 @@ database credentials</strong>):</p></li>
 | 
			
		||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ make run
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="admonition note">
 | 
			
		||||
<p class="admonition-title">Note</p>
 | 
			
		||||
<p>If email sending is disabled: <code class="docutils literal notranslate"><span class="pre">$</span> <span class="pre">make</span> <span class="pre">run-server</span></code></p>
 | 
			
		||||
</div>
 | 
			
		||||
<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>
 | 
			
		||||
<li><p>To set admin rights to the newly created account, use the following command line:</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>
 | 
			
		||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ make user-set-admin <span class="nv">USERNAME</span><span class="o">=</span><username>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="admonition note">
 | 
			
		||||
<p class="admonition-title">Note</p>
 | 
			
		||||
<p>If the user account is inactive, it activates it.</p>
 | 
			
		||||
</div>
 | 
			
		||||
</section>
 | 
			
		||||
</section>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="upgrade">
 | 
			
		||||
<h2>Upgrade<a class="headerlink" href="#upgrade" title="Permalink to this headline">¶</a></h2>
 | 
			
		||||
<h2>Upgrade<a class="headerlink" href="#upgrade" title="Permalink to this heading">¶</a></h2>
 | 
			
		||||
<div class="admonition warning">
 | 
			
		||||
<p class="admonition-title">Warning</p>
 | 
			
		||||
<div class="line-block">
 | 
			
		||||
@@ -674,9 +771,9 @@ database credentials</strong>):</p></li>
 | 
			
		||||
</div>
 | 
			
		||||
</div>
 | 
			
		||||
<section id="id2">
 | 
			
		||||
<h3>From PyPI<a class="headerlink" href="#id2" title="Permalink to this headline">¶</a></h3>
 | 
			
		||||
<h3>From PyPI<a class="headerlink" href="#id2" title="Permalink to this heading">¶</a></h3>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>Activate the virtualenv</p></li>
 | 
			
		||||
<li><p>Stop the application and activate the virtualenv</p></li>
 | 
			
		||||
<li><p>Upgrade with pip</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ pip install -U fittrackee
 | 
			
		||||
@@ -692,17 +789,17 @@ $ <span class="nb">source</span> .env
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>Upgrade database if needed (see changelog for migrations):</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ fittrackee_upgrade_db
 | 
			
		||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ ftcli db upgrade
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>Restart the application and task queue workers.</p></li>
 | 
			
		||||
<li><p>Restart the application and task queue workers (if email sending is enabled).</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="id3">
 | 
			
		||||
<h3>From sources<a class="headerlink" href="#id3" title="Permalink to this headline">¶</a></h3>
 | 
			
		||||
<h3>From sources<a class="headerlink" href="#id3" title="Permalink to this heading">¶</a></h3>
 | 
			
		||||
<section id="id4">
 | 
			
		||||
<h4>Dev environment<a class="headerlink" href="#id4" title="Permalink to this headline">¶</a></h4>
 | 
			
		||||
<h4>Dev environment<a class="headerlink" href="#id4" title="Permalink to this heading">¶</a></h4>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>Stop the application and pull the repository:</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
@@ -736,15 +833,15 @@ $ <span class="nb">source</span> .env
 | 
			
		||||
</div>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="prod-environment">
 | 
			
		||||
<h4>Prod environment<a class="headerlink" href="#prod-environment" title="Permalink to this headline">¶</a></h4>
 | 
			
		||||
<h4>Prod environment<a class="headerlink" href="#prod-environment" title="Permalink to this heading">¶</a></h4>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>Stop the application</p></li>
 | 
			
		||||
<li><p>Change to the directory where FitTrackee directory is located</p></li>
 | 
			
		||||
<li><p>Download the last release (for now, it is the release v0.5.7) and overwrite existing files:</p></li>
 | 
			
		||||
<li><p>Download the last release (for now, it is the release v0.6.10) and overwrite existing files:</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ wget https://github.com/SamR1/FitTrackee/archive/v0.5.7.tar.gz
 | 
			
		||||
$ tar -xzf v0.5.7.tar.gz
 | 
			
		||||
$ cp -R FitTrackee-0.5.7/* FitTrackee/
 | 
			
		||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.10.tar.gz
 | 
			
		||||
$ tar -xzf v0.6.10.tar.gz
 | 
			
		||||
$ cp -R FitTrackee-0.6.10/* FitTrackee/
 | 
			
		||||
$ <span class="nb">cd</span> FitTrackee
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -767,11 +864,15 @@ $ <span class="nb">cd</span> FitTrackee
 | 
			
		||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ make run
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<div class="admonition note">
 | 
			
		||||
<p class="admonition-title">Note</p>
 | 
			
		||||
<p>If email sending is disabled: <code class="docutils literal notranslate"><span class="pre">$</span> <span class="pre">make</span> <span class="pre">run-server</span></code></p>
 | 
			
		||||
</div>
 | 
			
		||||
</section>
 | 
			
		||||
</section>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="deployment">
 | 
			
		||||
<h2>Deployment<a class="headerlink" href="#deployment" title="Permalink to this headline">¶</a></h2>
 | 
			
		||||
<h2>Deployment<a class="headerlink" href="#deployment" title="Permalink to this heading">¶</a></h2>
 | 
			
		||||
<p>There are several ways to start <strong>FitTrackee</strong> web application and task queue
 | 
			
		||||
library.
 | 
			
		||||
One way is to use a <strong>systemd</strong> services and <strong>Nginx</strong> to proxy pass to <strong>Gunicorn</strong>.</p>
 | 
			
		||||
@@ -803,6 +904,7 @@ One way is to use a <strong>systemd</strong> services and <strong>Nginx</strong>
 | 
			
		||||
<span class="n">Environment</span><span class="o">=</span><span class="s2">"SENDER_EMAIL="</span>
 | 
			
		||||
<span class="n">Environment</span><span class="o">=</span><span class="s2">"REDIS_URL="</span>
 | 
			
		||||
<span class="n">Environment</span><span class="o">=</span><span class="s2">"TILE_SERVER_URL="</span>
 | 
			
		||||
<span class="n">Environment</span><span class="o">=</span><span class="s2">"STATICMAP_SUBDOMAINS="</span>
 | 
			
		||||
<span class="n">Environment</span><span class="o">=</span><span class="s2">"MAP_ATTRIBUTION="</span>
 | 
			
		||||
<span class="n">Environment</span><span class="o">=</span><span class="s2">"WEATHER_API_KEY="</span>
 | 
			
		||||
<span class="n">WorkingDirectory</span><span class="o">=/</span><span class="n">home</span><span class="o">/<</span><span class="n">USER</span><span class="o">>/<</span><span class="n">FITTRACKEE</span> <span class="n">DIRECTORY</span><span class="o">></span>
 | 
			
		||||
@@ -886,30 +988,34 @@ server {
 | 
			
		||||
</div>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="docker">
 | 
			
		||||
<h2>Docker<a class="headerlink" href="#docker" title="Permalink to this headline">¶</a></h2>
 | 
			
		||||
<h2>Docker<a class="headerlink" href="#docker" title="Permalink to this heading">¶</a></h2>
 | 
			
		||||
<section id="id5">
 | 
			
		||||
<h3>Installation<a class="headerlink" href="#id5" title="Permalink to this headline">¶</a></h3>
 | 
			
		||||
<h3>Installation<a class="headerlink" href="#id5" title="Permalink to this heading">¶</a></h3>
 | 
			
		||||
<div class="versionadded">
 | 
			
		||||
<p><span class="versionmodified added">New in version 0.4.4.</span></p>
 | 
			
		||||
</div>
 | 
			
		||||
<p>For evaluation purposes , docker files are available,
 | 
			
		||||
installing <strong>FitTrackee</strong> from <strong>sources</strong>.</p>
 | 
			
		||||
<p>For evaluation purposes, docker files are available, installing <strong>FitTrackee</strong> from <strong>sources</strong>.</p>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>To install <strong>FitTrackee</strong> with database initialisation and run the application and dramatiq workers:</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ git clone https://github.com/SamR1/FitTrackee.git
 | 
			
		||||
$ <span class="nb">cd</span> FitTrackee
 | 
			
		||||
$ cp .env.docker .env
 | 
			
		||||
$ make docker-build docker-run docker-init
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<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>
 | 
			
		||||
<li><p>To set admin rights to the newly created account, use the following command line:</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>
 | 
			
		||||
<div class="admonition note">
 | 
			
		||||
<p class="admonition-title">Note</p>
 | 
			
		||||
<p>If the user account is inactive, it activates it.</p>
 | 
			
		||||
</div>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>To stop <strong>Fittrackee</strong>:</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
@@ -930,7 +1036,7 @@ $ make docker-build docker-run docker-init
 | 
			
		||||
</div>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="development">
 | 
			
		||||
<h3>Development<a class="headerlink" href="#development" title="Permalink to this headline">¶</a></h3>
 | 
			
		||||
<h3>Development<a class="headerlink" href="#development" title="Permalink to this heading">¶</a></h3>
 | 
			
		||||
<div class="versionadded">
 | 
			
		||||
<p><span class="versionmodified added">New in version 0.5.0.</span></p>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -968,7 +1074,7 @@ $ make docker-build docker-run docker-init
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        © Copyright 2018 - 2022, SamR1.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 4.4.0.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.0.2.<br/>
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</footer>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								docs/objects.inv
									
									
									
									
									
								
							
							
						
						@@ -1,10 +1,10 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <title>Search — FitTrackee 0.5.7
 | 
			
		||||
    <title>Search — FitTrackee 0.6.10
 | 
			
		||||
 documentation</title>
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="_static/pygments.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
 | 
			
		||||
@@ -12,6 +12,7 @@
 | 
			
		||||
    <script data-url_root="./" id="documentation_options" src="_static/documentation_options.js"></script>
 | 
			
		||||
    <script src="_static/jquery.js"></script>
 | 
			
		||||
    <script src="_static/underscore.js"></script>
 | 
			
		||||
    <script src="_static/_sphinx_javascript_frameworks_compat.js"></script>
 | 
			
		||||
    <script src="_static/doctools.js"></script>
 | 
			
		||||
    <link rel="index" title="Index" href="genindex.html" />
 | 
			
		||||
    <link rel="search" title="Search" href="#" />
 | 
			
		||||
@@ -44,7 +45,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
        <a class="navbar-brand" href="index.html">
 | 
			
		||||
          FitTrackee</a>
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.5.7
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.6.10
 | 
			
		||||
</b></span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@@ -65,6 +66,7 @@
 | 
			
		||||
      aria-labelledby="dLabelGlobalToc"><ul>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="features.html">Features</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="cli.html">Command line interface</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="api/index.html">API documentation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="troubleshooting/index.html">Troubleshooting</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="changelog.html">Change log</a></li>
 | 
			
		||||
@@ -149,7 +151,7 @@
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        © Copyright 2018 - 2022, SamR1.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 4.4.0.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.0.2.<br/>
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</footer>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
 | 
			
		||||
    <title>Administrator — FitTrackee 0.5.7
 | 
			
		||||
    <title>Administrator — FitTrackee 0.6.10
 | 
			
		||||
 documentation</title>
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
    <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
 | 
			
		||||
    <script src="../_static/jquery.js"></script>
 | 
			
		||||
    <script src="../_static/underscore.js"></script>
 | 
			
		||||
    <script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
 | 
			
		||||
    <script src="../_static/doctools.js"></script>
 | 
			
		||||
    <link rel="index" title="Index" href="../genindex.html" />
 | 
			
		||||
    <link rel="search" title="Search" href="../search.html" />
 | 
			
		||||
@@ -40,7 +41,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
        <a class="navbar-brand" href="../index.html">
 | 
			
		||||
          FitTrackee</a>
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.5.7
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.6.10
 | 
			
		||||
</b></span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@@ -61,6 +62,7 @@
 | 
			
		||||
      aria-labelledby="dLabelGlobalToc"><ul class="current">
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../features.html">Features</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../installation.html">Installation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../cli.html">Command line interface</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../api/index.html">API documentation</a></li>
 | 
			
		||||
<li class="toctree-l1 current"><a class="reference internal" href="index.html">Troubleshooting</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../changelog.html">Change log</a></li>
 | 
			
		||||
@@ -79,6 +81,7 @@
 | 
			
		||||
      aria-labelledby="dLabelLocalToc"><ul>
 | 
			
		||||
<li><a class="reference internal" href="#">Administrator</a><ul>
 | 
			
		||||
<li><a class="reference internal" href="#fittrackee-fails-to-start"><cite>FitTrackee fails to start</cite></a></li>
 | 
			
		||||
<li><a class="reference internal" href="#map-images-are-not-displayed-but-map-is-shown-in-workout-detail"><cite>Map images are not displayed but map is shown in Workout detail</cite></a></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</li>
 | 
			
		||||
</ul>
 | 
			
		||||
@@ -129,16 +132,29 @@
 | 
			
		||||
    <div class="body col-md-12 content" role="main">
 | 
			
		||||
      
 | 
			
		||||
  <section id="administrator">
 | 
			
		||||
<h1>Administrator<a class="headerlink" href="#administrator" title="Permalink to this headline">¶</a></h1>
 | 
			
		||||
<h1>Administrator<a class="headerlink" href="#administrator" title="Permalink to this heading">¶</a></h1>
 | 
			
		||||
<section id="fittrackee-fails-to-start">
 | 
			
		||||
<h2><cite>FitTrackee fails to start</cite><a class="headerlink" href="#fittrackee-fails-to-start" title="Permalink to this headline">¶</a></h2>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>Check the database URL in <a class="reference external" href="../installation.html#envvar-DATABASE_URL">Environment variables</a> if the following error is displayed in <strong>gunicorn</strong> logs:</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<h2><cite>FitTrackee fails to start</cite><a class="headerlink" href="#fittrackee-fails-to-start" title="Permalink to this heading">¶</a></h2>
 | 
			
		||||
<ul>
 | 
			
		||||
<li><p>Check the database URL in <a class="reference external" href="../installation.html#envvar-DATABASE_URL">environment variables</a> if the following error is displayed in <strong>gunicorn</strong> logs:</p>
 | 
			
		||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">sqlalchemy</span><span class="o">.</span><span class="n">exc</span><span class="o">.</span><span class="n">NoSuchModuleError</span><span class="p">:</span> <span class="n">Can</span><span class="s1">'t load plugin: sqlalchemy.dialects:postgres</span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<p>It must start with <cite>postgresql://</cite> (engine URLs starting with <cite>postgres://</cite> are no longer supported).</p>
 | 
			
		||||
</li>
 | 
			
		||||
<li><p>Check the email URL in <a class="reference external" href="../installation.html#envvar-EMAIL_URL">environment variables</a> if the following error is displayed in <strong>gunicorn</strong> logs:</p>
 | 
			
		||||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">fittrackee</span><span class="o">.</span><span class="n">emails</span><span class="o">.</span><span class="n">exceptions</span><span class="o">.</span><span class="n">InvalidEmailUrlScheme</span>
 | 
			
		||||
</pre></div>
 | 
			
		||||
</div>
 | 
			
		||||
<p>A valid <code class="docutils literal notranslate"><span class="pre">EMAIL_URL</span></code> must be provided (see <a class="reference external" href="../installation.html#emails">emails</a>).</p>
 | 
			
		||||
</li>
 | 
			
		||||
</ul>
 | 
			
		||||
</section>
 | 
			
		||||
<section id="map-images-are-not-displayed-but-map-is-shown-in-workout-detail">
 | 
			
		||||
<h2><cite>Map images are not displayed but map is shown in Workout detail</cite><a class="headerlink" href="#map-images-are-not-displayed-but-map-is-shown-in-workout-detail" title="Permalink to this heading">¶</a></h2>
 | 
			
		||||
<ul class="simple">
 | 
			
		||||
<li><p>Check the path in <a class="reference external" href="../installation.html#envvar-UPLOAD_FOLDER">environment variables</a>. <code class="docutils literal notranslate"><span class="pre">UPLOAD_FOLDER</span></code> must be set with an absolute path.</p></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</section>
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
@@ -155,7 +171,7 @@
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        © Copyright 2018 - 2022, SamR1.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 4.4.0.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.0.2.<br/>
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</footer>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
 | 
			
		||||
    <title>Troubleshooting — FitTrackee 0.5.7
 | 
			
		||||
    <title>Troubleshooting — FitTrackee 0.6.10
 | 
			
		||||
 documentation</title>
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
    <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
 | 
			
		||||
    <script src="../_static/jquery.js"></script>
 | 
			
		||||
    <script src="../_static/underscore.js"></script>
 | 
			
		||||
    <script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
 | 
			
		||||
    <script src="../_static/doctools.js"></script>
 | 
			
		||||
    <link rel="index" title="Index" href="../genindex.html" />
 | 
			
		||||
    <link rel="search" title="Search" href="../search.html" />
 | 
			
		||||
@@ -40,7 +41,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
        <a class="navbar-brand" href="../index.html">
 | 
			
		||||
          FitTrackee</a>
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.5.7
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.6.10
 | 
			
		||||
</b></span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@@ -61,6 +62,7 @@
 | 
			
		||||
      aria-labelledby="dLabelGlobalToc"><ul class="current">
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../features.html">Features</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../installation.html">Installation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../cli.html">Command line interface</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../api/index.html">API documentation</a></li>
 | 
			
		||||
<li class="toctree-l1 current"><a class="current reference internal" href="#">Troubleshooting</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../changelog.html">Change log</a></li>
 | 
			
		||||
@@ -126,12 +128,12 @@
 | 
			
		||||
    <div class="body col-md-12 content" role="main">
 | 
			
		||||
      
 | 
			
		||||
  <section id="troubleshooting">
 | 
			
		||||
<h1>Troubleshooting<a class="headerlink" href="#troubleshooting" title="Permalink to this headline">¶</a></h1>
 | 
			
		||||
<h1>Troubleshooting<a class="headerlink" href="#troubleshooting" title="Permalink to this heading">¶</a></h1>
 | 
			
		||||
<div class="toctree-wrapper compound">
 | 
			
		||||
<p class="caption" role="heading"><span class="caption-text">Endpoints:</span></p>
 | 
			
		||||
<ul>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="administrator.html">Administrator</a><ul>
 | 
			
		||||
<li class="toctree-l2"><a class="reference internal" href="administrator.html#fittrackee-fails-to-start"><cite>FitTrackee fails to start</cite></a></li>
 | 
			
		||||
<li class="toctree-l2"><a class="reference internal" href="administrator.html#map-images-are-not-displayed-but-map-is-shown-in-workout-detail"><cite>Map images are not displayed but map is shown in Workout detail</cite></a></li>
 | 
			
		||||
</ul>
 | 
			
		||||
</li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="user.html">User</a></li>
 | 
			
		||||
@@ -152,7 +154,7 @@
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        © Copyright 2018 - 2022, SamR1.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 4.4.0.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.0.2.<br/>
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</footer>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
 | 
			
		||||
<html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
  <head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.18.1: http://docutils.sourceforge.net/" />
 | 
			
		||||
 | 
			
		||||
    <title>User — FitTrackee 0.5.7
 | 
			
		||||
    <title>User — FitTrackee 0.6.10
 | 
			
		||||
 documentation</title>
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
 | 
			
		||||
    <link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
    <script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
 | 
			
		||||
    <script src="../_static/jquery.js"></script>
 | 
			
		||||
    <script src="../_static/underscore.js"></script>
 | 
			
		||||
    <script src="../_static/_sphinx_javascript_frameworks_compat.js"></script>
 | 
			
		||||
    <script src="../_static/doctools.js"></script>
 | 
			
		||||
    <link rel="index" title="Index" href="../genindex.html" />
 | 
			
		||||
    <link rel="search" title="Search" href="../search.html" />
 | 
			
		||||
@@ -40,7 +41,7 @@
 | 
			
		||||
        </button>
 | 
			
		||||
        <a class="navbar-brand" href="../index.html">
 | 
			
		||||
          FitTrackee</a>
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.5.7
 | 
			
		||||
        <span class="navbar-text navbar-version pull-left"><b>0.6.10
 | 
			
		||||
</b></span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
@@ -61,6 +62,7 @@
 | 
			
		||||
      aria-labelledby="dLabelGlobalToc"><ul class="current">
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../features.html">Features</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../installation.html">Installation</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../cli.html">Command line interface</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../api/index.html">API documentation</a></li>
 | 
			
		||||
<li class="toctree-l1 current"><a class="reference internal" href="index.html">Troubleshooting</a></li>
 | 
			
		||||
<li class="toctree-l1"><a class="reference internal" href="../changelog.html">Change log</a></li>
 | 
			
		||||
@@ -126,7 +128,7 @@
 | 
			
		||||
    <div class="body col-md-12 content" role="main">
 | 
			
		||||
      
 | 
			
		||||
  <section id="user">
 | 
			
		||||
<h1>User<a class="headerlink" href="#user" title="Permalink to this headline">¶</a></h1>
 | 
			
		||||
<h1>User<a class="headerlink" href="#user" title="Permalink to this heading">¶</a></h1>
 | 
			
		||||
<p><code class="docutils literal notranslate"><span class="pre">TODO</span></code></p>
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
@@ -143,7 +145,7 @@
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>
 | 
			
		||||
        © Copyright 2018 - 2022, SamR1.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 4.4.0.<br/>
 | 
			
		||||
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.0.2.<br/>
 | 
			
		||||
    </p>
 | 
			
		||||
  </div>
 | 
			
		||||
</footer>
 | 
			
		||||
 
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 537 KiB After Width: | Height: | Size: 540 KiB  | 
| 
		 Before Width: | Height: | Size: 370 KiB After Width: | Height: | Size: 369 KiB  | 
| 
		 Before Width: | Height: | Size: 203 KiB After Width: | Height: | Size: 204 KiB  | 
| 
		 Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 69 KiB  | 
| 
		 Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 71 KiB  | 
| 
		 Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB  | 
@@ -4,8 +4,9 @@ Authentication
 | 
			
		||||
.. autoflask:: fittrackee:create_app()
 | 
			
		||||
   :endpoints:
 | 
			
		||||
    auth.register_user,
 | 
			
		||||
    auth.confirm_account,
 | 
			
		||||
    auth.resend_account_confirmation_email,
 | 
			
		||||
    auth.login_user,
 | 
			
		||||
    auth.logout_user,
 | 
			
		||||
    auth.get_authenticated_user_profile,
 | 
			
		||||
    auth.edit_user,
 | 
			
		||||
    auth.edit_user_preferences,
 | 
			
		||||
@@ -14,4 +15,6 @@ Authentication
 | 
			
		||||
    auth.edit_picture,
 | 
			
		||||
    auth.del_picture,
 | 
			
		||||
    auth.request_password_reset,
 | 
			
		||||
    auth.update_password
 | 
			
		||||
    auth.update_user_account,
 | 
			
		||||
    auth.update_password,
 | 
			
		||||
    auth.update_email
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										67
									
								
								docsrc/source/cli.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,67 @@
 | 
			
		||||
Command line interface
 | 
			
		||||
######################
 | 
			
		||||
 | 
			
		||||
A command line interface (CLI) is available to manage database and users.
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
    $ ftcli
 | 
			
		||||
    Usage: ftcli [OPTIONS] COMMAND [ARGS]...
 | 
			
		||||
 | 
			
		||||
      FitTrackee Command Line Interface
 | 
			
		||||
 | 
			
		||||
    Options:
 | 
			
		||||
      --help  Show this message and exit.
 | 
			
		||||
 | 
			
		||||
    Commands:
 | 
			
		||||
      db     Manage database.
 | 
			
		||||
      users  Manage users.
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
    | The following commands are now deprecated and will be removed in a next version:
 | 
			
		||||
    | - ``fittrackee_set_admin``
 | 
			
		||||
    | - ``fittrackee_upgrade_db``
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Database
 | 
			
		||||
~~~~~~~~
 | 
			
		||||
 | 
			
		||||
``ftcli db upgrade``
 | 
			
		||||
""""""""""""""""""""
 | 
			
		||||
.. versionadded:: 0.6.5
 | 
			
		||||
 | 
			
		||||
Apply migrations.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
``ftcli db drop``
 | 
			
		||||
"""""""""""""""""
 | 
			
		||||
.. versionadded:: 0.6.5
 | 
			
		||||
 | 
			
		||||
Empty database and delete uploaded files, only on development environments.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Users
 | 
			
		||||
~~~~~
 | 
			
		||||
 | 
			
		||||
``ftcli users update``
 | 
			
		||||
""""""""""""""""""""""
 | 
			
		||||
.. versionadded:: 0.6.5
 | 
			
		||||
 | 
			
		||||
Modify a user account (admin rights, active status, email and password).
 | 
			
		||||
 | 
			
		||||
.. cssclass:: table-bordered
 | 
			
		||||
.. list-table::
 | 
			
		||||
   :widths: 25 50
 | 
			
		||||
   :header-rows: 1
 | 
			
		||||
 | 
			
		||||
   * - Options
 | 
			
		||||
     - Description
 | 
			
		||||
   * - ``--set-admin BOOLEAN``
 | 
			
		||||
     - Add/remove admin rights (when adding admin rights, it also activates user account if not active).
 | 
			
		||||
   * - ``--activate``
 | 
			
		||||
     - Activate user account.
 | 
			
		||||
   * - ``--reset-password``
 | 
			
		||||
     - Reset user password (a new password will be displayed).
 | 
			
		||||
   * - ``--update-email EMAIL``
 | 
			
		||||
     - Update user email.
 | 
			
		||||
@@ -44,9 +44,13 @@ Workouts
 | 
			
		||||
    - average speed  (**new in 0.5.1**)
 | 
			
		||||
- User records by sports:
 | 
			
		||||
    - average speed
 | 
			
		||||
    - farest distance
 | 
			
		||||
    - farthest distance
 | 
			
		||||
    - longest duration
 | 
			
		||||
    - maximum speed
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
  Records may differ from records displayed by the application that originally generated the gpx files.
 | 
			
		||||
 | 
			
		||||
- Workouts list and filter. Only sports with workouts are displayed in sport dropdown.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
@@ -55,9 +59,17 @@ Workouts
 | 
			
		||||
 | 
			
		||||
Account & preferences
 | 
			
		||||
^^^^^^^^^^^^^^^^^^^^^
 | 
			
		||||
- A user can create, update and deleted his account
 | 
			
		||||
- A user can create, update and deleted his account.
 | 
			
		||||
- On registration, the user account is created with selected language in dropdown as user preference (*new in 0.6.9*).
 | 
			
		||||
- After registration, the user account is inactive and an email with confirmation instructions is sent to activate it.
 | 
			
		||||
  A user with an inactive account cannot log in. (*new in 0.6.0*)
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
  In case email sending is not configured, a `command line <cli.html#ftcli-users-update>`__ allows to activate users account.
 | 
			
		||||
 | 
			
		||||
- A user can set language, timezone and first day of week.
 | 
			
		||||
- A user can reset his password (*new in 0.3.0*)
 | 
			
		||||
- A user can change his email address (*new in 0.6.0*)
 | 
			
		||||
- A user can choose between metric system and imperial system for distance, elevation and speed display (*new in 0.5.0*)
 | 
			
		||||
- A user can set sport preferences (*new in 0.5.0*):
 | 
			
		||||
     - change sport color (used for sport image and charts)
 | 
			
		||||
@@ -82,15 +94,23 @@ Administration
 | 
			
		||||
  - maximum size of uploaded files
 | 
			
		||||
  - maximum size of zip archive
 | 
			
		||||
  - maximum number of files in the zip archive. If an archive contains more files, only the configured number of files is processed, without raising errors.
 | 
			
		||||
  - administrator email for contact (*new in 0.6.0*)
 | 
			
		||||
 | 
			
		||||
  .. warning::
 | 
			
		||||
      Updating server configuration may be necessary to handle large files (like `nginx <https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size>`_ for instance).
 | 
			
		||||
 | 
			
		||||
  .. note::
 | 
			
		||||
      If email sending is disabled, a warning is displayed.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
- **Users**
 | 
			
		||||
 | 
			
		||||
  - display users list and details
 | 
			
		||||
  - edit a user to add/remove administration rights
 | 
			
		||||
  - display and filter users list
 | 
			
		||||
  - edit a user to:
 | 
			
		||||
      - add/remove administration rights
 | 
			
		||||
      - activate his account (*new in 0.6.0*)
 | 
			
		||||
      - update his email (in case his account is locked) (*new in 0.6.0*)
 | 
			
		||||
      - reset his password (in case his account is locked) (*new in 0.6.0*). If email sending is disabled, it is only possible via CLI.
 | 
			
		||||
  - delete a user
 | 
			
		||||
 | 
			
		||||
- **Sports**
 | 
			
		||||
@@ -100,7 +120,9 @@ Administration
 | 
			
		||||
 | 
			
		||||
Translations
 | 
			
		||||
^^^^^^^^^^^^
 | 
			
		||||
FitTrackee is available in English and French (which can be saved in the user preferences).
 | 
			
		||||
FitTrackee is available in the following languages (which can be saved in the user preferences):
 | 
			
		||||
 | 
			
		||||
.. figure:: https://hosted.weblate.org/widgets/fittrackee/-/multi-auto.svg
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Screenshots
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,7 @@ Table of contents
 | 
			
		||||
 | 
			
		||||
   features
 | 
			
		||||
   installation
 | 
			
		||||
   cli
 | 
			
		||||
   api/index
 | 
			
		||||
   troubleshooting/index
 | 
			
		||||
   changelog
 | 
			
		||||
 
 | 
			
		||||
@@ -14,17 +14,17 @@ This application is written in Python (API) and Typescript (client):
 | 
			
		||||
    - `Leaflet <https://leafletjs.com/>`__ to display map
 | 
			
		||||
    - `Chart.js <https://www.chartjs.org/>`__ to display charts with elevation and speed
 | 
			
		||||
 | 
			
		||||
Logo, sports and weather icons are made by `Freepik <https://www.freepik.com/>`__ from `www.flaticon.com <https://www.flaticon.com/>`__.
 | 
			
		||||
| Logo, some sports and weather icons are made by `Freepik <https://www.freepik.com/>`__ from `www.flaticon.com <https://www.flaticon.com/>`__.
 | 
			
		||||
| FitTrackee also uses icons from `Fork Awesome <https://forkaweso.me>`__.
 | 
			
		||||
 | 
			
		||||
Prerequisites
 | 
			
		||||
~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
-  PostgreSQL database (10+)
 | 
			
		||||
-  Redis for task queue
 | 
			
		||||
-  Python 3.7+
 | 
			
		||||
-  PostgreSQL database (10+)
 | 
			
		||||
-  SMTP provider and Redis for task queue (if email sending is enabled)
 | 
			
		||||
-  API key from `Dark Sky <https://darksky.net/dev>`__ (not mandatory)
 | 
			
		||||
-  `Poetry <https://poetry.eustace.io>`__ (for installation from sources only)
 | 
			
		||||
-  API key from `Dark Sky <https://darksky.net/dev>`__ [not mandatory]
 | 
			
		||||
-  SMTP provider
 | 
			
		||||
-  `Yarn <https://yarnpkg.com>`__ (for development only)
 | 
			
		||||
-  Docker and Docker Compose (for development or evaluation purposes)
 | 
			
		||||
 | 
			
		||||
@@ -95,9 +95,9 @@ deployment method.
 | 
			
		||||
 | 
			
		||||
    .. versionadded:: 0.4.0
 | 
			
		||||
 | 
			
		||||
    Directory containing uploaded files.
 | 
			
		||||
    **Absolute path** to the directory where `uploads` folder will be created.
 | 
			
		||||
 | 
			
		||||
    :default: `fittrackee/uploads/`
 | 
			
		||||
    :default: `<application_directory>/fittrackee`
 | 
			
		||||
 | 
			
		||||
    .. danger::
 | 
			
		||||
        | With installation from PyPI, the directory will be located in
 | 
			
		||||
@@ -108,7 +108,7 @@ deployment method.
 | 
			
		||||
    | Database URL with username and password, must be initialized in production environment.
 | 
			
		||||
    | For example in dev environment : ``postgresql://fittrackee:fittrackee@localhost:5432/fittrackee``
 | 
			
		||||
 | 
			
		||||
    .. danger::
 | 
			
		||||
    .. warning::
 | 
			
		||||
        | Since `SQLAlchemy update (1.4+) <https://docs.sqlalchemy.org/en/14/changelog/changelog_14.html#change-3687655465c25a39b968b4f5f6e9170b>`__,
 | 
			
		||||
          engine URL should begin with `postgresql://`.
 | 
			
		||||
 | 
			
		||||
@@ -132,6 +132,13 @@ deployment method.
 | 
			
		||||
 | 
			
		||||
    Email URL with credentials, see `Emails <installation.html#emails>`__.
 | 
			
		||||
 | 
			
		||||
    .. versionchanged:: 0.6.5
 | 
			
		||||
 | 
			
		||||
    :default: empty string
 | 
			
		||||
 | 
			
		||||
    .. danger::
 | 
			
		||||
        If the email URL is empty, email sending will be disabled.
 | 
			
		||||
 | 
			
		||||
    .. warning::
 | 
			
		||||
        If the email URL is invalid, the application may not start.
 | 
			
		||||
 | 
			
		||||
@@ -168,6 +175,16 @@ deployment method.
 | 
			
		||||
    :default: `https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. envvar:: STATICMAP_SUBDOMAINS 🆕
 | 
			
		||||
 | 
			
		||||
    .. versionadded:: 0.6.10
 | 
			
		||||
 | 
			
		||||
    | Some tile servers require a subdomain, see `Map tile server <installation.html#map-tile-server>`__.
 | 
			
		||||
    | For instance: "a,b,c" for OSM France.
 | 
			
		||||
 | 
			
		||||
    :default: empty string
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. envvar:: MAP_ATTRIBUTION
 | 
			
		||||
 | 
			
		||||
    .. versionadded:: 0.4.0
 | 
			
		||||
@@ -177,11 +194,17 @@ deployment method.
 | 
			
		||||
    :default: `© <a href="http://www.openstreetmap.org/copyright" target="_blank" rel="noopener noreferrer">OpenStreetMap</a> contributors`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. envvar:: DEFAULT_STATICMAP 🆕
 | 
			
		||||
.. envvar:: DEFAULT_STATICMAP
 | 
			
		||||
 | 
			
		||||
    .. versionadded:: 0.4.9
 | 
			
		||||
 | 
			
		||||
    If `True`, it keeps using default tile server to generate static maps.
 | 
			
		||||
    | If `True`, it keeps using default tile server to generate static maps (Komoot.de tile server).
 | 
			
		||||
    | Otherwise, it uses the tile server set in `TILE_SERVER_URL <installation.html#envvar-TILE_SERVER_URL>`__.
 | 
			
		||||
 | 
			
		||||
    .. versionchanged:: 0.6.10
 | 
			
		||||
 | 
			
		||||
    | This variable is now case-insensitive.
 | 
			
		||||
    | If `False`, depending on tile server, `subdomains <installation.html#envvar-STATICMAP_SUBDOMAINS>`__ may be mandatory.
 | 
			
		||||
 | 
			
		||||
    :default: False
 | 
			
		||||
 | 
			
		||||
@@ -209,12 +232,31 @@ To send emails, a valid ``EMAIL_URL`` must be provided:
 | 
			
		||||
- with SSL: ``smtp://username:password@smtp.example.com:465/?ssl=True``
 | 
			
		||||
- with STARTTLS: ``smtp://username:password@smtp.example.com:587/?tls=True``
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
    | - If the email URL is invalid, the application may not start.
 | 
			
		||||
    | - Sending emails with Office365 may not work if SMTP auth is disabled.
 | 
			
		||||
 | 
			
		||||
.. versionadded:: 0.5.3
 | 
			
		||||
.. versionchanged:: 0.5.3
 | 
			
		||||
 | 
			
		||||
| Credentials can be omitted: ``smtp://smtp.example.com:25``.
 | 
			
		||||
| If ``:<port>`` is omitted, the port defaults to 25.
 | 
			
		||||
 | 
			
		||||
.. warning::
 | 
			
		||||
     | Since 0.6.0, newly created accounts must be confirmed (an email with confirmation instructions is sent after registration).
 | 
			
		||||
 | 
			
		||||
Emails sent by FitTrackee are:
 | 
			
		||||
 | 
			
		||||
- account confirmation instructions
 | 
			
		||||
- password reset request
 | 
			
		||||
- email change (to old and new email adresses)
 | 
			
		||||
- password change
 | 
			
		||||
 | 
			
		||||
.. versionchanged:: 0.6.5
 | 
			
		||||
 | 
			
		||||
| For single-user instance, it is possible to disable email sending with an empty ``EMAIL_URL`` (in this case, no need to start dramatiq workers).
 | 
			
		||||
| A `CLI <cli.html#ftcli-users-update>`__ is available to activate account and modify email and password.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Map tile server
 | 
			
		||||
^^^^^^^^^^^^^^^
 | 
			
		||||
.. versionadded:: 0.4.0
 | 
			
		||||
@@ -230,6 +272,20 @@ To keep using **ThunderForest Outdoors**, the configuration is:
 | 
			
		||||
.. note::
 | 
			
		||||
    | Check the terms of service of tile provider for map attribution
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. versionchanged:: 0.6.10
 | 
			
		||||
 | 
			
		||||
Since the tile server can be used for static map generation, some servers require a subdomain.
 | 
			
		||||
 | 
			
		||||
For instance, to set OSM France tile server, the expected values are:
 | 
			
		||||
 | 
			
		||||
- ``TILE_SERVER_URL=https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png``
 | 
			
		||||
- ``MAP_ATTRIBUTION='fond de carte par <a href="http://www.openstreetmap.fr/mentions-legales/" target="_blank" rel="nofollow noopener">OpenStreetMap France</a>, sous <a href="http://creativecommons.org/licenses/by-sa/2.0/fr/" target="_blank" rel="nofollow noopener">licence CC BY-SA</a>'``
 | 
			
		||||
- ``STATICMAP_SUBDOMAINS=a,b,c``
 | 
			
		||||
 | 
			
		||||
The subdomain will be chosen randomly.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Installation
 | 
			
		||||
~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
@@ -273,7 +329,7 @@ For instance, copy and update ``.env`` file from ``.env.example`` and source the
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
    $ fittrackee_upgrade_db
 | 
			
		||||
    $ ftcli db upgrade
 | 
			
		||||
 | 
			
		||||
- Start the application
 | 
			
		||||
 | 
			
		||||
@@ -281,7 +337,7 @@ For instance, copy and update ``.env`` file from ``.env.example`` and source the
 | 
			
		||||
 | 
			
		||||
    $ fittrackee
 | 
			
		||||
 | 
			
		||||
- Start task queue workers
 | 
			
		||||
- Start task queue workers if email sending is enabled.
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
@@ -292,12 +348,14 @@ For instance, copy and update ``.env`` file from ``.env.example`` and source the
 | 
			
		||||
 | 
			
		||||
- Open http://localhost:3000 and register
 | 
			
		||||
 | 
			
		||||
- To set admin rights to the newly created account, use the following command:
 | 
			
		||||
- To set admin rights to the newly created account, use the following command line:
 | 
			
		||||
 | 
			
		||||
.. code:: bash
 | 
			
		||||
 | 
			
		||||
   $ fittrackee_set_admin <username>
 | 
			
		||||
   $ ftcli users update <username> --set-admin true
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
    If the user account is inactive, it activates it.
 | 
			
		||||
 | 
			
		||||
From sources
 | 
			
		||||
^^^^^^^^^^^^
 | 
			
		||||
@@ -352,12 +410,14 @@ Dev environment
 | 
			
		||||
 | 
			
		||||
- Open http://localhost:3000 and register
 | 
			
		||||
 | 
			
		||||
- To set admin rights to the newly created account, use the following command:
 | 
			
		||||
- To set admin rights to the newly created account, use the following command line:
 | 
			
		||||
 | 
			
		||||
.. code:: bash
 | 
			
		||||
 | 
			
		||||
   $ make set-admin USERNAME=<username>
 | 
			
		||||
   $ make user-set-admin USERNAME=<username>
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
    If the user account is inactive, it activates it.
 | 
			
		||||
 | 
			
		||||
Production environment
 | 
			
		||||
""""""""""""""""""""""
 | 
			
		||||
@@ -365,13 +425,13 @@ Production environment
 | 
			
		||||
.. warning::
 | 
			
		||||
    | Note that FitTrackee is under heavy development, some features may be unstable.
 | 
			
		||||
 | 
			
		||||
-  Download the last release (for now, it is the release v0.5.7):
 | 
			
		||||
-  Download the last release (for now, it is the release v0.6.10):
 | 
			
		||||
 | 
			
		||||
.. code:: bash
 | 
			
		||||
 | 
			
		||||
   $ wget https://github.com/SamR1/FitTrackee/archive/v0.5.7.tar.gz
 | 
			
		||||
   $ tar -xzf v0.5.7.tar.gz
 | 
			
		||||
   $ mv FitTrackee-0.5.7 FitTrackee
 | 
			
		||||
   $ wget https://github.com/SamR1/FitTrackee/archive/v0.6.10.tar.gz
 | 
			
		||||
   $ tar -xzf v0.6.10.tar.gz
 | 
			
		||||
   $ mv FitTrackee-0.6.10 FitTrackee
 | 
			
		||||
   $ cd FitTrackee
 | 
			
		||||
 | 
			
		||||
-  Create **.env** from example and update it
 | 
			
		||||
@@ -396,14 +456,19 @@ Production environment
 | 
			
		||||
 | 
			
		||||
   $ make run
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
    If email sending is disabled: ``$ make run-server``
 | 
			
		||||
 | 
			
		||||
- Open http://localhost:5000 and register
 | 
			
		||||
 | 
			
		||||
- To set admin rights to the newly created account, use the following command:
 | 
			
		||||
- To set admin rights to the newly created account, use the following command line:
 | 
			
		||||
 | 
			
		||||
.. code:: bash
 | 
			
		||||
 | 
			
		||||
   $ make set-admin USERNAME=<username>
 | 
			
		||||
   $ make user-set-admin USERNAME=<username>
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
    If the user account is inactive, it activates it.
 | 
			
		||||
 | 
			
		||||
Upgrade
 | 
			
		||||
~~~~~~~
 | 
			
		||||
@@ -417,7 +482,7 @@ Upgrade
 | 
			
		||||
From PyPI
 | 
			
		||||
^^^^^^^^^
 | 
			
		||||
 | 
			
		||||
- Activate the virtualenv
 | 
			
		||||
- Stop the application and activate the virtualenv
 | 
			
		||||
 | 
			
		||||
- Upgrade with pip
 | 
			
		||||
 | 
			
		||||
@@ -436,10 +501,9 @@ From PyPI
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 | 
			
		||||
    $ fittrackee_upgrade_db
 | 
			
		||||
    $ ftcli db upgrade
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
- Restart the application and task queue workers.
 | 
			
		||||
- Restart the application and task queue workers (if email sending is enabled).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
From sources
 | 
			
		||||
@@ -487,13 +551,13 @@ Prod environment
 | 
			
		||||
 | 
			
		||||
- Change to the directory where FitTrackee directory is located
 | 
			
		||||
 | 
			
		||||
- Download the last release (for now, it is the release v0.5.7) and overwrite existing files:
 | 
			
		||||
- Download the last release (for now, it is the release v0.6.10) and overwrite existing files:
 | 
			
		||||
 | 
			
		||||
.. code:: bash
 | 
			
		||||
 | 
			
		||||
   $ wget https://github.com/SamR1/FitTrackee/archive/v0.5.7.tar.gz
 | 
			
		||||
   $ tar -xzf v0.5.7.tar.gz
 | 
			
		||||
   $ cp -R FitTrackee-0.5.7/* FitTrackee/
 | 
			
		||||
   $ wget https://github.com/SamR1/FitTrackee/archive/v0.6.10.tar.gz
 | 
			
		||||
   $ tar -xzf v0.6.10.tar.gz
 | 
			
		||||
   $ cp -R FitTrackee-0.6.10/* FitTrackee/
 | 
			
		||||
   $ cd FitTrackee
 | 
			
		||||
 | 
			
		||||
- Update **.env** if needed (see `Environment variables <installation.html#environment-variables>`__).
 | 
			
		||||
@@ -516,6 +580,8 @@ Prod environment
 | 
			
		||||
 | 
			
		||||
   $ make run
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
    If email sending is disabled: ``$ make run-server``
 | 
			
		||||
 | 
			
		||||
Deployment
 | 
			
		||||
~~~~~~~~~~
 | 
			
		||||
@@ -554,6 +620,7 @@ Examples (to update depending on your application configuration and given distri
 | 
			
		||||
    Environment="SENDER_EMAIL="
 | 
			
		||||
    Environment="REDIS_URL="
 | 
			
		||||
    Environment="TILE_SERVER_URL="
 | 
			
		||||
    Environment="STATICMAP_SUBDOMAINS="
 | 
			
		||||
    Environment="MAP_ATTRIBUTION="
 | 
			
		||||
    Environment="WEATHER_API_KEY="
 | 
			
		||||
    WorkingDirectory=/home/<USER>/<FITTRACKEE DIRECTORY>
 | 
			
		||||
@@ -641,8 +708,7 @@ Installation
 | 
			
		||||
 | 
			
		||||
.. versionadded:: 0.4.4
 | 
			
		||||
 | 
			
		||||
For evaluation purposes , docker files are available,
 | 
			
		||||
installing **FitTrackee** from **sources**.
 | 
			
		||||
For evaluation purposes, docker files are available, installing **FitTrackee** from **sources**.
 | 
			
		||||
 | 
			
		||||
- To install **FitTrackee** with database initialisation and run the application and dramatiq workers:
 | 
			
		||||
 | 
			
		||||
@@ -650,18 +716,22 @@ installing **FitTrackee** from **sources**.
 | 
			
		||||
 | 
			
		||||
    $ git clone https://github.com/SamR1/FitTrackee.git
 | 
			
		||||
    $ cd FitTrackee
 | 
			
		||||
    $ cp .env.docker .env
 | 
			
		||||
    $ make docker-build docker-run docker-init
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
- To set admin rights to the newly created account, use the following command line:
 | 
			
		||||
 | 
			
		||||
.. code:: bash
 | 
			
		||||
 | 
			
		||||
   $ make docker-set-admin USERNAME=<username>
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
    If the user account is inactive, it activates it.
 | 
			
		||||
 | 
			
		||||
- To stop **Fittrackee**:
 | 
			
		||||
 | 
			
		||||
.. code-block:: bash
 | 
			
		||||
 
 | 
			
		||||
@@ -5,10 +5,24 @@ Administrator
 | 
			
		||||
`FitTrackee fails to start`
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
- Check the database URL in `Environment variables <../installation.html#envvar-DATABASE_URL>`__ if the following error is displayed in **gunicorn** logs:
 | 
			
		||||
- Check the database URL in `environment variables <../installation.html#envvar-DATABASE_URL>`__ if the following error is displayed in **gunicorn** logs:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
  .. code::
 | 
			
		||||
 | 
			
		||||
   sqlalchemy.exc.NoSuchModuleError: Can't load plugin: sqlalchemy.dialects:postgres
 | 
			
		||||
     sqlalchemy.exc.NoSuchModuleError: Can't load plugin: sqlalchemy.dialects:postgres
 | 
			
		||||
 | 
			
		||||
It must start with `postgresql://` (engine URLs starting with `postgres://` are no longer supported).
 | 
			
		||||
  It must start with `postgresql://` (engine URLs starting with `postgres://` are no longer supported).
 | 
			
		||||
 | 
			
		||||
- Check the email URL in `environment variables <../installation.html#envvar-EMAIL_URL>`__ if the following error is displayed in **gunicorn** logs:
 | 
			
		||||
 | 
			
		||||
  .. code::
 | 
			
		||||
 | 
			
		||||
     fittrackee.emails.exceptions.InvalidEmailUrlScheme
 | 
			
		||||
 | 
			
		||||
  A valid ``EMAIL_URL`` must be provided (see `emails <../installation.html#emails>`__).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
`Map images are not displayed but map is shown in Workout detail`
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
- Check the path in `environment variables <../installation.html#envvar-UPLOAD_FOLDER>`__. ``UPLOAD_FOLDER`` must be set with an absolute path.
 | 
			
		||||
@@ -3,7 +3,6 @@ Troubleshooting
 | 
			
		||||
 | 
			
		||||
.. toctree::
 | 
			
		||||
   :maxdepth: 2
 | 
			
		||||
   :caption: Endpoints:
 | 
			
		||||
 | 
			
		||||
   administrator
 | 
			
		||||
   user
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
from selenium.webdriver.common.by import By
 | 
			
		||||
 | 
			
		||||
from .utils import TEST_URL
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -9,7 +11,7 @@ class TestIndex:
 | 
			
		||||
    def test_navbar_contains_all_links(self, selenium):
 | 
			
		||||
        selenium.get(TEST_URL)
 | 
			
		||||
 | 
			
		||||
        nav = selenium.find_element_by_id('nav').text
 | 
			
		||||
        nav = selenium.find_element(By.ID, 'nav').text
 | 
			
		||||
        assert "FitTrackee" in nav
 | 
			
		||||
        assert "Login" in nav
 | 
			
		||||
        assert "Register" in nav
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,6 @@
 | 
			
		||||
from .utils import (
 | 
			
		||||
    TEST_URL,
 | 
			
		||||
    assert_navbar,
 | 
			
		||||
    login_valid_user,
 | 
			
		||||
    register_valid_user_and_logout,
 | 
			
		||||
)
 | 
			
		||||
from selenium.webdriver.common.by import By
 | 
			
		||||
 | 
			
		||||
from .utils import TEST_URL, login_valid_user, register_valid_user_and_logout
 | 
			
		||||
 | 
			
		||||
URL = f'{TEST_URL}/login'
 | 
			
		||||
 | 
			
		||||
@@ -12,32 +9,42 @@ class TestLogin:
 | 
			
		||||
    def test_navbar_contains_login(self, selenium):
 | 
			
		||||
        selenium.get(URL)
 | 
			
		||||
 | 
			
		||||
        nav = selenium.find_element_by_id('nav').text
 | 
			
		||||
        nav = selenium.find_element(By.ID, 'nav').text
 | 
			
		||||
        assert 'Login' in nav
 | 
			
		||||
 | 
			
		||||
    def test_it_displays_login_form(self, selenium):
 | 
			
		||||
        selenium.get(URL)
 | 
			
		||||
 | 
			
		||||
        inputs = selenium.find_elements_by_tag_name('input')
 | 
			
		||||
        inputs = selenium.find_elements(By.TAG_NAME, 'input')
 | 
			
		||||
        assert len(inputs) == 2
 | 
			
		||||
        assert inputs[0].get_attribute('id') == 'email'
 | 
			
		||||
        assert inputs[0].get_attribute('type') == 'email'
 | 
			
		||||
        assert inputs[1].get_attribute('id') == 'password'
 | 
			
		||||
        assert inputs[1].get_attribute('type') == 'password'
 | 
			
		||||
 | 
			
		||||
        button = selenium.find_element_by_tag_name('button')
 | 
			
		||||
        button = selenium.find_element(By.TAG_NAME, 'button')
 | 
			
		||||
        assert button.get_attribute('type') == 'submit'
 | 
			
		||||
        assert 'Log in' in button.text
 | 
			
		||||
 | 
			
		||||
        links = selenium.find_elements_by_class_name('links')
 | 
			
		||||
        links = selenium.find_elements(By.CLASS_NAME, 'links')
 | 
			
		||||
        assert links[0].tag_name == 'a'
 | 
			
		||||
        assert 'Register' in links[0].text
 | 
			
		||||
        assert links[1].tag_name == 'a'
 | 
			
		||||
        assert 'Forgot password?' in links[1].text
 | 
			
		||||
        assert links[2].tag_name == 'a'
 | 
			
		||||
        assert "Didn't received instructions?" in links[2].text
 | 
			
		||||
 | 
			
		||||
    def test_user_can_log_in(self, selenium):
 | 
			
		||||
        user = register_valid_user_and_logout(selenium)
 | 
			
		||||
 | 
			
		||||
        login_valid_user(selenium, user)
 | 
			
		||||
 | 
			
		||||
        assert_navbar(selenium, user)
 | 
			
		||||
        nav = selenium.find_element(By.ID, 'nav').text
 | 
			
		||||
        assert 'Register' not in nav
 | 
			
		||||
        assert 'Login' not in nav
 | 
			
		||||
        assert 'Dashboard' in nav
 | 
			
		||||
        assert 'Workouts' in nav
 | 
			
		||||
        assert 'Statistics' in nav
 | 
			
		||||
        assert 'Add a workout' in nav
 | 
			
		||||
        assert user['username'] in nav
 | 
			
		||||
        assert 'Logout' in nav
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,18 @@
 | 
			
		||||
from selenium.webdriver.common.by import By
 | 
			
		||||
 | 
			
		||||
from .utils import register_valid_user
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestLogout:
 | 
			
		||||
    def test_user_can_log_out(self, selenium):
 | 
			
		||||
        user = register_valid_user(selenium)
 | 
			
		||||
        user_menu = selenium.find_element_by_class_name('nav-items-user-menu')
 | 
			
		||||
        logout_link = user_menu.find_elements_by_class_name('nav-item')[2]
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
        nav = selenium.find_element_by_id('nav').text
 | 
			
		||||
        nav = selenium.find_element(By.ID, 'nav').text
 | 
			
		||||
        assert 'Register' in nav
 | 
			
		||||
        assert 'Login' in nav
 | 
			
		||||
        assert user['username'] not in nav
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +1,24 @@
 | 
			
		||||
from .utils import TEST_URL, register_valid_user
 | 
			
		||||
from selenium.webdriver.common.by import By
 | 
			
		||||
 | 
			
		||||
URL = f'{TEST_URL}/profile'
 | 
			
		||||
from .utils import register_valid_user
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestProfile:
 | 
			
		||||
    def test_it_displays_user_profile(self, selenium):
 | 
			
		||||
        user = register_valid_user(selenium)
 | 
			
		||||
 | 
			
		||||
        selenium.get(URL)
 | 
			
		||||
        user_header = selenium.find_element_by_class_name('user-header')
 | 
			
		||||
        app_menu = selenium.find_element(By.CLASS_NAME, 'nav-items-user-menu')
 | 
			
		||||
        profile_link = app_menu.find_elements(By.CLASS_NAME, 'nav-item')[1]
 | 
			
		||||
        profile_link.click()
 | 
			
		||||
        selenium.implicitly_wait(1)
 | 
			
		||||
 | 
			
		||||
        user_header = selenium.find_element(By.CLASS_NAME, 'user-header')
 | 
			
		||||
        assert user['username'] in user_header.text
 | 
			
		||||
        assert '0\nworkouts' in user_header.text
 | 
			
		||||
        assert '0\nkm' in user_header.text
 | 
			
		||||
        assert '0\nsports' in user_header.text
 | 
			
		||||
 | 
			
		||||
        user_infos = selenium.find_element_by_id('user-infos')
 | 
			
		||||
        user_infos = selenium.find_element(By.ID, 'user-infos')
 | 
			
		||||
        assert 'Registration date' in user_infos.text
 | 
			
		||||
        assert 'First name' in user_infos.text
 | 
			
		||||
        assert 'Last name' in user_infos.text
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
from selenium.webdriver.common.by import By
 | 
			
		||||
 | 
			
		||||
from .utils import (
 | 
			
		||||
    TEST_URL,
 | 
			
		||||
    assert_navbar,
 | 
			
		||||
    random_string,
 | 
			
		||||
    register,
 | 
			
		||||
    register_valid_user,
 | 
			
		||||
    register_valid_user_and_logout,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -15,7 +15,7 @@ class TestRegistration:
 | 
			
		||||
        selenium.get(URL)
 | 
			
		||||
        selenium.implicitly_wait(1)
 | 
			
		||||
 | 
			
		||||
        inputs = selenium.find_elements_by_tag_name('input')
 | 
			
		||||
        inputs = selenium.find_elements(By.TAG_NAME, 'input')
 | 
			
		||||
        assert len(inputs) == 4
 | 
			
		||||
        assert inputs[0].get_attribute('id') == 'username'
 | 
			
		||||
        assert inputs[0].get_attribute('type') == 'text'
 | 
			
		||||
@@ -23,21 +23,40 @@ class TestRegistration:
 | 
			
		||||
        assert inputs[1].get_attribute('type') == 'email'
 | 
			
		||||
        assert inputs[2].get_attribute('id') == 'password'
 | 
			
		||||
        assert inputs[2].get_attribute('type') == 'password'
 | 
			
		||||
        assert inputs[3].get_attribute('id') == 'confirm-password'
 | 
			
		||||
        assert inputs[3].get_attribute('type') == 'password'
 | 
			
		||||
 | 
			
		||||
        button = selenium.find_element_by_tag_name('button')
 | 
			
		||||
        form_infos = selenium.find_elements(By.CLASS_NAME, 'form-info')
 | 
			
		||||
        assert len(form_infos) == 3
 | 
			
		||||
        assert form_infos[0].text == (
 | 
			
		||||
            '3 to 30 characters required, only alphanumeric characters and '
 | 
			
		||||
            'the underscore character "_" allowed.'
 | 
			
		||||
        )
 | 
			
		||||
        assert form_infos[1].text == 'Enter a valid email address.'
 | 
			
		||||
        assert form_infos[2].text == 'At least 8 characters required.'
 | 
			
		||||
 | 
			
		||||
        button = selenium.find_element(By.TAG_NAME, 'button')
 | 
			
		||||
        assert button.get_attribute('type') == 'submit'
 | 
			
		||||
        assert 'Register' in button.text
 | 
			
		||||
 | 
			
		||||
        link = selenium.find_element_by_class_name('links')
 | 
			
		||||
        assert link.tag_name == 'a'
 | 
			
		||||
        assert 'Login' in link.text
 | 
			
		||||
        links = selenium.find_elements(By.CLASS_NAME, 'links')
 | 
			
		||||
        assert links[0].tag_name == 'a'
 | 
			
		||||
        assert 'Login' in links[0].text
 | 
			
		||||
        assert links[1].tag_name == 'a'
 | 
			
		||||
        assert "Didn't received instructions?" in links[1].text
 | 
			
		||||
 | 
			
		||||
    def test_user_can_register(self, selenium):
 | 
			
		||||
        user = register_valid_user(selenium)
 | 
			
		||||
        user = {
 | 
			
		||||
            'username': random_string(),
 | 
			
		||||
            'email': f'{random_string()}@example.com',
 | 
			
		||||
            'password': 'p@ssw0rd',
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        assert_navbar(selenium, user)
 | 
			
		||||
        register(selenium, user)
 | 
			
		||||
 | 
			
		||||
        message = selenium.find_element(By.CLASS_NAME, 'success-message').text
 | 
			
		||||
        assert (
 | 
			
		||||
            'A link to activate your account has been '
 | 
			
		||||
            'emailed to the address provided.'
 | 
			
		||||
        ) in message
 | 
			
		||||
 | 
			
		||||
    def test_user_can_not_register_with_invalid_email(self, selenium):
 | 
			
		||||
        user_name = random_string()
 | 
			
		||||
@@ -45,13 +64,12 @@ class TestRegistration:
 | 
			
		||||
            'username': user_name,
 | 
			
		||||
            'email': user_name,
 | 
			
		||||
            'password': 'p@ssw0rd',
 | 
			
		||||
            'password_conf': 'p@ssw0rd',
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        register(selenium, user_infos)
 | 
			
		||||
 | 
			
		||||
        assert selenium.current_url == URL
 | 
			
		||||
        nav = selenium.find_element_by_id('nav').text
 | 
			
		||||
        nav = selenium.find_element(By.ID, 'nav').text
 | 
			
		||||
        assert 'Register' in nav
 | 
			
		||||
        assert 'Login' in nav
 | 
			
		||||
 | 
			
		||||
@@ -64,30 +82,20 @@ class TestRegistration:
 | 
			
		||||
        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
 | 
			
		||||
        errors = selenium.find_element(By.CLASS_NAME, 'error-message').text
 | 
			
		||||
        assert 'Sorry, that username is already taken.' in errors
 | 
			
		||||
 | 
			
		||||
    def test_user_can_not_register_if_email_is_already_taken(self, selenium):
 | 
			
		||||
    def test_user_does_not_return_error_if_email_is_already_taken(
 | 
			
		||||
        self, selenium
 | 
			
		||||
    ):
 | 
			
		||||
        user = register_valid_user_and_logout(selenium)
 | 
			
		||||
        user['username'] = random_string()
 | 
			
		||||
 | 
			
		||||
        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_it_displays_error_if_passwords_do_not_match(self, selenium):
 | 
			
		||||
        user_name = random_string()
 | 
			
		||||
        user_infos = {
 | 
			
		||||
            'username': user_name,
 | 
			
		||||
            'email': f'{user_name}@example.com',
 | 
			
		||||
            'password': 'p@ssw0rd',
 | 
			
		||||
            'password_conf': 'password',
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        register(selenium, user_infos)
 | 
			
		||||
 | 
			
		||||
        assert selenium.current_url == URL
 | 
			
		||||
        errors = selenium.find_element_by_class_name('error-message').text
 | 
			
		||||
        assert 'password and password confirmation don\'t match' in errors
 | 
			
		||||
        assert selenium.current_url == f'{TEST_URL}/login'
 | 
			
		||||
        message = selenium.find_element(By.CLASS_NAME, 'success-message').text
 | 
			
		||||
        assert (
 | 
			
		||||
            'A link to activate your account has been '
 | 
			
		||||
            'emailed to the address provided.'
 | 
			
		||||
        ) in message
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
from selenium.webdriver.common.by import By
 | 
			
		||||
from selenium.webdriver.support import expected_conditions as EC
 | 
			
		||||
from selenium.webdriver.support.ui import Select, WebDriverWait
 | 
			
		||||
 | 
			
		||||
@@ -7,36 +8,36 @@ from .utils import TEST_URL, register_valid_user
 | 
			
		||||
class TestWorkout:
 | 
			
		||||
    def test_user_can_add_workout_without_gpx(self, selenium):
 | 
			
		||||
        register_valid_user(selenium)
 | 
			
		||||
        app_menu = selenium.find_element_by_class_name('nav-items-app-menu')
 | 
			
		||||
        add_workout_link = app_menu.find_elements_by_class_name('nav-item')[3]
 | 
			
		||||
        app_menu = selenium.find_element(By.CLASS_NAME, 'nav-items-app-menu')
 | 
			
		||||
        add_workout_link = app_menu.find_elements(By.CLASS_NAME, 'nav-item')[3]
 | 
			
		||||
 | 
			
		||||
        add_workout_link.click()
 | 
			
		||||
        selenium.implicitly_wait(1)
 | 
			
		||||
        radio_button = selenium.find_element_by_id('withoutGpx')
 | 
			
		||||
        radio_button = selenium.find_element(By.ID, 'withoutGpx')
 | 
			
		||||
        radio_button.click()
 | 
			
		||||
 | 
			
		||||
        select = Select(selenium.find_element_by_id('sport'))
 | 
			
		||||
        select = Select(selenium.find_element(By.ID, 'sport'))
 | 
			
		||||
        select.select_by_index(1)
 | 
			
		||||
        selenium.find_element_by_name('title').send_keys('Workout title')
 | 
			
		||||
        selenium.find_element_by_name('workout-date').send_keys('2018-12-20')
 | 
			
		||||
        selenium.find_element_by_name('workout-time').send_keys('14:05')
 | 
			
		||||
        selenium.find_element_by_name('workout-duration-hour').send_keys('01')
 | 
			
		||||
        selenium.find_element_by_name('workout-duration-minutes').send_keys(
 | 
			
		||||
        selenium.find_element(By.NAME, 'title').send_keys('Workout title')
 | 
			
		||||
        selenium.find_element(By.NAME, 'workout-date').send_keys('2018-12-20')
 | 
			
		||||
        selenium.find_element(By.NAME, 'workout-time').send_keys('14:05')
 | 
			
		||||
        selenium.find_element(By.NAME, 'workout-duration-hour').send_keys('01')
 | 
			
		||||
        selenium.find_element(By.NAME, 'workout-duration-minutes').send_keys(
 | 
			
		||||
            '00'
 | 
			
		||||
        )
 | 
			
		||||
        selenium.find_element_by_name('workout-duration-seconds').send_keys(
 | 
			
		||||
        selenium.find_element(By.NAME, 'workout-duration-seconds').send_keys(
 | 
			
		||||
            '00'
 | 
			
		||||
        )
 | 
			
		||||
        selenium.find_element_by_name('workout-distance').send_keys('10')
 | 
			
		||||
        selenium.find_element(By.NAME, 'workout-distance').send_keys('10')
 | 
			
		||||
 | 
			
		||||
        confirm_button = selenium.find_element_by_class_name('confirm')
 | 
			
		||||
        confirm_button = selenium.find_element(By.CLASS_NAME, 'confirm')
 | 
			
		||||
        confirm_button.click()
 | 
			
		||||
 | 
			
		||||
        WebDriverWait(selenium, 10).until(
 | 
			
		||||
            EC.url_changes(f"{TEST_URL}/workouts/add")
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        workout_details = selenium.find_element_by_id('workout-info').text
 | 
			
		||||
        workout_details = selenium.find_element(By.ID, 'workout-info').text
 | 
			
		||||
        assert 'Duration: 1:00:00' in workout_details
 | 
			
		||||
        assert 'Distance: 10 km' in workout_details
 | 
			
		||||
        assert 'Average Speed: 10 km/h' in workout_details
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										72
									
								
								e2e/utils.py
									
									
									
									
									
								
							
							
						
						@@ -1,43 +1,49 @@
 | 
			
		||||
import os
 | 
			
		||||
import random
 | 
			
		||||
import re
 | 
			
		||||
import string
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
import requests
 | 
			
		||||
from selenium.webdriver.common.by import By
 | 
			
		||||
from selenium.webdriver.support import expected_conditions as EC
 | 
			
		||||
from selenium.webdriver.support.ui import WebDriverWait
 | 
			
		||||
from urllib3.util import parse_url
 | 
			
		||||
 | 
			
		||||
TEST_APP_URL = os.getenv('TEST_APP_URL')
 | 
			
		||||
TEST_CLIENT_URL = os.getenv('TEST_CLIENT_URL')
 | 
			
		||||
E2E_ARGS = os.getenv('E2E_ARGS')
 | 
			
		||||
TEST_URL = TEST_CLIENT_URL if E2E_ARGS == 'client' else TEST_APP_URL
 | 
			
		||||
EMAIL_URL = os.getenv('EMAIL_URL', 'smtp://none:none@0.0.0.0:1025')
 | 
			
		||||
parsed_email_url = parse_url(EMAIL_URL)
 | 
			
		||||
EMAIL_API_URL = f'http://{parsed_email_url.host}:8025'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def random_string(length=8):
 | 
			
		||||
    return ''.join(random.choice(string.ascii_letters) for x in range(length))
 | 
			
		||||
    return ''.join(random.choice(string.ascii_letters) for _ in range(length))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def register(selenium, user):
 | 
			
		||||
    selenium.get(f'{TEST_URL}/register')
 | 
			
		||||
    selenium.implicitly_wait(1)
 | 
			
		||||
    username = selenium.find_element_by_id('username')
 | 
			
		||||
    username = selenium.find_element(By.ID, 'username')
 | 
			
		||||
    username.send_keys(user.get('username'))
 | 
			
		||||
    email = selenium.find_element_by_id('email')
 | 
			
		||||
    email = selenium.find_element(By.ID, 'email')
 | 
			
		||||
    email.send_keys(user.get('email'))
 | 
			
		||||
    password = selenium.find_element_by_id('password')
 | 
			
		||||
    password = selenium.find_element(By.ID, 'password')
 | 
			
		||||
    password.send_keys(user.get('password'))
 | 
			
		||||
    password_conf = selenium.find_element_by_id('confirm-password')
 | 
			
		||||
    password_conf.send_keys(user.get('password_conf'))
 | 
			
		||||
    submit_button = selenium.find_element_by_tag_name('button')
 | 
			
		||||
    submit_button = selenium.find_element(By.TAG_NAME, 'button')
 | 
			
		||||
    submit_button.click()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def login(selenium, user):
 | 
			
		||||
    selenium.get(f'{TEST_URL}/login')
 | 
			
		||||
    selenium.implicitly_wait(1)
 | 
			
		||||
    email = selenium.find_element_by_id('email')
 | 
			
		||||
    email = selenium.find_element(By.ID, 'email')
 | 
			
		||||
    email.send_keys(user.get('email'))
 | 
			
		||||
    password = selenium.find_element_by_id('password')
 | 
			
		||||
    password = selenium.find_element(By.ID, 'password')
 | 
			
		||||
    password.send_keys(user.get('password'))
 | 
			
		||||
    submit_button = selenium.find_element_by_tag_name('button')
 | 
			
		||||
    submit_button = selenium.find_element(By.TAG_NAME, 'button')
 | 
			
		||||
    submit_button.click()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -47,44 +53,36 @@ def register_valid_user(selenium):
 | 
			
		||||
        '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"))
 | 
			
		||||
    WebDriverWait(selenium, 30).until(EC.url_changes(f"{TEST_URL}/register"))
 | 
			
		||||
    confirm_account(selenium, user)
 | 
			
		||||
    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]
 | 
			
		||||
    user = register_valid_user(selenium)
 | 
			
		||||
    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 confirm_account(selenium, user):
 | 
			
		||||
    time.sleep(1)
 | 
			
		||||
    response = requests.get(
 | 
			
		||||
        f"{EMAIL_API_URL}/api/v2/search?kind=to&query={user['email']}"
 | 
			
		||||
    )
 | 
			
		||||
    response.raise_for_status()
 | 
			
		||||
    results = response.json()
 | 
			
		||||
    message = results['items'][0]['Content']['Body']
 | 
			
		||||
    link = re.search(r'Verify your email: (.+?)\r\n', message).groups()[0]
 | 
			
		||||
    link = link.replace('http://0.0.0.0:5000', TEST_URL)
 | 
			
		||||
    selenium.get(link)
 | 
			
		||||
    WebDriverWait(selenium, 15).until(EC.url_changes(link))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def login_valid_user(selenium, user):
 | 
			
		||||
    login(selenium, user)
 | 
			
		||||
    WebDriverWait(selenium, 10).until(EC.url_changes(f"{TEST_URL}/login"))
 | 
			
		||||
    return user
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def assert_navbar(selenium, user):
 | 
			
		||||
    nav = selenium.find_element_by_id('nav').text
 | 
			
		||||
    assert 'Register' not in nav
 | 
			
		||||
    assert 'Login' not in nav
 | 
			
		||||
    assert 'Dashboard' in nav
 | 
			
		||||
    assert 'Workouts' in nav
 | 
			
		||||
    assert 'Statistics' in nav
 | 
			
		||||
    assert 'Add a workout' in nav
 | 
			
		||||
    assert user['username'] in nav
 | 
			
		||||
    assert 'Logout' in nav
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
import shutil
 | 
			
		||||
import re
 | 
			
		||||
from importlib import import_module, reload
 | 
			
		||||
from typing import Any
 | 
			
		||||
 | 
			
		||||
@@ -15,14 +15,16 @@ from flask_bcrypt import Bcrypt
 | 
			
		||||
from flask_dramatiq import Dramatiq
 | 
			
		||||
from flask_migrate import Migrate
 | 
			
		||||
from flask_sqlalchemy import SQLAlchemy
 | 
			
		||||
from sqlalchemy.exc import ProgrammingError
 | 
			
		||||
 | 
			
		||||
from fittrackee.emails.email import Email
 | 
			
		||||
from fittrackee.emails.email import EmailService
 | 
			
		||||
from fittrackee.request import CustomRequest
 | 
			
		||||
 | 
			
		||||
VERSION = __version__ = '0.5.7'
 | 
			
		||||
VERSION = __version__ = '0.6.10'
 | 
			
		||||
db = SQLAlchemy()
 | 
			
		||||
bcrypt = Bcrypt()
 | 
			
		||||
migrate = Migrate()
 | 
			
		||||
email_service = Email()
 | 
			
		||||
email_service = EmailService()
 | 
			
		||||
dramatiq = Dramatiq()
 | 
			
		||||
log_file = os.getenv('APP_LOG')
 | 
			
		||||
logging.basicConfig(
 | 
			
		||||
@@ -33,9 +35,17 @@ logging.basicConfig(
 | 
			
		||||
appLog = logging.getLogger('fittrackee')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_app() -> Flask:
 | 
			
		||||
class CustomFlask(Flask):
 | 
			
		||||
    # add custom Request to handle user-agent parsing
 | 
			
		||||
    # (removed in Werkzeug 2.1)
 | 
			
		||||
    request_class = CustomRequest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_app(init_email: bool = True) -> Flask:
 | 
			
		||||
    # instantiate the app
 | 
			
		||||
    app = Flask(__name__, static_folder='dist/static', template_folder='dist')
 | 
			
		||||
    app = CustomFlask(
 | 
			
		||||
        __name__, static_folder='dist/static', template_folder='dist'
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # set config
 | 
			
		||||
    with app.app_context():
 | 
			
		||||
@@ -54,8 +64,15 @@ def create_app() -> Flask:
 | 
			
		||||
    migrate.init_app(app, db)
 | 
			
		||||
    dramatiq.init_app(app)
 | 
			
		||||
 | 
			
		||||
    # set up email
 | 
			
		||||
    email_service.init_email(app)
 | 
			
		||||
    # set up email if 'EMAIL_URL' is initialized
 | 
			
		||||
    if init_email:
 | 
			
		||||
        if app.config['EMAIL_URL']:
 | 
			
		||||
            email_service.init_email(app)
 | 
			
		||||
            app.config['CAN_SEND_EMAILS'] = True
 | 
			
		||||
        else:
 | 
			
		||||
            appLog.warning(
 | 
			
		||||
                'EMAIL_URL is not provided, email sending is deactivated.'
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    # get configuration from database
 | 
			
		||||
    from .application.utils import (
 | 
			
		||||
@@ -66,9 +83,16 @@ def create_app() -> Flask:
 | 
			
		||||
    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 = get_or_init_config()
 | 
			
		||||
            update_app_config_from_database(app, db_app_config)
 | 
			
		||||
        try:
 | 
			
		||||
            if db.engine.dialect.has_table(db.engine.connect(), 'app_config'):
 | 
			
		||||
                db_app_config = get_or_init_config()
 | 
			
		||||
                update_app_config_from_database(app, db_app_config)
 | 
			
		||||
        except ProgrammingError as e:
 | 
			
		||||
            # avoid error on AppConfig migration
 | 
			
		||||
            if re.match(
 | 
			
		||||
                r'psycopg2.errors.UndefinedColumn(.*)app_config.', str(e)
 | 
			
		||||
            ):
 | 
			
		||||
                pass
 | 
			
		||||
 | 
			
		||||
    from .application.app_config import config_blueprint  # noqa
 | 
			
		||||
    from .users.auth import auth_blueprint  # noqa
 | 
			
		||||
@@ -96,7 +120,7 @@ def create_app() -> Flask:
 | 
			
		||||
        appLog.setLevel(logging.DEBUG)
 | 
			
		||||
 | 
			
		||||
        # Enable CORS
 | 
			
		||||
        @app.after_request
 | 
			
		||||
        @app.after_request  # type: ignore
 | 
			
		||||
        def after_request(response: Response) -> Response:
 | 
			
		||||
            response.headers.add('Access-Control-Allow-Origin', '*')
 | 
			
		||||
            response.headers.add(
 | 
			
		||||
@@ -129,17 +153,4 @@ def create_app() -> Flask:
 | 
			
		||||
        else:
 | 
			
		||||
            return render_template('index.html')
 | 
			
		||||
 | 
			
		||||
    @app.cli.command('drop-db')
 | 
			
		||||
    def drop_db() -> None:
 | 
			
		||||
        """Empty database and delete uploaded files for dev environments."""
 | 
			
		||||
        if app_settings == 'fittrackee.config.ProductionConfig':
 | 
			
		||||
            print('This is a production server, aborting!')
 | 
			
		||||
            return
 | 
			
		||||
        db.engine.execute("DROP TABLE IF EXISTS alembic_version;")
 | 
			
		||||
        db.drop_all()
 | 
			
		||||
        db.session.commit()
 | 
			
		||||
        print('Database dropped.')
 | 
			
		||||
        shutil.rmtree(app.config['UPLOAD_FOLDER'], ignore_errors=True)
 | 
			
		||||
        print('Uploaded files deleted.')
 | 
			
		||||
 | 
			
		||||
    return app
 | 
			
		||||
 
 | 
			
		||||
@@ -3,16 +3,23 @@
 | 
			
		||||
import os
 | 
			
		||||
from typing import Dict, Optional
 | 
			
		||||
 | 
			
		||||
import click
 | 
			
		||||
import gunicorn.app.base
 | 
			
		||||
from flask import Flask
 | 
			
		||||
from flask_migrate import upgrade
 | 
			
		||||
 | 
			
		||||
from fittrackee import create_app
 | 
			
		||||
from fittrackee.users.exceptions import UserNotFoundException
 | 
			
		||||
from fittrackee.users.utils.admin import UserManagerService
 | 
			
		||||
 | 
			
		||||
HOST = os.getenv('HOST', '0.0.0.0')
 | 
			
		||||
HOST = os.getenv('HOST', '127.0.0.1')
 | 
			
		||||
PORT = os.getenv('PORT', '5000')
 | 
			
		||||
WORKERS = os.getenv('APP_WORKERS', 1)
 | 
			
		||||
BASEDIR = os.path.abspath(os.path.dirname(__file__))
 | 
			
		||||
WARNING_MESSAGE = (
 | 
			
		||||
    "\nThis command is deprecated, it will be removed in a next version.\n"
 | 
			
		||||
    "Please use ftcli instead.\n"
 | 
			
		||||
)
 | 
			
		||||
app = create_app()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -37,7 +44,39 @@ class StandaloneApplication(gunicorn.app.base.BaseApplication):
 | 
			
		||||
        return self.application
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# DEPRECATED COMMANDS
 | 
			
		||||
@click.group()
 | 
			
		||||
def users_cli() -> None:
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@users_cli.command('set_admin')
 | 
			
		||||
@click.argument('username')
 | 
			
		||||
def set_admin(username: str) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    [deprecated] Set admin rights for given user.
 | 
			
		||||
 | 
			
		||||
    It will be removed in a next version.
 | 
			
		||||
    """
 | 
			
		||||
    print(WARNING_MESSAGE)
 | 
			
		||||
    with app.app_context():
 | 
			
		||||
        try:
 | 
			
		||||
            user_manager_service = UserManagerService(username)
 | 
			
		||||
            user_manager_service.update(
 | 
			
		||||
                is_admin=True,
 | 
			
		||||
            )
 | 
			
		||||
            print(f"User '{username}' updated.")
 | 
			
		||||
        except UserNotFoundException:
 | 
			
		||||
            print(f"User '{username}' not found.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def upgrade_db() -> None:
 | 
			
		||||
    """
 | 
			
		||||
    [deprecated] Apply migrations.
 | 
			
		||||
 | 
			
		||||
    It will be removed in a next version.
 | 
			
		||||
    """
 | 
			
		||||
    print(WARNING_MESSAGE)
 | 
			
		||||
    with app.app_context():
 | 
			
		||||
        upgrade(directory=BASEDIR + '/migrations')
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ from fittrackee.responses import (
 | 
			
		||||
)
 | 
			
		||||
from fittrackee.users.decorators import authenticate_as_admin
 | 
			
		||||
from fittrackee.users.models import User
 | 
			
		||||
from fittrackee.users.utils.controls import is_valid_email
 | 
			
		||||
 | 
			
		||||
from .models import AppConfig
 | 
			
		||||
from .utils import update_app_config_from_database, verify_app_config
 | 
			
		||||
@@ -39,13 +40,15 @@ def get_application_config() -> Union[Dict, HttpResponse]:
 | 
			
		||||
 | 
			
		||||
      {
 | 
			
		||||
        "data": {
 | 
			
		||||
          "admin_contact": "admin@example.com",
 | 
			
		||||
          "gpx_limit_import": 10,
 | 
			
		||||
          "is_email_sending_enabled": true,
 | 
			
		||||
          "is_registration_enabled": false,
 | 
			
		||||
          "max_single_file_size": 1048576,
 | 
			
		||||
          "max_zip_file_size": 10485760,
 | 
			
		||||
          "max_users": 0,
 | 
			
		||||
          "max_zip_file_size": 10485760,
 | 
			
		||||
          "map_attribution": "© <a href=http://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors"
 | 
			
		||||
          "version": "0.5.7"
 | 
			
		||||
          "version": "0.6.10"
 | 
			
		||||
        },
 | 
			
		||||
        "status": "success"
 | 
			
		||||
      }
 | 
			
		||||
@@ -87,20 +90,25 @@ def update_application_config(auth_user: User) -> Union[Dict, HttpResponse]:
 | 
			
		||||
 | 
			
		||||
      {
 | 
			
		||||
        "data": {
 | 
			
		||||
          "admin_contact": "admin@example.com",
 | 
			
		||||
          "gpx_limit_import": 10,
 | 
			
		||||
          "is_registration_enabled": true,
 | 
			
		||||
          "is_email_sending_enabled": true,
 | 
			
		||||
          "is_registration_enabled": false,
 | 
			
		||||
          "max_single_file_size": 1048576,
 | 
			
		||||
          "max_users": 10,
 | 
			
		||||
          "max_zip_file_size": 10485760,
 | 
			
		||||
          "max_users": 10
 | 
			
		||||
          "map_attribution": "© <a href=http://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors"
 | 
			
		||||
          "version": "0.6.10"
 | 
			
		||||
        },
 | 
			
		||||
        "status": "success"
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    :<json string admin_contact: email to contact the administrator
 | 
			
		||||
    :<json integer gpx_limit_import: max number of files in zip archive
 | 
			
		||||
    :<json boolean is_registration_enabled: is registration enabled ?
 | 
			
		||||
    :<json integer max_single_file_size: max size of a single file
 | 
			
		||||
    :<json integer max_zip_file_size: max size of a zip archive
 | 
			
		||||
    :<json integer max_users: max users allowed to register on instance
 | 
			
		||||
    :<json integer max_zip_file_size: max size of a zip archive
 | 
			
		||||
 | 
			
		||||
    :reqheader Authorization: OAuth 2.0 Bearer Token
 | 
			
		||||
 | 
			
		||||
@@ -110,6 +118,7 @@ def update_application_config(auth_user: User) -> Union[Dict, HttpResponse]:
 | 
			
		||||
        - provide a valid auth token
 | 
			
		||||
        - signature expired, please log in again
 | 
			
		||||
        - invalid token, please log in again
 | 
			
		||||
        - valid email must be provided for admin contact
 | 
			
		||||
    :statuscode 403: you do not have permissions
 | 
			
		||||
    :statuscode 500: error when updating configuration
 | 
			
		||||
    """
 | 
			
		||||
@@ -118,6 +127,9 @@ def update_application_config(auth_user: User) -> Union[Dict, HttpResponse]:
 | 
			
		||||
        return InvalidPayloadErrorResponse()
 | 
			
		||||
 | 
			
		||||
    ret = verify_app_config(config_data)
 | 
			
		||||
    admin_contact = config_data.get('admin_contact')
 | 
			
		||||
    if admin_contact and not is_valid_email(admin_contact):
 | 
			
		||||
        ret.append('valid email must be provided for admin contact')
 | 
			
		||||
    if ret:
 | 
			
		||||
        return InvalidPayloadErrorResponse(message=ret)
 | 
			
		||||
 | 
			
		||||
@@ -133,6 +145,8 @@ def update_application_config(auth_user: User) -> Union[Dict, HttpResponse]:
 | 
			
		||||
            config.max_zip_file_size = config_data.get('max_zip_file_size')
 | 
			
		||||
        if 'max_users' in config_data:
 | 
			
		||||
            config.max_users = config_data.get('max_users')
 | 
			
		||||
        if 'admin_contact' in config_data:
 | 
			
		||||
            config.admin_contact = admin_contact if admin_contact else None
 | 
			
		||||
 | 
			
		||||
        if config.max_zip_file_size < config.max_single_file_size:
 | 
			
		||||
            return InvalidPayloadErrorResponse(
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ class AppConfig(BaseModel):
 | 
			
		||||
        db.Integer, default=1048576, nullable=False
 | 
			
		||||
    )
 | 
			
		||||
    max_zip_file_size = db.Column(db.Integer, default=10485760, nullable=False)
 | 
			
		||||
    admin_contact = db.Column(db.String(255), nullable=True)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def is_registration_enabled(self) -> bool:
 | 
			
		||||
@@ -43,7 +44,9 @@ class AppConfig(BaseModel):
 | 
			
		||||
 | 
			
		||||
    def serialize(self) -> Dict:
 | 
			
		||||
        return {
 | 
			
		||||
            'admin_contact': self.admin_contact,
 | 
			
		||||
            'gpx_limit_import': self.gpx_limit_import,
 | 
			
		||||
            'is_email_sending_enabled': current_app.config['CAN_SEND_EMAILS'],
 | 
			
		||||
            'is_registration_enabled': self.is_registration_enabled,
 | 
			
		||||
            'max_single_file_size': self.max_single_file_size,
 | 
			
		||||
            'max_zip_file_size': self.max_zip_file_size,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								fittrackee/cli/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,14 @@
 | 
			
		||||
import click
 | 
			
		||||
 | 
			
		||||
from fittrackee.migrations.commands import db_cli
 | 
			
		||||
from fittrackee.users.commands import users_cli
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@click.group()
 | 
			
		||||
def cli() -> None:
 | 
			
		||||
    """FitTrackee Command Line Interface"""
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
cli.add_command(db_cli)
 | 
			
		||||
cli.add_command(users_cli)
 | 
			
		||||
							
								
								
									
										3
									
								
								fittrackee/cli/app.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,3 @@
 | 
			
		||||
from fittrackee import create_app
 | 
			
		||||
 | 
			
		||||
app = create_app(init_email=False)
 | 
			
		||||
@@ -12,7 +12,6 @@ else:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseConfig:
 | 
			
		||||
    """Base configuration"""
 | 
			
		||||
 | 
			
		||||
    DEBUG = False
 | 
			
		||||
    TESTING = False
 | 
			
		||||
@@ -30,6 +29,7 @@ class BaseConfig:
 | 
			
		||||
    UI_URL = os.environ.get('UI_URL')
 | 
			
		||||
    EMAIL_URL = os.environ.get('EMAIL_URL')
 | 
			
		||||
    SENDER_EMAIL = os.environ.get('SENDER_EMAIL')
 | 
			
		||||
    CAN_SEND_EMAILS = False
 | 
			
		||||
    DRAMATIQ_BROKER = broker
 | 
			
		||||
    TILE_SERVER = {
 | 
			
		||||
        'URL': os.environ.get(
 | 
			
		||||
@@ -43,40 +43,42 @@ class BaseConfig:
 | 
			
		||||
            ' contributors',
 | 
			
		||||
        ),
 | 
			
		||||
        'DEFAULT_STATICMAP': (
 | 
			
		||||
            os.environ.get('DEFAULT_STATICMAP', 'False') == 'True'
 | 
			
		||||
            os.environ.get('DEFAULT_STATICMAP', 'false').lower() == 'true'
 | 
			
		||||
        ),
 | 
			
		||||
        'STATICMAP_SUBDOMAINS': os.environ.get('STATICMAP_SUBDOMAINS', ''),
 | 
			
		||||
    }
 | 
			
		||||
    TRANSLATIONS_FOLDER = os.path.join(
 | 
			
		||||
        current_app.root_path, 'emails/translations'
 | 
			
		||||
    )
 | 
			
		||||
    LANGUAGES = ['en', 'fr', 'de']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DevelopmentConfig(BaseConfig):
 | 
			
		||||
    """Development configuration"""
 | 
			
		||||
 | 
			
		||||
    DEBUG = True
 | 
			
		||||
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')
 | 
			
		||||
    SECRET_KEY = 'development key'
 | 
			
		||||
    SECRET_KEY = 'development key'  # nosec
 | 
			
		||||
    BCRYPT_LOG_ROUNDS = 4
 | 
			
		||||
    DRAMATIQ_BROKER_URL = os.getenv('REDIS_URL', 'redis://')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestingConfig(BaseConfig):
 | 
			
		||||
    """Testing configuration"""
 | 
			
		||||
 | 
			
		||||
    DEBUG = True
 | 
			
		||||
    TESTING = True
 | 
			
		||||
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_TEST_URL')
 | 
			
		||||
    SECRET_KEY = 'test key'
 | 
			
		||||
    SECRET_KEY = 'test key'  # nosec
 | 
			
		||||
    BCRYPT_LOG_ROUNDS = 4
 | 
			
		||||
    TOKEN_EXPIRATION_DAYS = 0
 | 
			
		||||
    TOKEN_EXPIRATION_SECONDS = 3
 | 
			
		||||
    PASSWORD_TOKEN_EXPIRATION_SECONDS = 3
 | 
			
		||||
    UPLOAD_FOLDER = '/tmp/fitTrackee/uploads'
 | 
			
		||||
    UI_URL = 'http://0.0.0.0:5000'
 | 
			
		||||
    SENDER_EMAIL = 'fittrackee@example.com'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProductionConfig(BaseConfig):
 | 
			
		||||
    """Production configuration"""
 | 
			
		||||
class End2EndTestingConfig(TestingConfig):
 | 
			
		||||
    DRAMATIQ_BROKER_URL = os.getenv('REDIS_URL', 'redis://')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProductionConfig(BaseConfig):
 | 
			
		||||
    DEBUG = False
 | 
			
		||||
    # https://docs.sqlalchemy.org/en/13/core/pooling.html#using-connection-pools-with-multiprocessing-or-os-fork  # noqa
 | 
			
		||||
    SQLALCHEMY_ENGINE_OPTIONS = (
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								fittrackee/dist/index.html
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -1 +1 @@
 | 
			
		||||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="/favicon.ico"><![endif]--><link rel="stylesheet" href="/static/css/fork-awesome.min.css"><link rel="stylesheet" href="/static/css/leaflet.css"><title>FitTrackee</title><link href="/static/css/admin.e77f8b26.css" rel="prefetch"><link href="/static/css/profile.8b668068.css" rel="prefetch"><link href="/static/css/reset.fc19709e.css" rel="prefetch"><link href="/static/css/statistics.2afdc8a9.css" rel="prefetch"><link href="/static/css/workouts.1bed04b1.css" rel="prefetch"><link href="/static/js/admin.5f46d0fe.js" rel="prefetch"><link href="/static/js/chunk-2d0c9189.c81458cc.js" rel="prefetch"><link href="/static/js/chunk-2d0cf391.020c75ea.js" rel="prefetch"><link href="/static/js/chunk-2d0da8f3.c8c3e7e8.js" rel="prefetch"><link href="/static/js/chunk-2d2248b6.d84473c1.js" rel="prefetch"><link href="/static/js/chunk-2d22523a.4b710d99.js" rel="prefetch"><link href="/static/js/profile.d25975e2.js" rel="prefetch"><link href="/static/js/reset.ca898ebe.js" rel="prefetch"><link href="/static/js/statistics.d03ca304.js" rel="prefetch"><link href="/static/js/workouts.ca40c08d.js" rel="prefetch"><link href="/static/css/app.f2234171.css" rel="preload" as="style"><link href="/static/js/app.ad2630ed.js" rel="preload" as="script"><link href="/static/js/chunk-vendors.4605b41e.js" rel="preload" as="script"><link href="/static/css/app.f2234171.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png"><link rel="manifest" href="/manifest.json"><meta name="theme-color" content="#4DBA87"><meta name="apple-mobile-web-app-capable" content="no"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="apple-mobile-web-app-title" content="fittrackee_client"><link rel="apple-touch-icon" href="/img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#4DBA87"><meta name="msapplication-TileImage" content="/img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><noscript><strong>We're sorry but FitTrackee doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/static/js/chunk-vendors.4605b41e.js"></script><script src="/static/js/app.ad2630ed.js"></script></body></html>
 | 
			
		||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="/favicon.ico"><![endif]--><link rel="stylesheet" href="/static/css/fork-awesome.min.css"/><link rel="stylesheet" href="/static/css/leaflet.css"/><title>FitTrackee</title><script defer="defer" src="/static/js/chunk-vendors.7132edc6.js"></script><script defer="defer" src="/static/js/app.bf1d4e1c.js"></script><link href="/static/css/app.32d0ced1.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png"><link rel="manifest" href="/manifest.json"><meta name="theme-color" content="#4DBA87"><meta name="apple-mobile-web-app-capable" content="no"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="apple-mobile-web-app-title" content="fittrackee_client"><link rel="apple-touch-icon" href="/img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#4DBA87"><meta name="msapplication-TileImage" content="/img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><noscript><strong>We're sorry but FitTrackee doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
 | 
			
		||||
@@ -1,246 +0,0 @@
 | 
			
		||||
self.__precacheManifest = (self.__precacheManifest || []).concat([
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "ab4faed007625b209af72b9b8dce84db",
 | 
			
		||||
    "url": "/img/bike.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "428c6ccb838f8e91dc4826c5b650a98b",
 | 
			
		||||
    "url": "/img/weather/breeze.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "f3e8628de2e5089f508f03944f8be951",
 | 
			
		||||
    "url": "/img/weather/clear-day.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "6a0d6579a93e89fdf1a2a95b398e0935",
 | 
			
		||||
    "url": "/img/weather/clear-night.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "c52fe62c9d9fea2cdf5ae2813b3df366",
 | 
			
		||||
    "url": "/img/weather/cloudy.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "9f4d74289594eb83f9c175d13167a0eb",
 | 
			
		||||
    "url": "/img/weather/fog.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "f730c2e4f4c014e8b49c9d4a0a141b90",
 | 
			
		||||
    "url": "/img/weather/partly-cloudy-day.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "0d9270901c4dffbc05f3998d835171c8",
 | 
			
		||||
    "url": "/img/weather/partly-cloudy-night.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "91eb3159ba8781d258d8c384a8ef987c",
 | 
			
		||||
    "url": "/img/weather/pour-rain.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "c52ca88ce0ddf4de0fc29167e578b2be",
 | 
			
		||||
    "url": "/img/weather/rain.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "97d61d89332f912c1ec27bcbe077b7bd",
 | 
			
		||||
    "url": "/img/weather/sleet.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "9e88c89c0d6d14174e9665e98932dd80",
 | 
			
		||||
    "url": "/img/weather/snow.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "d42b62d0b57d5d326506277d87b70435",
 | 
			
		||||
    "url": "/img/weather/temperature.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "d75fbe559220d7b2b9577a98a91f38d0",
 | 
			
		||||
    "url": "/img/weather/wind.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "375196b7ade7c07c3b64ac4cb8b850a8",
 | 
			
		||||
    "url": "/img/workouts/finish.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "a41d3168615e9c642fe96c36bcca146d",
 | 
			
		||||
    "url": "/img/workouts/map.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "0b94f1d4a050a86ca5ac89ab3ed60f9c",
 | 
			
		||||
    "url": "/img/workouts/mountains.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "97f6a197b6c2869e0cb2ff0e7697af86",
 | 
			
		||||
    "url": "/img/workouts/start.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "9bf054725eec8a2ca4d9b1be2cbd785a",
 | 
			
		||||
    "url": "/index.html"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "c61028ca6e9f81bf958e6b66238e2d7c",
 | 
			
		||||
    "url": "/manifest.json"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "b6216d61c03e6ce0c9aea6ca7808f7ca",
 | 
			
		||||
    "url": "/robots.txt"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "8f4c00514b231a913495",
 | 
			
		||||
    "url": "/static/css/admin.e77f8b26.css"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "9c72219a46ba089d3797",
 | 
			
		||||
    "url": "/static/css/app.f2234171.css"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "82c1118c918377daaa71a320ab8eea42",
 | 
			
		||||
    "url": "/static/css/fork-awesome.min.css"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "5c761a156eea82263d8bacf1718fe04d",
 | 
			
		||||
    "url": "/static/css/leaflet.css"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "00382d944a1bc6fca08b",
 | 
			
		||||
    "url": "/static/css/profile.8b668068.css"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "ed921b9e1d1e590ca0ec",
 | 
			
		||||
    "url": "/static/css/reset.fc19709e.css"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "d772b2a24985f6471afd",
 | 
			
		||||
    "url": "/static/css/statistics.2afdc8a9.css"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "4fe9e93fc76c94cbb529",
 | 
			
		||||
    "url": "/static/css/workouts.1bed04b1.css"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "e719f9244c69e28e7d00e725ca1e280e",
 | 
			
		||||
    "url": "/static/fonts/forkawesome-webfont.eot"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "d58987a18774d859d98af95281c1dee5",
 | 
			
		||||
    "url": "/static/fonts/forkawesome-webfont.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "fe45770c6c277da3921355d971045ea5",
 | 
			
		||||
    "url": "/static/fonts/forkawesome-webfont.ttf"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "92cd4d8328b2ae614386a01ff935a9f3",
 | 
			
		||||
    "url": "/static/fonts/forkawesome-webfont.woff"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "12ee76445f0383c58529169d700b6650",
 | 
			
		||||
    "url": "/static/fonts/forkawesome-webfont.woff2"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "1db19001a508392a6be99f75e3a627f0",
 | 
			
		||||
    "url": "/static/fonts/pt-sans-v9-latin-700.1db19001.eot"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "6078a79524f81df56f2512c42eac7299",
 | 
			
		||||
    "url": "/static/fonts/pt-sans-v9-latin-700.6078a795.ttf"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "e86e8add16b15acb0882a8f009e0e84c",
 | 
			
		||||
    "url": "/static/fonts/pt-sans-v9-latin-700.e86e8add.woff"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "f6d5807c5cbc7ff22671d30fbf09ae1a",
 | 
			
		||||
    "url": "/static/fonts/pt-sans-v9-latin-700.f6d5807c.woff2"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "4ef1aecea14c2161f0f882b7247473a5",
 | 
			
		||||
    "url": "/static/fonts/pt-sans-v9-latin-italic.4ef1aece.ttf"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "57d98c19d7c99ec67ffdf8a265dd2144",
 | 
			
		||||
    "url": "/static/fonts/pt-sans-v9-latin-italic.57d98c19.woff2"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "d5f81343aed1d9322fde2e03e416221e",
 | 
			
		||||
    "url": "/static/fonts/pt-sans-v9-latin-italic.d5f81343.eot"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "de9540fd6912070844aae49eb7b0f2d6",
 | 
			
		||||
    "url": "/static/fonts/pt-sans-v9-latin-italic.de9540fd.woff"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "198d320b73bc44e4f0dd33d6b09c9511",
 | 
			
		||||
    "url": "/static/fonts/pt-sans-v9-latin-regular.198d320b.woff2"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "399a9357b6e525fb4d8e2e2786df190e",
 | 
			
		||||
    "url": "/static/fonts/pt-sans-v9-latin-regular.399a9357.woff"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "4ffa4616720ccdaebdf6002f6839a326",
 | 
			
		||||
    "url": "/static/fonts/pt-sans-v9-latin-regular.4ffa4616.ttf"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "c2a4d575c73721ba88c000c74a7995b4",
 | 
			
		||||
    "url": "/static/fonts/pt-sans-v9-latin-regular.c2a4d575.eot"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "061c85661fe8a113e7dbf56b1496e9a1",
 | 
			
		||||
    "url": "/static/img/pt-sans-v9-latin-700.061c8566.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "648d335176f8de8d377069f095dd9c94",
 | 
			
		||||
    "url": "/static/img/pt-sans-v9-latin-italic.648d3351.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "f1f73e45f75956c4b462dada9736ced2",
 | 
			
		||||
    "url": "/static/img/pt-sans-v9-latin-regular.f1f73e45.svg"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "8f4c00514b231a913495",
 | 
			
		||||
    "url": "/static/js/admin.5f46d0fe.js"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "9c72219a46ba089d3797",
 | 
			
		||||
    "url": "/static/js/app.ad2630ed.js"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "bd7d183c9f68e5f4027d",
 | 
			
		||||
    "url": "/static/js/chunk-2d0c9189.c81458cc.js"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "8ad7d194c31be0a8a03b",
 | 
			
		||||
    "url": "/static/js/chunk-2d0cf391.020c75ea.js"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "19720c22fb5bc17061e5",
 | 
			
		||||
    "url": "/static/js/chunk-2d0da8f3.c8c3e7e8.js"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "91470db3e6dda2f0a9ee",
 | 
			
		||||
    "url": "/static/js/chunk-2d2248b6.d84473c1.js"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "142f8131e877952087ff",
 | 
			
		||||
    "url": "/static/js/chunk-2d22523a.4b710d99.js"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "4b5b226c28a37969ec2f",
 | 
			
		||||
    "url": "/static/js/chunk-vendors.4605b41e.js"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "00382d944a1bc6fca08b",
 | 
			
		||||
    "url": "/static/js/profile.d25975e2.js"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "ed921b9e1d1e590ca0ec",
 | 
			
		||||
    "url": "/static/js/reset.ca898ebe.js"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "d772b2a24985f6471afd",
 | 
			
		||||
    "url": "/static/js/statistics.d03ca304.js"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "revision": "4fe9e93fc76c94cbb529",
 | 
			
		||||
    "url": "/static/js/workouts.ca40c08d.js"
 | 
			
		||||
  }
 | 
			
		||||
]);
 | 
			
		||||
							
								
								
									
										36
									
								
								fittrackee/dist/service-worker.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
							
								
								
									
										1
									
								
								fittrackee/dist/service-worker.js.map
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								fittrackee/dist/static/css/app.32d0ced1.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										1
									
								
								fittrackee/dist/static/css/app.f2234171.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
							
								
								
									
										1
									
								
								fittrackee/dist/static/css/profile.203e78e2.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1 @@
 | 
			
		||||
#account-confirmation[data-v-785df978]{display:flex;flex-direction:column;align-items:center}#account-confirmation svg[data-v-785df978]{stroke:none;fill-rule:nonzero;fill:var(--app-color);filter:var(--svg-filter);width:100px}#account-confirmation .error-message[data-v-785df978]{font-size:1.1em;text-align:center;display:flex;flex-direction:column}@media screen and (max-width:1000px){#account-confirmation .error-message[data-v-785df978]{font-size:1em}}#email-update[data-v-8c2ec9ce]{display:flex;flex-direction:column;align-items:center}#email-update svg[data-v-8c2ec9ce]{stroke:none;fill-rule:nonzero;fill:var(--app-color);filter:var(--svg-filter);width:100px}#email-update .error-message[data-v-8c2ec9ce]{font-size:1.1em;text-align:center;display:flex;flex-direction:column}@media screen and (max-width:1000px){#email-update .error-message[data-v-8c2ec9ce]{font-size:1em}}#profile[data-v-05463732],#profile[data-v-05463732] .profile-form{display:flex;flex-direction:column}#profile[data-v-05463732] .profile-form hr{border-color:var(--card-border-color);border-width:1px 0 0 0}#profile[data-v-05463732] .profile-form .form-items{display:flex;flex-direction:column}#profile[data-v-05463732] .profile-form .form-items input{margin:5px 0}#profile[data-v-05463732] .profile-form .form-items select{height:35px;padding:5px 0}#profile[data-v-05463732] .profile-form .form-items ::v-deep(.custom-textarea) textarea{padding:5px}#profile[data-v-05463732] .profile-form .form-items .form-item{display:flex;flex-direction:column;padding:10px}#profile[data-v-05463732] .profile-form .form-items .birth-date{height:20px}#profile[data-v-05463732] .profile-form .form-buttons{display:flex;margin-top:10px;padding:10px 0;gap:10px}#user[data-v-af7007f4]{margin:auto;width:700px}@media screen and (max-width:1000px){#user[data-v-af7007f4]{width:100%;margin:0 auto 50px auto}}
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
#profile[data-v-163d82f7],#profile[data-v-163d82f7] .profile-form{display:flex;flex-direction:column}#profile[data-v-163d82f7] .profile-form hr{border-color:var(--card-border-color);border-width:1px 0 0 0}#profile[data-v-163d82f7] .profile-form .form-items{display:flex;flex-direction:column}#profile[data-v-163d82f7] .profile-form .form-items input{margin:5px 0}#profile[data-v-163d82f7] .profile-form .form-items select{height:35px;padding:5px 0}#profile[data-v-163d82f7] .profile-form .form-items ::v-deep(.custom-textarea) textarea{padding:5px}#profile[data-v-163d82f7] .profile-form .form-items .form-item{display:flex;flex-direction:column;padding:10px}#profile[data-v-163d82f7] .profile-form .form-items .birth-date{height:20px}#profile[data-v-163d82f7] .profile-form .form-buttons{display:flex;margin-top:10px;padding:10px 0;gap:10px}#user[data-v-10e7b479]{margin:auto;width:700px}@media screen and (max-width:1000px){#user[data-v-10e7b479]{width:100%;margin:0 auto 50px auto}}
 | 
			
		||||
							
								
								
									
										1
									
								
								fittrackee/dist/static/css/reset.e2527ec6.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1 @@
 | 
			
		||||
#account-confirmation-email[data-v-66aca424]{display:flex;flex-direction:column}#account-confirmation-email .email-sent[data-v-66aca424]{display:flex;flex-direction:column;align-items:center}#account-confirmation-email .email-sent svg[data-v-66aca424]{stroke:none;fill-rule:nonzero;fill:var(--app-color);filter:var(--svg-filter);width:100px}#account-confirmation-email .email-sent .email-sent-message[data-v-66aca424]{font-size:1.1em;text-align:center}@media screen and (max-width:1000px){#account-confirmation-email .email-sent .email-sent-message[data-v-66aca424]{font-size:1em}}#account-confirmation-email[data-v-66aca424] .card .card-content #user-auth-form{margin-top:0}#account-confirmation-email[data-v-66aca424] .card .card-content #user-auth-form #user-form{width:100%}#account-confirmation[data-v-35aad344]{display:flex}#account-confirmation .container[data-v-35aad344]{display:flex;justify-content:center;width:50%}@media screen and (max-width:700px){#account-confirmation .container[data-v-35aad344]{width:100%}}#password-action-done[data-v-eac78356]{display:flex;flex-direction:column;align-items:center}#password-action-done svg[data-v-eac78356]{stroke:none;fill-rule:nonzero;fill:var(--app-color);filter:var(--svg-filter);width:100px}#password-action-done .password-message[data-v-eac78356]{font-size:1.1em;text-align:center}@media screen and (max-width:1000px){#password-action-done .password-message[data-v-eac78356]{font-size:1em}}#password-reset-request[data-v-68377e44] .card .card-content #user-form{width:100%}#password-reset[data-v-a1cc55c4]{display:flex}#password-reset .container[data-v-a1cc55c4]{display:flex;justify-content:center;width:50%}@media screen and (max-width:700px){#password-reset .container[data-v-a1cc55c4]{width:100%}}
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
#password-action-done[data-v-18334f6d]{display:flex;flex-direction:column;align-items:center}#password-action-done svg[data-v-18334f6d]{stroke:none;fill-rule:nonzero;fill:var(--app-color);filter:var(--svg-filter);width:100px}#password-action-done .password-message[data-v-18334f6d]{font-size:1.1em;text-align:center}@media screen and (max-width:1000px){#password-action-done .password-message[data-v-18334f6d]{font-size:1em}}#password-reset-request[data-v-68377e44] .card .card-content #user-form{width:100%}#password-reset[data-v-f5e39b60]{display:flex}#password-reset .container[data-v-f5e39b60]{display:flex;justify-content:center;width:50%}@media screen and (max-width:700px){#password-reset .container[data-v-f5e39b60]{width:100%;margin:0 auto 50px auto}}
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
.chart-menu[data-v-af15954c]{display:flex}.chart-menu .chart-arrow[data-v-af15954c],.chart-menu .time-frames[data-v-af15954c]{flex-grow:1;text-align:center}.chart-menu .chart-arrow[data-v-af15954c]{cursor:pointer}.sports-menu{display:flex;flex-wrap:wrap;padding:10px}.sports-menu label{display:flex;align-items:center;font-size:.9em;font-weight:400;min-width:120px;padding:10px}@media screen and (max-width:1000px){.sports-menu label{min-width:100px}}@media screen and (max-width:500px){.sports-menu label{min-width:20px}.sports-menu label .sport-label{display:none}}.sports-menu .sport-img{padding:3px;width:20px;height:20px}#user-statistics.stats-disabled[data-v-7d54529b]{opacity:.3;pointer-events:none}#user-statistics[data-v-7d54529b] .chart-radio{justify-content:space-around;padding:30px 10px 10px 10px}#statistics[data-v-0d93da6e]{display:flex;width:100%}#statistics .container[data-v-0d93da6e]{display:flex;flex-direction:column;width:100%}
 | 
			
		||||
							
								
								
									
										1
									
								
								fittrackee/dist/static/css/statistics.d0356685.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1 @@
 | 
			
		||||
.chart-menu[data-v-22d55de2]{display:flex}.chart-menu .chart-arrow[data-v-22d55de2],.chart-menu .time-frames[data-v-22d55de2]{flex-grow:1;text-align:center}.chart-menu .chart-arrow[data-v-22d55de2]{cursor:pointer}.sports-menu{display:flex;flex-wrap:wrap;padding:10px}.sports-menu label{display:flex;align-items:center;font-size:.9em;font-weight:400;min-width:120px;padding:10px}@media screen and (max-width:1000px){.sports-menu label{min-width:100px}}@media screen and (max-width:500px){.sports-menu label{min-width:20px}.sports-menu label .sport-label{display:none}}.sports-menu .sport-img{padding:3px;width:20px;height:20px}#user-statistics.stats-disabled[data-v-d693c7da]{opacity:.3;pointer-events:none}#user-statistics[data-v-d693c7da] .chart-radio{justify-content:space-around;padding:30px 10px 10px 10px}#statistics[data-v-2e341d4e]{display:flex;width:100%}#statistics .container[data-v-2e341d4e]{display:flex;flex-direction:column;width:100%}
 | 
			
		||||