Merge branch 'dev' into oauth2

This commit is contained in:
Sam 2022-06-12 09:28:01 +02:00
commit 22c8105ea6
65 changed files with 961 additions and 627 deletions

View File

@ -1,5 +1,16 @@
# Change log # Change log
## 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) ## Version 0.6.6 (2022/05/29)
### Misc ### Misc

View File

@ -74,7 +74,7 @@ docker-stop:
docker-compose -f docker-compose-dev.yml stop docker-compose -f docker-compose-dev.yml stop
docker-up: docker-up:
docker-compose -f docker-compose-dev.yml up fittrackeee docker-compose -f docker-compose-dev.yml up fittrackee
downgrade-db: downgrade-db:
$(FLASK) db downgrade --directory $(MIGRATIONS) $(FLASK) db downgrade --directory $(MIGRATIONS)

View File

@ -1 +1 @@
0.6.6 0.6.7

View File

@ -1,4 +1,4 @@
# Sphinx build info version 1 # 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. # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 4cdeaae17c31d716c1f73eb46769543b config: e7f46ba7f0469b1929d2349b0c52c449
tags: 645f666f9bcd5a90fca523b33c5a78b7 tags: 645f666f9bcd5a90fca523b33c5a78b7

Binary file not shown.

Before

Width:  |  Height:  |  Size: 539 KiB

After

Width:  |  Height:  |  Size: 539 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 368 KiB

After

Width:  |  Height:  |  Size: 368 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 103 KiB

View File

@ -1,5 +1,16 @@
# Change log # Change log
## 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) ## Version 0.6.6 (2022/05/29)
### Misc ### Misc

View File

@ -395,13 +395,13 @@ Production environment
.. warning:: .. warning::
| Note that FitTrackee is under heavy development, some features may be unstable. | Note that FitTrackee is under heavy development, some features may be unstable.
- Download the last release (for now, it is the release v0.6.6): - Download the last release (for now, it is the release v0.6.7):
.. code:: bash .. code:: bash
$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.6.tar.gz $ wget https://github.com/SamR1/FitTrackee/archive/v0.6.7.tar.gz
$ tar -xzf v0.6.6.tar.gz $ tar -xzf v0.6.7.tar.gz
$ mv FitTrackee-0.6.6 FitTrackee $ mv FitTrackee-0.6.7 FitTrackee
$ cd FitTrackee $ cd FitTrackee
- Create **.env** from example and update it - Create **.env** from example and update it
@ -521,13 +521,13 @@ Prod environment
- Change to the directory where FitTrackee directory is located - Change to the directory where FitTrackee directory is located
- Download the last release (for now, it is the release v0.6.6) and overwrite existing files: - Download the last release (for now, it is the release v0.6.7) and overwrite existing files:
.. code:: bash .. code:: bash
$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.6.tar.gz $ wget https://github.com/SamR1/FitTrackee/archive/v0.6.7.tar.gz
$ tar -xzf v0.6.6.tar.gz $ tar -xzf v0.6.7.tar.gz
$ cp -R FitTrackee-0.6.6/* FitTrackee/ $ cp -R FitTrackee-0.6.7/* FitTrackee/
$ cd FitTrackee $ cd FitTrackee
- Update **.env** if needed (see `Environment variables <installation.html#environment-variables>`__). - Update **.env** if needed (see `Environment variables <installation.html#environment-variables>`__).

View File

@ -1,6 +1,6 @@
var DOCUMENTATION_OPTIONS = { var DOCUMENTATION_OPTIONS = {
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
VERSION: '0.6.6', VERSION: '0.6.7',
LANGUAGE: 'None', LANGUAGE: 'None',
COLLAPSE_INDEX: false, COLLAPSE_INDEX: false,
BUILDER: 'html', BUILDER: 'html',

View File

@ -5,7 +5,7 @@
<meta charset="utf-8" /> <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.17.1: http://docutils.sourceforge.net/" />
<title>Authentication &#8212; FitTrackee 0.6.6 <title>Authentication &#8212; FitTrackee 0.6.7
documentation</title> documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" /> <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/bootstrap-sphinx.css" />
@ -40,7 +40,7 @@
</button> </button>
<a class="navbar-brand" href="../index.html"> <a class="navbar-brand" href="../index.html">
FitTrackee</a> FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.6.6 <span class="navbar-text navbar-version pull-left"><b>0.6.7
</b></span> </b></span>
</div> </div>

View File

@ -5,7 +5,7 @@
<meta charset="utf-8" /> <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.17.1: http://docutils.sourceforge.net/" />
<title>Configuration &#8212; FitTrackee 0.6.6 <title>Configuration &#8212; FitTrackee 0.6.7
documentation</title> documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" /> <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/bootstrap-sphinx.css" />
@ -40,7 +40,7 @@
</button> </button>
<a class="navbar-brand" href="../index.html"> <a class="navbar-brand" href="../index.html">
FitTrackee</a> FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.6.6 <span class="navbar-text navbar-version pull-left"><b>0.6.7
</b></span> </b></span>
</div> </div>
@ -151,7 +151,7 @@
<span class="w"> </span><span class="nt">&quot;max_users&quot;</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">&quot;max_users&quot;</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">&quot;max_zip_file_size&quot;</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">&quot;max_zip_file_size&quot;</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">&quot;map_attribution&quot;</span><span class="p">:</span><span class="w"> </span><span class="nt">&quot;&amp;copy; &lt;a href=http://www.openstreetmap.org/copyright&gt;OpenStreetMap&lt;/a&gt; contributors&quot;</span><span class="w"></span> <span class="w"> </span><span class="nt">&quot;map_attribution&quot;</span><span class="p">:</span><span class="w"> </span><span class="nt">&quot;&amp;copy; &lt;a href=http://www.openstreetmap.org/copyright&gt;OpenStreetMap&lt;/a&gt; contributors&quot;</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;version&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;0.6.6&quot;</span><span class="w"></span> <span class="w"> </span><span class="nt">&quot;version&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;0.6.7&quot;</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">&quot;status&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;success&quot;</span><span class="w"></span> <span class="w"> </span><span class="nt">&quot;status&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;success&quot;</span><span class="w"></span>
<span class="p">}</span><span class="w"></span> <span class="p">}</span><span class="w"></span>
@ -191,7 +191,7 @@
<span class="w"> </span><span class="nt">&quot;max_users&quot;</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">&quot;max_users&quot;</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">&quot;max_zip_file_size&quot;</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">&quot;max_zip_file_size&quot;</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">&quot;map_attribution&quot;</span><span class="p">:</span><span class="w"> </span><span class="nt">&quot;&amp;copy; &lt;a href=http://www.openstreetmap.org/copyright&gt;OpenStreetMap&lt;/a&gt; contributors&quot;</span><span class="w"></span> <span class="w"> </span><span class="nt">&quot;map_attribution&quot;</span><span class="p">:</span><span class="w"> </span><span class="nt">&quot;&amp;copy; &lt;a href=http://www.openstreetmap.org/copyright&gt;OpenStreetMap&lt;/a&gt; contributors&quot;</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;version&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;0.6.6&quot;</span><span class="w"></span> <span class="w"> </span><span class="nt">&quot;version&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;0.6.7&quot;</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">&quot;status&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;success&quot;</span><span class="w"></span> <span class="w"> </span><span class="nt">&quot;status&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;success&quot;</span><span class="w"></span>
<span class="p">}</span><span class="w"></span> <span class="p">}</span><span class="w"></span>

View File

@ -5,7 +5,7 @@
<meta charset="utf-8" /> <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.17.1: http://docutils.sourceforge.net/" />
<title>API documentation &#8212; FitTrackee 0.6.6 <title>API documentation &#8212; FitTrackee 0.6.7
documentation</title> documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" /> <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/bootstrap-sphinx.css" />
@ -40,7 +40,7 @@
</button> </button>
<a class="navbar-brand" href="../index.html"> <a class="navbar-brand" href="../index.html">
FitTrackee</a> FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.6.6 <span class="navbar-text navbar-version pull-left"><b>0.6.7
</b></span> </b></span>
</div> </div>

View File

@ -5,7 +5,7 @@
<meta charset="utf-8" /> <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.17.1: http://docutils.sourceforge.net/" />
<title>Records &#8212; FitTrackee 0.6.6 <title>Records &#8212; FitTrackee 0.6.7
documentation</title> documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" /> <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/bootstrap-sphinx.css" />
@ -40,7 +40,7 @@
</button> </button>
<a class="navbar-brand" href="../index.html"> <a class="navbar-brand" href="../index.html">
FitTrackee</a> FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.6.6 <span class="navbar-text navbar-version pull-left"><b>0.6.7
</b></span> </b></span>
</div> </div>

View File

@ -5,7 +5,7 @@
<meta charset="utf-8" /> <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.17.1: http://docutils.sourceforge.net/" />
<title>Sports &#8212; FitTrackee 0.6.6 <title>Sports &#8212; FitTrackee 0.6.7
documentation</title> documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" /> <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/bootstrap-sphinx.css" />
@ -40,7 +40,7 @@
</button> </button>
<a class="navbar-brand" href="../index.html"> <a class="navbar-brand" href="../index.html">
FitTrackee</a> FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.6.6 <span class="navbar-text navbar-version pull-left"><b>0.6.7
</b></span> </b></span>
</div> </div>

View File

@ -5,7 +5,7 @@
<meta charset="utf-8" /> <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.17.1: http://docutils.sourceforge.net/" />
<title>Statistics &#8212; FitTrackee 0.6.6 <title>Statistics &#8212; FitTrackee 0.6.7
documentation</title> documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" /> <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/bootstrap-sphinx.css" />
@ -40,7 +40,7 @@
</button> </button>
<a class="navbar-brand" href="../index.html"> <a class="navbar-brand" href="../index.html">
FitTrackee</a> FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.6.6 <span class="navbar-text navbar-version pull-left"><b>0.6.7
</b></span> </b></span>
</div> </div>

View File

@ -5,7 +5,7 @@
<meta charset="utf-8" /> <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.17.1: http://docutils.sourceforge.net/" />
<title>Users &#8212; FitTrackee 0.6.6 <title>Users &#8212; FitTrackee 0.6.7
documentation</title> documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" /> <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/bootstrap-sphinx.css" />
@ -40,7 +40,7 @@
</button> </button>
<a class="navbar-brand" href="../index.html"> <a class="navbar-brand" href="../index.html">
FitTrackee</a> FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.6.6 <span class="navbar-text navbar-version pull-left"><b>0.6.7
</b></span> </b></span>
</div> </div>

View File

@ -5,7 +5,7 @@
<meta charset="utf-8" /> <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.17.1: http://docutils.sourceforge.net/" />
<title>Workouts &#8212; FitTrackee 0.6.6 <title>Workouts &#8212; FitTrackee 0.6.7
documentation</title> documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" /> <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/bootstrap-sphinx.css" />
@ -40,7 +40,7 @@
</button> </button>
<a class="navbar-brand" href="../index.html"> <a class="navbar-brand" href="../index.html">
FitTrackee</a> FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.6.6 <span class="navbar-text navbar-version pull-left"><b>0.6.7
</b></span> </b></span>
</div> </div>
@ -284,7 +284,7 @@
<dl class="http get"> <dl class="http get">
<dt class="sig sig-object http" id="get--api-workouts-(string-workout_short_id)"> <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> <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> <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> <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> </pre></div>
@ -378,7 +378,7 @@
<dl class="http get"> <dl class="http get">
<dt class="sig sig-object http" id="get--api-workouts-(string-workout_short_id)-gpx"> <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> <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> <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> <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> <span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
@ -431,7 +431,7 @@
<dl class="http get"> <dl class="http get">
<dt class="sig sig-object http" id="get--api-workouts-(string-workout_short_id)-chart_data"> <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> <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> <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> <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> <span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
@ -503,7 +503,7 @@
<dl class="http get"> <dl class="http get">
<dt class="sig sig-object http" id="get--api-workouts-(string-workout_short_id)-chart_data-segment-(int-segment_id)"> <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> <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> <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> <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> <span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
@ -573,7 +573,7 @@
<dl class="http get"> <dl class="http get">
<dt class="sig sig-object http" id="get--api-workouts-(string-workout_short_id)-gpx-segment-(int-segment_id)"> <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> <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> <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> <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> <span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
@ -724,7 +724,7 @@
<dl class="http post"> <dl class="http post">
<dt class="sig sig-object http" id="post--api-workouts"> <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> <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> <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> <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> <span class="na">Content-Type</span><span class="o">:</span> <span class="l">multipart/form-data</span>
@ -847,7 +847,7 @@
<dl class="http post"> <dl class="http post">
<dt class="sig sig-object http" id="post--api-workouts-no_gpx"> <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> <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> <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> <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> <span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
@ -935,7 +935,8 @@
<dl class="field-list simple"> <dl class="field-list simple">
<dt class="field-odd">Request JSON Object</dt> <dt class="field-odd">Request JSON Object</dt>
<dd class="field-odd"><ul class="simple"> <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>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>duration</strong> (<em>integer</em>) workout duration in seconds</p></li>
<li><p><strong>notes</strong> (<em>string</em>) notes (not mandatory)</p></li> <li><p><strong>notes</strong> (<em>string</em>) notes (not mandatory)</p></li>
@ -967,7 +968,7 @@
<dl class="http patch"> <dl class="http patch">
<dt class="sig sig-object http" id="patch--api-workouts-(string-workout_short_id)"> <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> <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> <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> <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> <span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
@ -1060,7 +1061,8 @@
</dd> </dd>
<dt class="field-even">Request JSON Object</dt> <dt class="field-even">Request JSON Object</dt>
<dd class="field-even"><ul class="simple"> <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> (only for workout without gpx)</p></li>
<li><p><strong>distance</strong> (<em>float</em>) workout distance in km <li><p><strong>distance</strong> (<em>float</em>) workout distance in km
(only for workout without gpx)</p></li> (only for workout without gpx)</p></li>
@ -1096,7 +1098,7 @@
<dl class="http delete"> <dl class="http delete">
<dt class="sig sig-object http" id="delete--api-workouts-(string-workout_short_id)"> <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> <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> <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> <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> <span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
<meta charset="utf-8" /> <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.17.1: http://docutils.sourceforge.net/" />
<title>Command line interface &#8212; FitTrackee 0.6.6 <title>Command line interface &#8212; FitTrackee 0.6.7
documentation</title> documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <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/bootstrap-sphinx.css" />
@ -40,7 +40,7 @@
</button> </button>
<a class="navbar-brand" href="index.html"> <a class="navbar-brand" href="index.html">
FitTrackee</a> FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.6.6 <span class="navbar-text navbar-version pull-left"><b>0.6.7
</b></span> </b></span>
</div> </div>

View File

@ -5,7 +5,7 @@
<meta charset="utf-8" /> <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.17.1: http://docutils.sourceforge.net/" />
<title>Features &#8212; FitTrackee 0.6.6 <title>Features &#8212; FitTrackee 0.6.7
documentation</title> documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <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/bootstrap-sphinx.css" />
@ -40,7 +40,7 @@
</button> </button>
<a class="navbar-brand" href="index.html"> <a class="navbar-brand" href="index.html">
FitTrackee</a> FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.6.6 <span class="navbar-text navbar-version pull-left"><b>0.6.7
</b></span> </b></span>
</div> </div>

View File

@ -4,7 +4,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Index &#8212; FitTrackee 0.6.6 <title>Index &#8212; FitTrackee 0.6.7
documentation</title> documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <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/bootstrap-sphinx.css" />
@ -37,7 +37,7 @@
</button> </button>
<a class="navbar-brand" href="index.html"> <a class="navbar-brand" href="index.html">
FitTrackee</a> FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.6.6 <span class="navbar-text navbar-version pull-left"><b>0.6.7
</b></span> </b></span>
</div> </div>

View File

@ -4,7 +4,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>HTTP Routing Table &#8212; FitTrackee 0.6.6 <title>HTTP Routing Table &#8212; FitTrackee 0.6.7
documentation</title> documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <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/bootstrap-sphinx.css" />
@ -44,7 +44,7 @@
</button> </button>
<a class="navbar-brand" href="index.html"> <a class="navbar-brand" href="index.html">
FitTrackee</a> FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.6.6 <span class="navbar-text navbar-version pull-left"><b>0.6.7
</b></span> </b></span>
</div> </div>

View File

@ -5,7 +5,7 @@
<meta charset="utf-8" /> <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.17.1: http://docutils.sourceforge.net/" />
<title>FitTrackee &#8212; FitTrackee 0.6.6 <title>FitTrackee &#8212; FitTrackee 0.6.7
documentation</title> documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <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/bootstrap-sphinx.css" />
@ -39,7 +39,7 @@
</button> </button>
<a class="navbar-brand" href="#"> <a class="navbar-brand" href="#">
FitTrackee</a> FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.6.6 <span class="navbar-text navbar-version pull-left"><b>0.6.7
</b></span> </b></span>
</div> </div>

View File

@ -5,7 +5,7 @@
<meta charset="utf-8" /> <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.17.1: http://docutils.sourceforge.net/" />
<title>Installation &#8212; FitTrackee 0.6.6 <title>Installation &#8212; FitTrackee 0.6.7
documentation</title> documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <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/bootstrap-sphinx.css" />
@ -40,7 +40,7 @@
</button> </button>
<a class="navbar-brand" href="index.html"> <a class="navbar-brand" href="index.html">
FitTrackee</a> FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.6.6 <span class="navbar-text navbar-version pull-left"><b>0.6.7
</b></span> </b></span>
</div> </div>
@ -674,11 +674,11 @@ $ make install-db
</div> </div>
</div> </div>
<ul class="simple"> <ul class="simple">
<li><p>Download the last release (for now, it is the release v0.6.6):</p></li> <li><p>Download the last release (for now, it is the release v0.6.7):</p></li>
</ul> </ul>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.6.tar.gz <div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.7.tar.gz
$ tar -xzf v0.6.6.tar.gz $ tar -xzf v0.6.7.tar.gz
$ mv FitTrackee-0.6.6 FitTrackee $ mv FitTrackee-0.6.7 FitTrackee
$ <span class="nb">cd</span> FitTrackee $ <span class="nb">cd</span> FitTrackee
</pre></div> </pre></div>
</div> </div>
@ -798,11 +798,11 @@ $ <span class="nb">source</span> .env
<ul class="simple"> <ul class="simple">
<li><p>Stop the application</p></li> <li><p>Stop the application</p></li>
<li><p>Change to the directory where FitTrackee directory is located</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.6.6) and overwrite existing files:</p></li> <li><p>Download the last release (for now, it is the release v0.6.7) and overwrite existing files:</p></li>
</ul> </ul>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.6.tar.gz <div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.7.tar.gz
$ tar -xzf v0.6.6.tar.gz $ tar -xzf v0.6.7.tar.gz
$ cp -R FitTrackee-0.6.6/* FitTrackee/ $ cp -R FitTrackee-0.6.7/* FitTrackee/
$ <span class="nb">cd</span> FitTrackee $ <span class="nb">cd</span> FitTrackee
</pre></div> </pre></div>
</div> </div>

Binary file not shown.

View File

@ -4,7 +4,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Search &#8212; FitTrackee 0.6.6 <title>Search &#8212; FitTrackee 0.6.7
documentation</title> documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" /> <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/bootstrap-sphinx.css" />
@ -44,7 +44,7 @@
</button> </button>
<a class="navbar-brand" href="index.html"> <a class="navbar-brand" href="index.html">
FitTrackee</a> FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.6.6 <span class="navbar-text navbar-version pull-left"><b>0.6.7
</b></span> </b></span>
</div> </div>

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,7 @@
<meta charset="utf-8" /> <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.17.1: http://docutils.sourceforge.net/" />
<title>Administrator &#8212; FitTrackee 0.6.6 <title>Administrator &#8212; FitTrackee 0.6.7
documentation</title> documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" /> <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/bootstrap-sphinx.css" />
@ -40,7 +40,7 @@
</button> </button>
<a class="navbar-brand" href="../index.html"> <a class="navbar-brand" href="../index.html">
FitTrackee</a> FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.6.6 <span class="navbar-text navbar-version pull-left"><b>0.6.7
</b></span> </b></span>
</div> </div>

View File

@ -5,7 +5,7 @@
<meta charset="utf-8" /> <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.17.1: http://docutils.sourceforge.net/" />
<title>Troubleshooting &#8212; FitTrackee 0.6.6 <title>Troubleshooting &#8212; FitTrackee 0.6.7
documentation</title> documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" /> <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/bootstrap-sphinx.css" />
@ -40,7 +40,7 @@
</button> </button>
<a class="navbar-brand" href="../index.html"> <a class="navbar-brand" href="../index.html">
FitTrackee</a> FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.6.6 <span class="navbar-text navbar-version pull-left"><b>0.6.7
</b></span> </b></span>
</div> </div>

View File

@ -5,7 +5,7 @@
<meta charset="utf-8" /> <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.17.1: http://docutils.sourceforge.net/" />
<title>User &#8212; FitTrackee 0.6.6 <title>User &#8212; FitTrackee 0.6.7
documentation</title> documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" /> <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/bootstrap-sphinx.css" />
@ -40,7 +40,7 @@
</button> </button>
<a class="navbar-brand" href="../index.html"> <a class="navbar-brand" href="../index.html">
FitTrackee</a> FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.6.6 <span class="navbar-text navbar-version pull-left"><b>0.6.7
</b></span> </b></span>
</div> </div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 539 KiB

After

Width:  |  Height:  |  Size: 539 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 368 KiB

After

Width:  |  Height:  |  Size: 368 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 103 KiB

View File

@ -395,13 +395,13 @@ Production environment
.. warning:: .. warning::
| Note that FitTrackee is under heavy development, some features may be unstable. | Note that FitTrackee is under heavy development, some features may be unstable.
- Download the last release (for now, it is the release v0.6.6): - Download the last release (for now, it is the release v0.6.7):
.. code:: bash .. code:: bash
$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.6.tar.gz $ wget https://github.com/SamR1/FitTrackee/archive/v0.6.7.tar.gz
$ tar -xzf v0.6.6.tar.gz $ tar -xzf v0.6.7.tar.gz
$ mv FitTrackee-0.6.6 FitTrackee $ mv FitTrackee-0.6.7 FitTrackee
$ cd FitTrackee $ cd FitTrackee
- Create **.env** from example and update it - Create **.env** from example and update it
@ -521,13 +521,13 @@ Prod environment
- Change to the directory where FitTrackee directory is located - Change to the directory where FitTrackee directory is located
- Download the last release (for now, it is the release v0.6.6) and overwrite existing files: - Download the last release (for now, it is the release v0.6.7) and overwrite existing files:
.. code:: bash .. code:: bash
$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.6.tar.gz $ wget https://github.com/SamR1/FitTrackee/archive/v0.6.7.tar.gz
$ tar -xzf v0.6.6.tar.gz $ tar -xzf v0.6.7.tar.gz
$ cp -R FitTrackee-0.6.6/* FitTrackee/ $ cp -R FitTrackee-0.6.7/* FitTrackee/
$ cd FitTrackee $ cd FitTrackee
- Update **.env** if needed (see `Environment variables <installation.html#environment-variables>`__). - Update **.env** if needed (see `Environment variables <installation.html#environment-variables>`__).

View File

@ -21,7 +21,7 @@ from werkzeug.middleware.proxy_fix import ProxyFix
from fittrackee.emails.email import EmailService from fittrackee.emails.email import EmailService
from fittrackee.request import CustomRequest from fittrackee.request import CustomRequest
VERSION = __version__ = '0.6.6' VERSION = __version__ = '0.6.7'
db = SQLAlchemy() db = SQLAlchemy()
bcrypt = Bcrypt() bcrypt = Bcrypt()
migrate = Migrate() migrate = Migrate()

View File

@ -48,7 +48,7 @@ def get_application_config() -> Union[Dict, HttpResponse]:
"max_users": 0, "max_users": 0,
"max_zip_file_size": 10485760, "max_zip_file_size": 10485760,
"map_attribution": "&copy; <a href=http://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors" "map_attribution": "&copy; <a href=http://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors"
"version": "0.6.6" "version": "0.6.7"
}, },
"status": "success" "status": "success"
} }
@ -98,7 +98,7 @@ def update_application_config(auth_user: User) -> Union[Dict, HttpResponse]:
"max_users": 10, "max_users": 10,
"max_zip_file_size": 10485760, "max_zip_file_size": 10485760,
"map_attribution": "&copy; <a href=http://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors" "map_attribution": "&copy; <a href=http://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors"
"version": "0.6.6" "version": "0.6.7"
}, },
"status": "success" "status": "success"
} }

View File

@ -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><script defer="defer" src="/static/js/chunk-vendors.e335684a.js"></script><script defer="defer" src="/static/js/app.a9b9439a.js"></script><link href="/static/css/app.e8b7692c.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> <!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.e335684a.js"></script><script defer="defer" src="/static/js/app.8517c25d.js"></script><link href="/static/css/app.e8b7692c.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>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -452,6 +452,120 @@ def gpx_file_wo_name() -> str:
) )
@pytest.fixture()
def gpx_file_with_offset() -> str:
return (
'<?xml version=\'1.0\' encoding=\'UTF-8\'?>'
'<gpx xmlns:gpxdata="http://www.cluetrust.com/XML/GPXDATA/1/0" xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1" xmlns:gpxext="http://www.garmin.com/xmlschemas/GpxExtensions/v3" xmlns="http://www.topografix.com/GPX/1/1">' # noqa
' <metadata/>'
' <trk>'
' <trkseg>'
' <trkpt lat="44.68095" lon="6.07367">'
' <ele>998</ele>'
' <time>2018-03-13T13:44:45+01:00</time>'
' </trkpt>'
' <trkpt lat="44.68091" lon="6.07367">'
' <ele>998</ele>'
' <time>2018-03-13T13:44:50+01:00</time>'
' </trkpt>'
' <trkpt lat="44.6808" lon="6.07364">'
' <ele>994</ele>'
' <time>2018-03-13T13:45:00+01:00</time>'
' </trkpt>'
' <trkpt lat="44.68075" lon="6.07364">'
' <ele>994</ele>'
' <time>2018-03-13T13:45:05+01:00</time>'
' </trkpt>'
' <trkpt lat="44.68071" lon="6.07364">'
' <ele>994</ele>'
' <time>2018-03-13T13:45:10+01:00</time>'
' </trkpt>'
' <trkpt lat="44.68049" lon="6.07361">'
' <ele>993</ele>'
' <time>2018-03-13T13:45:30+01:00</time>'
' </trkpt>'
' <trkpt lat="44.68019" lon="6.07356">'
' <ele>992</ele>'
' <time>2018-03-13T13:45:55+01:00</time>'
' </trkpt>'
' <trkpt lat="44.68014" lon="6.07355">'
' <ele>992</ele>'
' <time>2018-03-13T13:46:00+01:00</time>'
' </trkpt>'
' <trkpt lat="44.67995" lon="6.07358">'
' <ele>987</ele>'
' <time>2018-03-13T13:46:15+01:00</time>'
' </trkpt>'
' <trkpt lat="44.67977" lon="6.07364">'
' <ele>987</ele>'
' <time>2018-03-13T13:46:30+01:00</time>'
' </trkpt>'
' <trkpt lat="44.67972" lon="6.07367">'
' <ele>987</ele>'
' <time>2018-03-13T13:46:35+01:00</time>'
' </trkpt>'
' <trkpt lat="44.67966" lon="6.07368">'
' <ele>987</ele>'
' <time>2018-03-13T13:46:40+01:00</time>'
' </trkpt>'
' <trkpt lat="44.67961" lon="6.0737">'
' <ele>986</ele>'
' <time>2018-03-13T13:46:45+01:00</time>'
' </trkpt>'
' <trkpt lat="44.67938" lon="6.07377">'
' <ele>986</ele>'
' <time>2018-03-13T13:47:05+01:00</time>'
' </trkpt>'
' <trkpt lat="44.67933" lon="6.07381">'
' <ele>986</ele>'
' <time>2018-03-13T13:47:10+01:00</time>'
' </trkpt>'
' <trkpt lat="44.67922" lon="6.07385">'
' <ele>985</ele>'
' <time>2018-03-13T13:47:20+01:00</time>'
' </trkpt>'
' <trkpt lat="44.67911" lon="6.0739">'
' <ele>980</ele>'
' <time>2018-03-13T13:47:30+01:00</time>'
' </trkpt>'
' <trkpt lat="44.679" lon="6.07399">'
' <ele>980</ele>'
' <time>2018-03-13T13:47:40+01:00</time>'
' </trkpt>'
' <trkpt lat="44.67896" lon="6.07402">'
' <ele>980</ele>'
' <time>2018-03-13T13:47:45+01:00</time>'
' </trkpt>'
' <trkpt lat="44.67884" lon="6.07408">'
' <ele>979</ele>'
' <time>2018-03-13T13:47:55+01:00</time>'
' </trkpt>'
' <trkpt lat="44.67863" lon="6.07423">'
' <ele>981</ele>'
' <time>2018-03-13T13:48:15+01:00</time>'
' </trkpt>'
' <trkpt lat="44.67858" lon="6.07425">'
' <ele>980</ele>'
' <time>2018-03-13T13:48:20+01:00</time>'
' </trkpt>'
' <trkpt lat="44.67842" lon="6.07434">'
' <ele>979</ele>'
' <time>2018-03-13T13:48:35+01:00</time>'
' </trkpt>'
' <trkpt lat="44.67837" lon="6.07435">'
' <ele>979</ele>'
' <time>2018-03-13T13:48:40+01:00</time>'
' </trkpt>'
' <trkpt lat="44.67822" lon="6.07442">'
' <ele>975</ele>'
' <time>2018-03-13T13:48:55+01:00</time>'
' </trkpt>'
' </trkseg>'
' </trk>'
'</gpx>'
)
@pytest.fixture() @pytest.fixture()
def gpx_file_wo_track() -> str: def gpx_file_wo_track() -> str:
return ( return (

View File

@ -2,7 +2,7 @@ from unittest.mock import call, patch
import pytest import pytest
from flask import Flask from flask import Flask
from gpxpy.gpx import MovingData from gpxpy.gpx import IGNORE_TOP_SPEED_PERCENTILES, MovingData
from werkzeug.datastructures import FileStorage from werkzeug.datastructures import FileStorage
from fittrackee.users.models import User, UserSportPreference from fittrackee.users.models import User, UserSportPreference
@ -55,7 +55,12 @@ class TestStoppedSpeedThreshold:
assert gpx_track_segment_mock.call_args_list[0] == call( assert gpx_track_segment_mock.call_args_list[0] == call(
stopped_speed_threshold=expected_threshold stopped_speed_threshold=expected_threshold
) )
gpx_track_segment_mock.assert_called_with(expected_threshold) gpx_track_segment_mock.assert_called_with(
expected_threshold, # stopped_speed_threshold
False, # raw
IGNORE_TOP_SPEED_PERCENTILES, # speed_extreemes_percentiles
True, # ignore_nonstandard_distances
)
def test_it_calls_get_moving_data_with_threshold_depending_from_user_preference( # noqa def test_it_calls_get_moving_data_with_threshold_depending_from_user_preference( # noqa
self, self,
@ -85,4 +90,9 @@ class TestStoppedSpeedThreshold:
assert gpx_track_segment_mock.call_args_list[0] == call( assert gpx_track_segment_mock.call_args_list[0] == call(
stopped_speed_threshold=expected_threshold stopped_speed_threshold=expected_threshold
) )
gpx_track_segment_mock.assert_called_with(expected_threshold) gpx_track_segment_mock.assert_called_with(
expected_threshold, # stopped_speed_threshold
False, # raw
IGNORE_TOP_SPEED_PERCENTILES, # speed_extreemes_percentiles
True, # ignore_nonstandard_distances
)

View File

@ -1,9 +1,27 @@
from datetime import datetime
from statistics import mean from statistics import mean
from typing import List from typing import List, Union
import pytest import pytest
import pytz
from gpxpy.gpxfield import SimpleTZ
from fittrackee.workouts.utils.workouts import get_average_speed from fittrackee.workouts.utils.workouts import (
get_average_speed,
get_workout_datetime,
)
utc_datetime = datetime(
year=2022, month=6, day=11, hour=10, minute=23, second=00, tzinfo=pytz.utc
)
input_workout_dates = [
utc_datetime,
utc_datetime.replace(tzinfo=None),
utc_datetime.replace(tzinfo=SimpleTZ('Z')),
utc_datetime.astimezone(pytz.timezone('Europe/Paris')),
utc_datetime.astimezone(pytz.timezone('America/Toronto')),
'2022-06-11 12:23:00',
]
class TestWorkoutAverageSpeed: class TestWorkoutAverageSpeed:
@ -30,3 +48,68 @@ class TestWorkoutAverageSpeed:
assert get_average_speed( assert get_average_speed(
nb_workouts, total_average_speed, workout_average_speed nb_workouts, total_average_speed, workout_average_speed
) == mean(ave_speeds_list) ) == mean(ave_speeds_list)
class TestWorkoutGetWorkoutDatetime:
@pytest.mark.parametrize('input_workout_date', input_workout_dates)
def test_it_returns_naive_datetime(
self, input_workout_date: Union[datetime, str]
) -> None:
naive_workout_date, _ = get_workout_datetime(
workout_date=input_workout_date, user_timezone='Europe/Paris'
)
assert naive_workout_date == datetime(
year=2022, month=6, day=11, hour=10, minute=23, second=00
)
def test_it_return_naive_datetime_when_no_user_timezone(self) -> None:
naive_workout_date, _ = get_workout_datetime(
workout_date='2022-06-11 12:23:00', user_timezone=None
)
assert naive_workout_date == datetime(
year=2022, month=6, day=11, hour=12, minute=23, second=00
)
@pytest.mark.parametrize('input_workout_date', input_workout_dates)
def test_it_returns_datetime_with_user_timezone(
self, input_workout_date: Union[datetime, str]
) -> None:
timezone = 'Europe/Paris'
_, workout_date_with_tz = get_workout_datetime(
input_workout_date, user_timezone=timezone, with_timezone=True
)
assert workout_date_with_tz == datetime(
year=2022,
month=6,
day=11,
hour=10,
minute=23,
second=00,
tzinfo=pytz.utc,
).astimezone(pytz.timezone(timezone))
def test_it_does_not_return_datetime_with_user_timezone_when_no_user_tz(
self,
) -> None:
_, workout_date_with_tz = get_workout_datetime(
workout_date='2022-06-11 12:23:00',
user_timezone=None,
with_timezone=True,
)
assert workout_date_with_tz is None
def test_it_does_not_return_datetime_with_user_timezone_when_with_timezone_to_false( # noqa
self,
) -> None:
_, workout_date_with_tz = get_workout_datetime(
workout_date='2022-06-11 12:23:00',
user_timezone='Europe/Paris',
with_timezone=False,
)
assert workout_date_with_tz is None

View File

@ -969,7 +969,7 @@ class TestGetWorkoutsWithFiltersAndPagination(ApiTestCaseMixin):
class TestGetWorkout(ApiTestCaseMixin): class TestGetWorkout(ApiTestCaseMixin):
def test_it_gets_an_workout( def test_it_gets_a_workout(
self, self,
app: Flask, app: Flask,
user_1: User, user_1: User,
@ -1105,7 +1105,7 @@ class TestGetWorkout(ApiTestCaseMixin):
response, f'no gpx file for this workout (id: {workout_short_id})' response, f'no gpx file for this workout (id: {workout_short_id})'
) )
def test_it_returns_500_on_getting_gpx_if_an_workout_has_invalid_gpx_pathname( # noqa def test_it_returns_500_on_getting_gpx_if_a_workout_has_invalid_gpx_pathname( # noqa
self, self,
app: Flask, app: Flask,
user_1: User, user_1: User,
@ -1125,7 +1125,7 @@ class TestGetWorkout(ApiTestCaseMixin):
data = self.assert_500(response) data = self.assert_500(response)
assert 'data' not in data assert 'data' not in data
def test_it_returns_500_on_getting_chart_data_if_an_workout_has_invalid_gpx_pathname( # noqa def test_it_returns_500_on_getting_chart_data_if_a_workout_has_invalid_gpx_pathname( # noqa
self, self,
app: Flask, app: Flask,
user_1: User, user_1: User,

View File

@ -2,7 +2,7 @@ import json
import os import os
from datetime import datetime from datetime import datetime
from io import BytesIO from io import BytesIO
from typing import Dict from typing import Dict, Optional
from unittest.mock import Mock from unittest.mock import Mock
import pytest import pytest
@ -92,9 +92,7 @@ def assert_workout_data_with_gpx_segments(data: Dict) -> None:
assert data['data']['workouts'][0]['descent'] == 23.4 assert data['data']['workouts'][0]['descent'] == 23.4
assert data['data']['workouts'][0]['distance'] == 0.3 assert data['data']['workouts'][0]['distance'] == 0.3
assert data['data']['workouts'][0]['max_alt'] == 998.0 assert data['data']['workouts'][0]['max_alt'] == 998.0
assert ( assert data['data']['workouts'][0]['max_speed'] == 5.25
data['data']['workouts'][0]['max_speed'] is None
) # not enough points
assert data['data']['workouts'][0]['min_alt'] == 975.0 assert data['data']['workouts'][0]['min_alt'] == 975.0
assert data['data']['workouts'][0]['moving'] == '0:03:55' assert data['data']['workouts'][0]['moving'] == '0:03:55'
assert data['data']['workouts'][0]['pauses'] == '0:00:15' assert data['data']['workouts'][0]['pauses'] == '0:00:15'
@ -114,7 +112,7 @@ def assert_workout_data_with_gpx_segments(data: Dict) -> None:
assert segment['descent'] == 11.0 assert segment['descent'] == 11.0
assert segment['distance'] == 0.113 assert segment['distance'] == 0.113
assert segment['max_alt'] == 998.0 assert segment['max_alt'] == 998.0
assert segment['max_speed'] is None assert segment['max_speed'] == 5.25
assert segment['min_alt'] == 987.0 assert segment['min_alt'] == 987.0
assert segment['moving'] == '0:01:30' assert segment['moving'] == '0:01:30'
assert segment['pauses'] is None assert segment['pauses'] is None
@ -128,28 +126,33 @@ def assert_workout_data_with_gpx_segments(data: Dict) -> None:
assert segment['descent'] == 12.4 assert segment['descent'] == 12.4
assert segment['distance'] == 0.186 assert segment['distance'] == 0.186
assert segment['max_alt'] == 987.0 assert segment['max_alt'] == 987.0
assert segment['max_speed'] is None assert segment['max_speed'] == 5.12
assert segment['min_alt'] == 975.0 assert segment['min_alt'] == 975.0
assert segment['moving'] == '0:02:25' assert segment['moving'] == '0:02:25'
assert segment['pauses'] is None assert segment['pauses'] is None
records = data['data']['workouts'][0]['records'] records = data['data']['workouts'][0]['records']
assert len(records) == 3 assert len(records) == 4
assert records[0]['sport_id'] == 1 assert records[0]['sport_id'] == 1
assert records[0]['workout_id'] == data['data']['workouts'][0]['id'] assert records[0]['workout_id'] == data['data']['workouts'][0]['id']
assert records[0]['record_type'] == 'LD' assert records[0]['record_type'] == 'MS'
assert records[0]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT' assert records[0]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT'
assert records[0]['value'] == '0:03:55' assert records[0]['value'] == 5.25
assert records[1]['sport_id'] == 1 assert records[1]['sport_id'] == 1
assert records[1]['workout_id'] == data['data']['workouts'][0]['id'] assert records[1]['workout_id'] == data['data']['workouts'][0]['id']
assert records[1]['record_type'] == 'FD' assert records[1]['record_type'] == 'LD'
assert records[1]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT' assert records[1]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT'
assert records[1]['value'] == 0.3 assert records[1]['value'] == '0:03:55'
assert records[2]['sport_id'] == 1 assert records[2]['sport_id'] == 1
assert records[2]['workout_id'] == data['data']['workouts'][0]['id'] assert records[2]['workout_id'] == data['data']['workouts'][0]['id']
assert records[2]['record_type'] == 'AS' assert records[2]['record_type'] == 'FD'
assert records[2]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT' assert records[2]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT'
assert records[2]['value'] == 4.59 assert records[2]['value'] == 0.3
assert records[3]['sport_id'] == 1
assert records[3]['workout_id'] == data['data']['workouts'][0]['id']
assert records[3]['record_type'] == 'AS'
assert records[3]['workout_date'] == 'Tue, 13 Mar 2018 12:44:45 GMT'
assert records[3]['value'] == 4.59
def assert_workout_data_wo_gpx(data: Dict) -> None: def assert_workout_data_wo_gpx(data: Dict) -> None:
@ -222,7 +225,7 @@ class TestPostWorkoutWithGpx(ApiTestCaseMixin, CallArgsMixin):
self.assert_401(response) self.assert_401(response)
def test_it_adds_an_workout_with_gpx_file( def test_it_adds_a_workout_with_gpx_file(
self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str
) -> None: ) -> None:
client, auth_token = self.get_test_client_and_auth_token( client, auth_token = self.get_test_client_and_auth_token(
@ -248,7 +251,7 @@ class TestPostWorkoutWithGpx(ApiTestCaseMixin, CallArgsMixin):
assert 'just a workout' == data['data']['workouts'][0]['title'] assert 'just a workout' == data['data']['workouts'][0]['title']
assert_workout_data_with_gpx(data) assert_workout_data_with_gpx(data)
def test_it_adds_an_workout_with_gpx_without_name( def test_it_adds_a_workout_with_gpx_without_name(
self, self,
app: Flask, app: Flask,
user_1: User, user_1: User,
@ -281,7 +284,7 @@ class TestPostWorkoutWithGpx(ApiTestCaseMixin, CallArgsMixin):
) )
assert_workout_data_with_gpx(data) assert_workout_data_with_gpx(data)
def test_it_adds_an_workout_with_gpx_without_name_timezone( def test_it_adds_a_workout_with_gpx_without_name_timezone(
self, self,
app: Flask, app: Flask,
user_1: User, user_1: User,
@ -315,6 +318,41 @@ class TestPostWorkoutWithGpx(ApiTestCaseMixin, CallArgsMixin):
) )
assert_workout_data_with_gpx(data) assert_workout_data_with_gpx(data)
@pytest.mark.parametrize('input_user_timezone', [None, 'Europe/Paris'])
def test_it_adds_a_workout_with_gpx_with_offset(
self,
app: Flask,
user_1: User,
sport_1_cycling: Sport,
gpx_file_with_offset: str,
input_user_timezone: Optional[str],
) -> None:
user_1.timezone = input_user_timezone
client, auth_token = self.get_test_client_and_auth_token(
app, user_1.email
)
response = client.post(
'/api/workouts',
data=dict(
file=(
BytesIO(str.encode(gpx_file_with_offset)),
'example.gpx',
),
data='{"sport_id": 1}',
),
headers=dict(
content_type='multipart/form-data',
Authorization=f'Bearer {auth_token}',
),
)
data = json.loads(response.data.decode())
assert response.status_code == 201
assert 'created' in data['status']
assert len(data['data']['workouts']) == 1
assert_workout_data_with_gpx(data)
@pytest.mark.parametrize( @pytest.mark.parametrize(
'input_description,input_notes', 'input_description,input_notes',
[ [
@ -606,7 +644,7 @@ class TestPostWorkoutWithoutGpx(ApiTestCaseMixin):
self.assert_401(response) self.assert_401(response)
def test_it_adds_an_workout_without_gpx( def test_it_adds_a_workout_without_gpx(
self, app: Flask, user_1: User, sport_1_cycling: Sport self, app: Flask, user_1: User, sport_1_cycling: Sport
) -> None: ) -> None:
client, auth_token = self.get_test_client_and_auth_token( client, auth_token = self.get_test_client_and_auth_token(
@ -953,12 +991,12 @@ class TestPostAndGetWorkoutWithGpx(ApiTestCaseMixin):
self.assert_500(response) self.assert_500(response)
def test_it_gets_an_workout_created_with_gpx( def test_it_gets_a_workout_created_with_gpx(
self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str
) -> None: ) -> None:
return self.workout_assertion(app, user_1, gpx_file, False) return self.workout_assertion(app, user_1, gpx_file, False)
def test_it_gets_an_workout_created_with_gpx_with_segments( def test_it_gets_a_workout_created_with_gpx_with_segments(
self, self,
app: Flask, app: Flask,
user_1: User, user_1: User,
@ -969,7 +1007,7 @@ class TestPostAndGetWorkoutWithGpx(ApiTestCaseMixin):
app, user_1, gpx_file_with_segments, True app, user_1, gpx_file_with_segments, True
) )
def test_it_gets_chart_data_for_an_workout_created_with_gpx( def test_it_gets_chart_data_for_a_workout_created_with_gpx(
self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str
) -> None: ) -> None:
client, auth_token = self.get_test_client_and_auth_token( client, auth_token = self.get_test_client_and_auth_token(
@ -1000,7 +1038,7 @@ class TestPostAndGetWorkoutWithGpx(ApiTestCaseMixin):
assert data['message'] == '' assert data['message'] == ''
assert data['data']['chart_data'] != '' assert data['data']['chart_data'] != ''
def test_it_gets_segment_chart_data_for_an_workout_created_with_gpx( def test_it_gets_segment_chart_data_for_a_workout_created_with_gpx(
self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str
) -> None: ) -> None:
client, auth_token = self.get_test_client_and_auth_token( client, auth_token = self.get_test_client_and_auth_token(
@ -1125,7 +1163,7 @@ class TestPostAndGetWorkoutWithGpx(ApiTestCaseMixin):
class TestPostAndGetWorkoutWithoutGpx(ApiTestCaseMixin): class TestPostAndGetWorkoutWithoutGpx(ApiTestCaseMixin):
def test_it_add_and_gets_an_workout_wo_gpx( def test_it_add_and_gets_a_workout_wo_gpx(
self, app: Flask, user_1: User, sport_1_cycling: Sport self, app: Flask, user_1: User, sport_1_cycling: Sport
) -> None: ) -> None:
client, auth_token = self.get_test_client_and_auth_token( client, auth_token = self.get_test_client_and_auth_token(
@ -1158,7 +1196,7 @@ class TestPostAndGetWorkoutWithoutGpx(ApiTestCaseMixin):
assert len(data['data']['workouts']) == 1 assert len(data['data']['workouts']) == 1
assert_workout_data_wo_gpx(data) assert_workout_data_wo_gpx(data)
def test_it_adds_and_gets_an_workout_wo_gpx_notes( def test_it_adds_and_gets_a_workout_wo_gpx_notes(
self, app: Flask, user_1: User, sport_1_cycling: Sport self, app: Flask, user_1: User, sport_1_cycling: Sport
) -> None: ) -> None:
client, auth_token = self.get_test_client_and_auth_token( client, auth_token = self.get_test_client_and_auth_token(
@ -1194,7 +1232,7 @@ class TestPostAndGetWorkoutWithoutGpx(ApiTestCaseMixin):
class TestPostAndGetWorkoutUsingTimezones(ApiTestCaseMixin): class TestPostAndGetWorkoutUsingTimezones(ApiTestCaseMixin):
def test_it_add_and_gets_an_workout_wo_gpx_with_timezone( def test_it_add_and_gets_a_workout_wo_gpx_with_timezone(
self, app: Flask, user_1: User, sport_1_cycling: Sport self, app: Flask, user_1: User, sport_1_cycling: Sport
) -> None: ) -> None:
user_1.timezone = 'Europe/Paris' user_1.timezone = 'Europe/Paris'

View File

@ -9,7 +9,7 @@ from fittrackee.users.models import User
from fittrackee.workouts.models import Sport, Workout from fittrackee.workouts.models import Sport, Workout
from ..mixins import ApiTestCaseMixin from ..mixins import ApiTestCaseMixin
from .utils import get_random_short_id, post_an_workout from .utils import get_random_short_id, post_a_workout
def assert_workout_data_with_gpx(data: Dict, sport_id: int) -> None: def assert_workout_data_with_gpx(data: Dict, sport_id: int) -> None:
@ -56,7 +56,7 @@ def assert_workout_data_with_gpx(data: Dict, sport_id: int) -> None:
class TestEditWorkoutWithGpx(ApiTestCaseMixin): class TestEditWorkoutWithGpx(ApiTestCaseMixin):
def test_it_updates_title_for_an_workout_with_gpx( def test_it_updates_title_for_a_workout_with_gpx(
self, self,
app: Flask, app: Flask,
user_1: User, user_1: User,
@ -64,7 +64,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin):
sport_2_running: Sport, sport_2_running: Sport,
gpx_file: str, gpx_file: str,
) -> None: ) -> None:
token, workout_short_id = post_an_workout(app, gpx_file) token, workout_short_id = post_a_workout(app, gpx_file)
client = app.test_client() client = app.test_client()
response = client.patch( response = client.patch(
@ -100,7 +100,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin):
sport_2_running: Sport, sport_2_running: Sport,
gpx_file: str, gpx_file: str,
) -> None: ) -> None:
token, workout_short_id = post_an_workout(app, gpx_file) token, workout_short_id = post_a_workout(app, gpx_file)
client = app.test_client() client = app.test_client()
response = client.patch( response = client.patch(
@ -124,7 +124,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin):
sport_2_running: Sport, sport_2_running: Sport,
gpx_file: str, gpx_file: str,
) -> None: ) -> None:
token, workout_short_id = post_an_workout( token, workout_short_id = post_a_workout(
app, gpx_file, notes=uuid4().hex app, gpx_file, notes=uuid4().hex
) )
client = app.test_client() client = app.test_client()
@ -142,7 +142,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin):
assert len(data['data']['workouts']) == 1 assert len(data['data']['workouts']) == 1
assert data['data']['workouts'][0]['notes'] == '' assert data['data']['workouts'][0]['notes'] == ''
def test_it_raises_403_when_editing_an_workout_from_different_user( def test_it_raises_403_when_editing_a_workout_from_different_user(
self, self,
app: Flask, app: Flask,
user_1: User, user_1: User,
@ -151,7 +151,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin):
sport_2_running: Sport, sport_2_running: Sport,
gpx_file: str, gpx_file: str,
) -> None: ) -> None:
_, workout_short_id = post_an_workout(app, gpx_file) _, workout_short_id = post_a_workout(app, gpx_file)
client, auth_token = self.get_test_client_and_auth_token( client, auth_token = self.get_test_client_and_auth_token(
app, user_2.email app, user_2.email
) )
@ -173,7 +173,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin):
sport_2_running: Sport, sport_2_running: Sport,
gpx_file: str, gpx_file: str,
) -> None: ) -> None:
token, workout_short_id = post_an_workout(app, gpx_file) token, workout_short_id = post_a_workout(app, gpx_file)
client = app.test_client() client = app.test_client()
response = client.patch( response = client.patch(
@ -194,7 +194,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin):
def test_it_returns_400_if_payload_is_empty( def test_it_returns_400_if_payload_is_empty(
self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str
) -> None: ) -> None:
token, workout_short_id = post_an_workout(app, gpx_file) token, workout_short_id = post_a_workout(app, gpx_file)
client = app.test_client() client = app.test_client()
response = client.patch( response = client.patch(
@ -209,7 +209,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin):
def test_it_raises_500_if_sport_does_not_exists( def test_it_raises_500_if_sport_does_not_exists(
self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str
) -> None: ) -> None:
token, workout_short_id = post_an_workout(app, gpx_file) token, workout_short_id = post_a_workout(app, gpx_file)
client = app.test_client() client = app.test_client()
response = client.patch( response = client.patch(
@ -223,7 +223,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin):
class TestEditWorkoutWithoutGpx(ApiTestCaseMixin): class TestEditWorkoutWithoutGpx(ApiTestCaseMixin):
def test_it_updates_an_workout_wo_gpx( def test_it_updates_a_workout_wo_gpx(
self, self,
app: Flask, app: Flask,
user_1: User, user_1: User,
@ -364,7 +364,7 @@ class TestEditWorkoutWithoutGpx(ApiTestCaseMixin):
assert len(data['data']['workouts']) == 1 assert len(data['data']['workouts']) == 1
assert data['data']['workouts'][0]['notes'] == '' assert data['data']['workouts'][0]['notes'] == ''
def test_returns_403_when_editing_an_workout_wo_gpx_from_different_user( def test_returns_403_when_editing_a_workout_wo_gpx_from_different_user(
self, self,
app: Flask, app: Flask,
user_1: User, user_1: User,
@ -393,7 +393,7 @@ class TestEditWorkoutWithoutGpx(ApiTestCaseMixin):
self.assert_403(response) self.assert_403(response)
def test_it_updates_an_workout_wo_gpx_with_timezone( def test_it_updates_a_workout_wo_gpx_with_timezone(
self, self,
app: Flask, app: Flask,
user_1_paris: User, user_1_paris: User,
@ -468,7 +468,7 @@ class TestEditWorkoutWithoutGpx(ApiTestCaseMixin):
assert records[3]['workout_date'] == 'Tue, 15 May 2018 13:05:00 GMT' assert records[3]['workout_date'] == 'Tue, 15 May 2018 13:05:00 GMT'
assert records[3]['value'] == 8.0 assert records[3]['value'] == 8.0
def test_it_updates_only_sport_and_distance_an_workout_wo_gpx( def test_it_updates_only_sport_and_distance_a_workout_wo_gpx(
self, self,
app: Flask, app: Flask,
user_1: User, user_1: User,

View File

@ -7,7 +7,7 @@ from fittrackee.users.models import User
from fittrackee.workouts.models import Sport, Workout from fittrackee.workouts.models import Sport, Workout
from ..mixins import ApiTestCaseMixin from ..mixins import ApiTestCaseMixin
from .utils import get_random_short_id, post_an_workout from .utils import get_random_short_id, post_a_workout
def get_gpx_filepath(workout_id: int) -> str: def get_gpx_filepath(workout_id: int) -> str:
@ -16,10 +16,10 @@ def get_gpx_filepath(workout_id: int) -> str:
class TestDeleteWorkoutWithGpx(ApiTestCaseMixin): class TestDeleteWorkoutWithGpx(ApiTestCaseMixin):
def test_it_deletes_an_workout_with_gpx( def test_it_deletes_a_workout_with_gpx(
self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str
) -> None: ) -> None:
token, workout_short_id = post_an_workout(app, gpx_file) token, workout_short_id = post_a_workout(app, gpx_file)
client = app.test_client() client = app.test_client()
response = client.delete( response = client.delete(
@ -29,7 +29,7 @@ class TestDeleteWorkoutWithGpx(ApiTestCaseMixin):
assert response.status_code == 204 assert response.status_code == 204
def test_it_returns_403_when_deleting_an_workout_from_different_user( def test_it_returns_403_when_deleting_a_workout_from_different_user(
self, self,
app: Flask, app: Flask,
user_1: User, user_1: User,
@ -37,7 +37,7 @@ class TestDeleteWorkoutWithGpx(ApiTestCaseMixin):
sport_1_cycling: Sport, sport_1_cycling: Sport,
gpx_file: str, gpx_file: str,
) -> None: ) -> None:
_, workout_short_id = post_an_workout(app, gpx_file) _, workout_short_id = post_a_workout(app, gpx_file)
client, auth_token = self.get_test_client_and_auth_token( client, auth_token = self.get_test_client_and_auth_token(
app, user_2.email app, user_2.email
) )
@ -64,10 +64,10 @@ class TestDeleteWorkoutWithGpx(ApiTestCaseMixin):
data = self.assert_404(response) data = self.assert_404(response)
assert 'not found' in data['status'] assert 'not found' in data['status']
def test_it_returns_500_when_deleting_an_workout_with_gpx_invalid_file( def test_it_returns_500_when_deleting_a_workout_with_gpx_invalid_file(
self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str
) -> None: ) -> None:
token, workout_short_id = post_an_workout(app, gpx_file) token, workout_short_id = post_a_workout(app, gpx_file)
client = app.test_client() client = app.test_client()
gpx_filepath = get_gpx_filepath(1) gpx_filepath = get_gpx_filepath(1)
gpx_filepath = get_absolute_file_path(gpx_filepath) gpx_filepath = get_absolute_file_path(gpx_filepath)
@ -82,7 +82,7 @@ class TestDeleteWorkoutWithGpx(ApiTestCaseMixin):
class TestDeleteWorkoutWithoutGpx(ApiTestCaseMixin): class TestDeleteWorkoutWithoutGpx(ApiTestCaseMixin):
def test_it_deletes_an_workout_wo_gpx( def test_it_deletes_a_workout_wo_gpx(
self, self,
app: Flask, app: Flask,
user_1: User, user_1: User,
@ -98,7 +98,7 @@ class TestDeleteWorkoutWithoutGpx(ApiTestCaseMixin):
) )
assert response.status_code == 204 assert response.status_code == 204
def test_it_returns_403_when_deleting_an_workout_from_different_user( def test_it_returns_403_when_deleting_a_workout_from_different_user(
self, self,
app: Flask, app: Flask,
user_1: User, user_1: User,

View File

@ -12,7 +12,7 @@ def get_random_short_id() -> str:
return encode_uuid(uuid4()) return encode_uuid(uuid4())
def post_an_workout( def post_a_workout(
app: Flask, gpx_file: str, notes: Optional[str] = None app: Flask, gpx_file: str, notes: Optional[str] = None
) -> Tuple[str, str]: ) -> Tuple[str, str]:
client = app.test_client() client = app.test_client()

View File

@ -1,5 +1,5 @@
from datetime import timedelta from datetime import datetime, timedelta, timezone
from typing import Any, Dict, List, Optional, Tuple from typing import Any, Dict, List, Optional, Tuple, Union
import gpxpy.gpx import gpxpy.gpx
@ -16,9 +16,9 @@ def open_gpx_file(gpx_file: str) -> Optional[gpxpy.gpx.GPX]:
def get_gpx_data( def get_gpx_data(
parsed_gpx: gpxpy.gpx, parsed_gpx: Union[gpxpy.gpx.GPX, gpxpy.gpx.GPXTrackSegment],
max_speed: float, max_speed: float,
start: int, start: Union[datetime, None],
stopped_time_between_seg: timedelta, stopped_time_between_seg: timedelta,
stopped_speed_threshold: float, stopped_speed_threshold: float,
) -> Dict: ) -> Dict:
@ -32,7 +32,8 @@ def get_gpx_data(
duration = parsed_gpx.get_duration() duration = parsed_gpx.get_duration()
gpx_data['duration'] = ( gpx_data['duration'] = (
timedelta(seconds=duration) + stopped_time_between_seg timedelta(seconds=duration if duration else 0)
+ stopped_time_between_seg
) )
ele = parsed_gpx.get_elevation_extremes() ele = parsed_gpx.get_elevation_extremes()
@ -43,17 +44,23 @@ def get_gpx_data(
gpx_data['uphill'] = hill.uphill gpx_data['uphill'] = hill.uphill
gpx_data['downhill'] = hill.downhill gpx_data['downhill'] = hill.downhill
mv = parsed_gpx.get_moving_data( moving_data = parsed_gpx.get_moving_data(
stopped_speed_threshold=stopped_speed_threshold stopped_speed_threshold=stopped_speed_threshold
) )
gpx_data['moving_time'] = timedelta(seconds=mv.moving_time) if moving_data:
gpx_data['moving_time'] = timedelta(seconds=moving_data.moving_time)
gpx_data['stop_time'] = ( gpx_data['stop_time'] = (
timedelta(seconds=mv.stopped_time) + stopped_time_between_seg timedelta(seconds=moving_data.stopped_time)
+ stopped_time_between_seg
) )
distance = mv.moving_distance + mv.stopped_distance distance = moving_data.moving_distance + moving_data.stopped_distance
gpx_data['distance'] = distance / 1000 gpx_data['distance'] = distance / 1000
average_speed = distance / mv.moving_time if mv.moving_time > 0 else 0 average_speed = (
distance / moving_data.moving_time
if moving_data.moving_time > 0
else 0
)
gpx_data['average_speed'] = (average_speed / 1000) * 3600 gpx_data['average_speed'] = (average_speed / 1000) * 3600
return gpx_data return gpx_data
@ -72,9 +79,9 @@ def get_gpx_info(
if gpx is None: if gpx is None:
raise WorkoutGPXException('not found', 'No gpx file') raise WorkoutGPXException('not found', 'No gpx file')
gpx_data = {'name': gpx.tracks[0].name, 'segments': []} gpx_data: Dict = {'name': gpx.tracks[0].name, 'segments': []}
max_speed = 0 max_speed = 0.0
start = 0 start: Optional[datetime] = None
map_data = [] map_data = []
weather_data = [] weather_data = []
segments_nb = len(gpx.tracks[0].segments) segments_nb = len(gpx.tracks[0].segments)
@ -83,14 +90,15 @@ def get_gpx_info(
stopped_time_between_seg = no_stopped_time stopped_time_between_seg = no_stopped_time
for segment_idx, segment in enumerate(gpx.tracks[0].segments): for segment_idx, segment in enumerate(gpx.tracks[0].segments):
segment_start = 0 segment_start: Optional[datetime] = None
segment_points_nb = len(segment.points) segment_points_nb = len(segment.points)
for point_idx, point in enumerate(segment.points): for point_idx, point in enumerate(segment.points):
if point_idx == 0: if point_idx == 0:
segment_start = point.time
# first gpx point => get weather # first gpx point => get weather
if start == 0: if start is None:
start = point.time start = point.time
if update_weather_data: if point.time and update_weather_data:
weather_data.append(get_weather(point)) weather_data.append(get_weather(point))
# if a previous segment exists, calculate stopped time between # if a previous segment exists, calculate stopped time between
@ -108,13 +116,19 @@ def get_gpx_info(
if update_map_data: if update_map_data:
map_data.append([point.longitude, point.latitude]) map_data.append([point.longitude, point.latitude])
calculated_max_speed = segment.get_moving_data( moving_data = segment.get_moving_data(
stopped_speed_threshold=stopped_speed_threshold stopped_speed_threshold=stopped_speed_threshold
).max_speed )
segment_max_speed = calculated_max_speed if calculated_max_speed else 0 if moving_data:
calculated_max_speed = moving_data.max_speed
segment_max_speed = (
calculated_max_speed if calculated_max_speed else 0
)
if segment_max_speed > max_speed: if segment_max_speed > max_speed:
max_speed = segment_max_speed max_speed = segment_max_speed
else:
segment_max_speed = 0.0
segment_data = get_gpx_data( segment_data = get_gpx_data(
segment, segment,
@ -137,12 +151,16 @@ def get_gpx_info(
if update_map_data: if update_map_data:
bounds = gpx.get_bounds() bounds = gpx.get_bounds()
gpx_data['bounds'] = [ gpx_data['bounds'] = (
[
bounds.min_latitude, bounds.min_latitude,
bounds.min_longitude, bounds.min_longitude,
bounds.max_latitude, bounds.max_latitude,
bounds.max_longitude, bounds.max_longitude,
] ]
if bounds
else []
)
return gpx_data, map_data, weather_data return gpx_data, map_data, weather_data
@ -222,7 +240,11 @@ def get_chart_data(
'latitude': point.latitude, 'latitude': point.latitude,
'longitude': point.longitude, 'longitude': point.longitude,
'speed': speed, 'speed': speed,
'time': point.time, # workaround
# https://github.com/tkrajina/gpxpy/issues/209
'time': point.time.replace(
tzinfo=timezone(point.time.utcoffset())
),
} }
) )
previous_point = point previous_point = point

View File

@ -3,18 +3,22 @@ from typing import Dict, Optional
import forecastio import forecastio
import pytz import pytz
from gpxpy.gpx import GPXRoutePoint from gpxpy.gpx import GPXTrackPoint
from fittrackee import appLog from fittrackee import appLog
API_KEY = os.getenv('WEATHER_API_KEY') API_KEY = os.getenv('WEATHER_API_KEY')
def get_weather(point: GPXRoutePoint) -> Optional[Dict]: def get_weather(point: GPXTrackPoint) -> Optional[Dict]:
if not API_KEY or API_KEY == '': if not API_KEY or not point.time:
return None return None
try: try:
point_time = pytz.utc.localize(point.time) point_time = (
pytz.utc.localize(point.time)
if point.time.tzinfo is None
else point.time.astimezone(pytz.utc)
)
forecast = forecastio.load_forecast( forecast = forecastio.load_forecast(
API_KEY, API_KEY,
point.latitude, point.latitude,

View File

@ -22,31 +22,42 @@ from .gpx import get_gpx_info
from .maps import generate_map, get_map_hash from .maps import generate_map, get_map_hash
def get_datetime_with_tz( def get_workout_datetime(
timezone: str, workout_date: datetime, gpx_data: Optional[Dict] = None workout_date: Union[datetime, str],
) -> Tuple[Optional[datetime], datetime]: user_timezone: Optional[str],
date_str_format: Optional[str] = None,
with_timezone: bool = False,
) -> Tuple[datetime, Optional[datetime]]:
""" """
Return naive datetime and datetime with user timezone Return naive datetime and datetime with user timezone if with_timezone
""" """
workout_date_tz = None workout_date_with_user_tz = None
if timezone:
user_tz = pytz.timezone(timezone)
utc_tz = pytz.utc
if gpx_data:
# workout date in gpx is in UTC, but in naive datetime
fmt = '%Y-%m-%d %H:%M:%S'
workout_date_string = workout_date.strftime(fmt)
workout_date_tmp = utc_tz.localize(
datetime.strptime(workout_date_string, fmt)
)
workout_date_tz = workout_date_tmp.astimezone(user_tz)
else:
workout_date_tz = user_tz.localize(workout_date)
workout_date = workout_date_tz.astimezone(utc_tz)
# make datetime 'naive' like in gpx file
workout_date = workout_date.replace(tzinfo=None)
return workout_date_tz, workout_date # workout w/o gpx
if isinstance(workout_date, str):
if not date_str_format:
date_str_format = '%Y-%m-%d %H:%M:%S'
workout_date = datetime.strptime(workout_date, date_str_format)
if user_timezone:
workout_date = pytz.timezone(user_timezone).localize(workout_date)
if workout_date.tzinfo is None:
naive_workout_date = workout_date
if user_timezone and with_timezone:
pytz.utc.localize(naive_workout_date)
workout_date_with_user_tz = pytz.utc.localize(
naive_workout_date
).astimezone(pytz.timezone(user_timezone))
else:
naive_workout_date = workout_date.astimezone(pytz.utc).replace(
tzinfo=None
)
if user_timezone and with_timezone:
workout_date_with_user_tz = workout_date.astimezone(
pytz.timezone(user_timezone)
)
return naive_workout_date, workout_date_with_user_tz
def get_datetime_from_request_args( def get_datetime_from_request_args(
@ -57,25 +68,32 @@ def get_datetime_from_request_args(
date_from_str = params.get('from') date_from_str = params.get('from')
if date_from_str: if date_from_str:
date_from = datetime.strptime(date_from_str, '%Y-%m-%d') date_from, _ = get_workout_datetime(
_, date_from = get_datetime_with_tz(user.timezone, date_from) workout_date=date_from_str,
user_timezone=user.timezone,
date_str_format='%Y-%m-%d',
)
date_to_str = params.get('to') date_to_str = params.get('to')
if date_to_str: if date_to_str:
date_to = datetime.strptime( date_to, _ = get_workout_datetime(
f'{date_to_str} 23:59:59', '%Y-%m-%d %H:%M:%S' workout_date=f'{date_to_str} 23:59:59',
user_timezone=user.timezone,
) )
_, date_to = get_datetime_with_tz(user.timezone, date_to)
return date_from, date_to return date_from, date_to
def _remove_microseconds(delta: timedelta) -> timedelta:
return delta - timedelta(microseconds=delta.microseconds)
def update_workout_data( def update_workout_data(
workout: Union[Workout, WorkoutSegment], gpx_data: Dict workout: Union[Workout, WorkoutSegment], gpx_data: Dict
) -> Union[Workout, WorkoutSegment]: ) -> Union[Workout, WorkoutSegment]:
""" """
Update workout or workout segment with data from gpx file Update workout or workout segment with data from gpx file
""" """
workout.pauses = gpx_data['stop_time'] workout.pauses = _remove_microseconds(gpx_data['stop_time'])
workout.moving = gpx_data['moving_time'] workout.moving = _remove_microseconds(gpx_data['moving_time'])
workout.min_alt = gpx_data['elevation_min'] workout.min_alt = gpx_data['elevation_min']
workout.max_alt = gpx_data['elevation_max'] workout.max_alt = gpx_data['elevation_max']
workout.descent = gpx_data['downhill'] workout.descent = gpx_data['downhill']
@ -92,17 +110,17 @@ def create_workout(
Create Workout from data entered by user and from gpx if a gpx file is Create Workout from data entered by user and from gpx if a gpx file is
provided provided
""" """
workout_date = ( workout_date, workout_date_tz = get_workout_datetime(
gpx_data['start'] workout_date=gpx_data['start']
if gpx_data if gpx_data
else datetime.strptime(workout_data['workout_date'], '%Y-%m-%d %H:%M') else workout_data['workout_date'],
) date_str_format=None if gpx_data else '%Y-%m-%d %H:%M',
workout_date_tz, workout_date = get_datetime_with_tz( user_timezone=user.timezone,
user.timezone, workout_date, gpx_data with_timezone=True,
) )
duration = ( duration = (
gpx_data['duration'] _remove_microseconds(gpx_data['duration'])
if gpx_data if gpx_data
else timedelta(seconds=workout_data['duration']) else timedelta(seconds=workout_data['duration'])
) )
@ -202,11 +220,10 @@ def edit_workout(
workout.notes = workout_data.get('notes') workout.notes = workout_data.get('notes')
if not workout.gpx: if not workout.gpx:
if workout_data.get('workout_date'): if workout_data.get('workout_date'):
workout_date = datetime.strptime( workout.workout_date, _ = get_workout_datetime(
workout_data['workout_date'], '%Y-%m-%d %H:%M' workout_date=workout_data.get('workout_date', ''),
) date_str_format='%Y-%m-%d %H:%M',
_, workout.workout_date = get_datetime_with_tz( user_timezone=auth_user.timezone,
auth_user.timezone, workout_date
) )
if workout_data.get('duration'): if workout_data.get('duration'):

View File

@ -303,7 +303,7 @@ def get_workout(
auth_user: User, workout_short_id: str auth_user: User, workout_short_id: str
) -> Union[Dict, HttpResponse]: ) -> Union[Dict, HttpResponse]:
""" """
Get an workout Get a workout
**Example request**: **Example request**:
@ -405,7 +405,7 @@ def get_workout_data(
data_type: str, data_type: str,
segment_id: Optional[int] = None, segment_id: Optional[int] = None,
) -> Union[Dict, HttpResponse]: ) -> Union[Dict, HttpResponse]:
"""Get data from an workout gpx file""" """Get data from a workout gpx file"""
workout_uuid = decode_short_id(workout_short_id) workout_uuid = decode_short_id(workout_short_id)
workout = Workout.query.filter_by(uuid=workout_uuid).first() workout = Workout.query.filter_by(uuid=workout_uuid).first()
if not workout: if not workout:
@ -467,7 +467,7 @@ def get_workout_gpx(
auth_user: User, workout_short_id: str auth_user: User, workout_short_id: str
) -> Union[Dict, HttpResponse]: ) -> Union[Dict, HttpResponse]:
""" """
Get gpx file for an workout displayed on map with Leaflet Get gpx file for a workout displayed on map with Leaflet
**Example request**: **Example request**:
@ -517,7 +517,7 @@ def get_workout_chart_data(
auth_user: User, workout_short_id: str auth_user: User, workout_short_id: str
) -> Union[Dict, HttpResponse]: ) -> Union[Dict, HttpResponse]:
""" """
Get chart data from an workout gpx file, to display it with Recharts Get chart data from a workout gpx file, to display it with Recharts
**Example request**: **Example request**:
@ -587,7 +587,7 @@ def get_segment_gpx(
auth_user: User, workout_short_id: str, segment_id: int auth_user: User, workout_short_id: str, segment_id: int
) -> Union[Dict, HttpResponse]: ) -> Union[Dict, HttpResponse]:
""" """
Get gpx file for an workout segment displayed on map with Leaflet Get gpx file for a workout segment displayed on map with Leaflet
**Example request**: **Example request**:
@ -639,7 +639,7 @@ def get_segment_chart_data(
auth_user: User, workout_short_id: str, segment_id: int auth_user: User, workout_short_id: str, segment_id: int
) -> Union[Dict, HttpResponse]: ) -> Union[Dict, HttpResponse]:
""" """
Get chart data from an workout gpx file, to display it with Recharts Get chart data from a workout gpx file, to display it with Recharts
**Example request**: **Example request**:
@ -851,7 +851,7 @@ def get_map_tile(s: str, z: str, x: str, y: str) -> Tuple[Response, int]:
@require_auth(scopes='write') @require_auth(scopes='write')
def post_workout(auth_user: User) -> Union[Tuple[Dict, int], HttpResponse]: def post_workout(auth_user: User) -> Union[Tuple[Dict, int], HttpResponse]:
""" """
Post an workout with a gpx file Post a workout with a gpx file
**Example request**: **Example request**:
@ -1021,7 +1021,7 @@ def post_workout_no_gpx(
auth_user: User, auth_user: User,
) -> Union[Tuple[Dict, int], HttpResponse]: ) -> Union[Tuple[Dict, int], HttpResponse]:
""" """
Post an workout without gpx file Post a workout without gpx file
**Example request**: **Example request**:
@ -1111,7 +1111,8 @@ def post_workout_no_gpx(
"status": "success" "status": "success"
} }
:<json string workout_date: workout date (format: ``%Y-%m-%d %H:%M``) :<json string workout_date: workout date, in user timezone
(format: ``%Y-%m-%d %H:%M``)
:<json float distance: workout distance in km :<json float distance: workout distance in km
:<json integer duration: workout duration in seconds :<json integer duration: workout duration in seconds
:<json string notes: notes (not mandatory) :<json string notes: notes (not mandatory)
@ -1169,7 +1170,7 @@ def update_workout(
auth_user: User, workout_short_id: str auth_user: User, workout_short_id: str
) -> Union[Dict, HttpResponse]: ) -> Union[Dict, HttpResponse]:
""" """
Update an workout Update a workout
**Example request**: **Example request**:
@ -1261,7 +1262,8 @@ def update_workout(
:param string workout_short_id: workout short id :param string workout_short_id: workout short id
:<json string workout_date: workout date (format: ``%Y-%m-%d %H:%M``) :<json string workout_date: workout date in user timezone
(format: ``%Y-%m-%d %H:%M``)
(only for workout without gpx) (only for workout without gpx)
:<json float distance: workout distance in km :<json float distance: workout distance in km
(only for workout without gpx) (only for workout without gpx)
@ -1316,7 +1318,7 @@ def delete_workout(
auth_user: User, workout_short_id: str auth_user: User, workout_short_id: str
) -> Union[Tuple[Dict, int], HttpResponse]: ) -> Union[Tuple[Dict, int], HttpResponse]:
""" """
Delete an workout Delete a workout
**Example request**: **Example request**:

View File

@ -1,6 +1,6 @@
{ {
"name": "fittrackee_client", "name": "fittrackee_client",
"version": "0.6.6", "version": "0.6.7",
"private": true, "private": true,
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "serve": "vue-cli-service serve",

264
poetry.lock generated
View File

@ -8,11 +8,11 @@ python-versions = "*"
[[package]] [[package]]
name = "alembic" name = "alembic"
version = "1.7.7" version = "1.8.0"
description = "A database migration tool for SQLAlchemy." description = "A database migration tool for SQLAlchemy."
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.7"
[package.dependencies] [package.dependencies]
importlib-metadata = {version = "*", markers = "python_version < \"3.9\""} importlib-metadata = {version = "*", markers = "python_version < \"3.9\""}
@ -206,14 +206,14 @@ test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"]
[[package]] [[package]]
name = "coverage" name = "coverage"
version = "6.4" version = "6.4.1"
description = "Code coverage measurement for Python" description = "Code coverage measurement for Python"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
[package.dependencies] [package.dependencies]
tomli = {version = "*", optional = true, markers = "python_version < \"3.11\" and extra == \"toml\""} tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
[package.extras] [package.extras]
toml = ["tomli"] toml = ["tomli"]
@ -397,11 +397,11 @@ typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.8\""
[[package]] [[package]]
name = "gpxpy" name = "gpxpy"
version = "1.3.4" version = "1.5.0"
description = "GPX file parser and GPS track manipulation library" description = "GPX file parser and GPS track manipulation library"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" python-versions = ">=3.6"
[[package]] [[package]]
name = "greenlet" name = "greenlet"
@ -580,7 +580,7 @@ python-versions = "*"
[[package]] [[package]]
name = "mypy" name = "mypy"
version = "0.960" version = "0.961"
description = "Optional static typing for Python" description = "Optional static typing for Python"
category = "dev" category = "dev"
optional = false optional = false
@ -1001,7 +1001,7 @@ sphinx = ">=1.3.1"
[[package]] [[package]]
name = "redis" name = "redis"
version = "4.3.1" version = "4.3.3"
description = "Python client for Redis database and key-value store" description = "Python client for Redis database and key-value store"
category = "main" category = "main"
optional = false optional = false
@ -1020,20 +1020,20 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"
[[package]] [[package]]
name = "requests" name = "requests"
version = "2.27.1" version = "2.28.0"
description = "Python HTTP for Humans." description = "Python HTTP for Humans."
category = "main" category = "main"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" python-versions = ">=3.7, <4"
[package.dependencies] [package.dependencies]
certifi = ">=2017.4.17" certifi = ">=2017.4.17"
charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} charset-normalizer = ">=2.0.0,<2.1.0"
idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} idna = ">=2.5,<4"
urllib3 = ">=1.21.1,<1.27" urllib3 = ">=1.21.1,<1.27"
[package.extras] [package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
[[package]] [[package]]
@ -1238,7 +1238,7 @@ test = ["pytest"]
[[package]] [[package]]
name = "sqlalchemy" name = "sqlalchemy"
version = "1.4.36" version = "1.4.37"
description = "Database Abstraction Library" description = "Database Abstraction Library"
category = "main" category = "main"
optional = false optional = false
@ -1263,7 +1263,7 @@ mysql_connector = ["mysql-connector-python"]
oracle = ["cx_oracle (>=7,<8)", "cx_oracle (>=7)"] oracle = ["cx_oracle (>=7,<8)", "cx_oracle (>=7)"]
postgresql = ["psycopg2 (>=2.7)"] postgresql = ["psycopg2 (>=2.7)"]
postgresql_asyncpg = ["greenlet (!=0.4.17)", "asyncpg"] postgresql_asyncpg = ["greenlet (!=0.4.17)", "asyncpg"]
postgresql_pg8000 = ["pg8000 (>=1.16.6)"] postgresql_pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"]
postgresql_psycopg2binary = ["psycopg2-binary"] postgresql_psycopg2binary = ["psycopg2-binary"]
postgresql_psycopg2cffi = ["psycopg2cffi"] postgresql_psycopg2cffi = ["psycopg2cffi"]
pymysql = ["pymysql (<1)", "pymysql"] pymysql = ["pymysql (<1)", "pymysql"]
@ -1325,7 +1325,7 @@ python-versions = ">=3.7"
[[package]] [[package]]
name = "trio" name = "trio"
version = "0.20.0" version = "0.21.0"
description = "A friendly Python library for async concurrency and I/O" description = "A friendly Python library for async concurrency and I/O"
category = "dev" category = "dev"
optional = false optional = false
@ -1363,7 +1363,7 @@ python-versions = ">=3.6"
[[package]] [[package]]
name = "types-freezegun" name = "types-freezegun"
version = "1.1.9" version = "1.1.10"
description = "Typing stubs for freezegun" description = "Typing stubs for freezegun"
category = "dev" category = "dev"
optional = false optional = false
@ -1379,7 +1379,7 @@ python-versions = "*"
[[package]] [[package]]
name = "types-requests" name = "types-requests"
version = "2.27.29" version = "2.27.30"
description = "Typing stubs for requests" description = "Typing stubs for requests"
category = "dev" category = "dev"
optional = false optional = false
@ -1477,7 +1477,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.7" python-versions = "^3.7"
content-hash = "84bb7759a500c94555fb8d8e5ca25bd98b287f6c53c80eb259bcc3c0d31b1031" content-hash = "6d019761837f6c9003d5749a29af885eed271bc8409c9b3dcb07f290247f7437"
[metadata.files] [metadata.files]
alabaster = [ alabaster = [
@ -1485,8 +1485,8 @@ alabaster = [
{file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"},
] ]
alembic = [ alembic = [
{file = "alembic-1.7.7-py3-none-any.whl", hash = "sha256:29be0856ec7591c39f4e1cb10f198045d890e6e2274cf8da80cb5e721a09642b"}, {file = "alembic-1.8.0-py3-none-any.whl", hash = "sha256:b5ae4bbfc7d1302ed413989d39474d102e7cfa158f6d5969d2497955ffe85a30"},
{file = "alembic-1.7.7.tar.gz", hash = "sha256:4961248173ead7ce8a21efb3de378f13b8398e6630fab0eb258dc74a8af24c58"}, {file = "alembic-1.8.0.tar.gz", hash = "sha256:a2d4d90da70b30e70352cd9455e35873a255a31402a438fe24815758d7a0e5e1"},
] ]
async-generator = [ async-generator = [
{file = "async_generator-1.10-py3-none-any.whl", hash = "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b"}, {file = "async_generator-1.10-py3-none-any.whl", hash = "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b"},
@ -1627,47 +1627,47 @@ commonmark = [
{file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"},
] ]
coverage = [ coverage = [
{file = "coverage-6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:50ed480b798febce113709846b11f5d5ed1e529c88d8ae92f707806c50297abf"}, {file = "coverage-6.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f1d5aa2703e1dab4ae6cf416eb0095304f49d004c39e9db1d86f57924f43006b"},
{file = "coverage-6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:26f8f92699756cb7af2b30720de0c5bb8d028e923a95b6d0c891088025a1ac8f"}, {file = "coverage-6.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ce1b258493cbf8aec43e9b50d89982346b98e9ffdfaae8ae5793bc112fb0068"},
{file = "coverage-6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60c2147921da7f4d2d04f570e1838db32b95c5509d248f3fe6417e91437eaf41"}, {file = "coverage-6.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c4e737f60c6936460c5be330d296dd5b48b3963f48634c53b3f7deb0f34ec4"},
{file = "coverage-6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:750e13834b597eeb8ae6e72aa58d1d831b96beec5ad1d04479ae3772373a8088"}, {file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84e65ef149028516c6d64461b95a8dbcfce95cfd5b9eb634320596173332ea84"},
{file = "coverage-6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af5b9ee0fc146e907aa0f5fb858c3b3da9199d78b7bb2c9973d95550bd40f701"}, {file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f69718750eaae75efe506406c490d6fc5a6161d047206cc63ce25527e8a3adad"},
{file = "coverage-6.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a022394996419142b33a0cf7274cb444c01d2bb123727c4bb0b9acabcb515dea"}, {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e57816f8ffe46b1df8f12e1b348f06d164fd5219beba7d9433ba79608ef011cc"},
{file = "coverage-6.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5a78cf2c43b13aa6b56003707c5203f28585944c277c1f3f109c7b041b16bd39"}, {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:01c5615d13f3dd3aa8543afc069e5319cfa0c7d712f6e04b920431e5c564a749"},
{file = "coverage-6.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9229d074e097f21dfe0643d9d0140ee7433814b3f0fc3706b4abffd1e3038632"}, {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75ab269400706fab15981fd4bd5080c56bd5cc07c3bccb86aab5e1d5a88dc8f4"},
{file = "coverage-6.4-cp310-cp310-win32.whl", hash = "sha256:fb45fe08e1abc64eb836d187b20a59172053999823f7f6ef4f18a819c44ba16f"}, {file = "coverage-6.4.1-cp310-cp310-win32.whl", hash = "sha256:a7f3049243783df2e6cc6deafc49ea123522b59f464831476d3d1448e30d72df"},
{file = "coverage-6.4-cp310-cp310-win_amd64.whl", hash = "sha256:3cfd07c5889ddb96a401449109a8b97a165be9d67077df6802f59708bfb07720"}, {file = "coverage-6.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:ee2ddcac99b2d2aec413e36d7a429ae9ebcadf912946b13ffa88e7d4c9b712d6"},
{file = "coverage-6.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:03014a74023abaf5a591eeeaf1ac66a73d54eba178ff4cb1fa0c0a44aae70383"}, {file = "coverage-6.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb73e0011b8793c053bfa85e53129ba5f0250fdc0392c1591fd35d915ec75c46"},
{file = "coverage-6.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c82f2cd69c71698152e943f4a5a6b83a3ab1db73b88f6e769fabc86074c3b08"}, {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106c16dfe494de3193ec55cac9640dd039b66e196e4641fa8ac396181578b982"},
{file = "coverage-6.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b546cf2b1974ddc2cb222a109b37c6ed1778b9be7e6b0c0bc0cf0438d9e45a6"}, {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87f4f3df85aa39da00fd3ec4b5abeb7407e82b68c7c5ad181308b0e2526da5d4"},
{file = "coverage-6.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc173f1ce9ffb16b299f51c9ce53f66a62f4d975abe5640e976904066f3c835d"}, {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:961e2fb0680b4f5ad63234e0bf55dfb90d302740ae9c7ed0120677a94a1590cb"},
{file = "coverage-6.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c53ad261dfc8695062fc8811ac7c162bd6096a05a19f26097f411bdf5747aee7"}, {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cec3a0f75c8f1031825e19cd86ee787e87cf03e4fd2865c79c057092e69e3a3b"},
{file = "coverage-6.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:eef5292b60b6de753d6e7f2d128d5841c7915fb1e3321c3a1fe6acfe76c38052"}, {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:129cd05ba6f0d08a766d942a9ed4b29283aff7b2cccf5b7ce279d50796860bb3"},
{file = "coverage-6.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:543e172ce4c0de533fa892034cce260467b213c0ea8e39da2f65f9a477425211"}, {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bf5601c33213d3cb19d17a796f8a14a9eaa5e87629a53979a5981e3e3ae166f6"},
{file = "coverage-6.4-cp37-cp37m-win32.whl", hash = "sha256:00c8544510f3c98476bbd58201ac2b150ffbcce46a8c3e4fb89ebf01998f806a"}, {file = "coverage-6.4.1-cp37-cp37m-win32.whl", hash = "sha256:269eaa2c20a13a5bf17558d4dc91a8d078c4fa1872f25303dddcbba3a813085e"},
{file = "coverage-6.4-cp37-cp37m-win_amd64.whl", hash = "sha256:b84ab65444dcc68d761e95d4d70f3cfd347ceca5a029f2ffec37d4f124f61311"}, {file = "coverage-6.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f02cbbf8119db68455b9d763f2f8737bb7db7e43720afa07d8eb1604e5c5ae28"},
{file = "coverage-6.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d548edacbf16a8276af13063a2b0669d58bbcfca7c55a255f84aac2870786a61"}, {file = "coverage-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ffa9297c3a453fba4717d06df579af42ab9a28022444cae7fa605af4df612d54"},
{file = "coverage-6.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:033ebec282793bd9eb988d0271c211e58442c31077976c19c442e24d827d356f"}, {file = "coverage-6.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:145f296d00441ca703a659e8f3eb48ae39fb083baba2d7ce4482fb2723e050d9"},
{file = "coverage-6.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:742fb8b43835078dd7496c3c25a1ec8d15351df49fb0037bffb4754291ef30ce"}, {file = "coverage-6.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d44996140af8b84284e5e7d398e589574b376fb4de8ccd28d82ad8e3bea13"},
{file = "coverage-6.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55fae115ef9f67934e9f1103c9ba826b4c690e4c5bcf94482b8b2398311bf9c"}, {file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2bd9a6fc18aab8d2e18f89b7ff91c0f34ff4d5e0ba0b33e989b3cd4194c81fd9"},
{file = "coverage-6.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cd698341626f3c77784858427bad0cdd54a713115b423d22ac83a28303d1d95"}, {file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3384f2a3652cef289e38100f2d037956194a837221edd520a7ee5b42d00cc605"},
{file = "coverage-6.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:62d382f7d77eeeaff14b30516b17bcbe80f645f5cf02bb755baac376591c653c"}, {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9b3e07152b4563722be523e8cd0b209e0d1a373022cfbde395ebb6575bf6790d"},
{file = "coverage-6.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:016d7f5cf1c8c84f533a3c1f8f36126fbe00b2ec0ccca47cc5731c3723d327c6"}, {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1480ff858b4113db2718848d7b2d1b75bc79895a9c22e76a221b9d8d62496428"},
{file = "coverage-6.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:69432946f154c6add0e9ede03cc43b96e2ef2733110a77444823c053b1ff5166"}, {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:865d69ae811a392f4d06bde506d531f6a28a00af36f5c8649684a9e5e4a85c83"},
{file = "coverage-6.4-cp38-cp38-win32.whl", hash = "sha256:83bd142cdec5e4a5c4ca1d4ff6fa807d28460f9db919f9f6a31babaaa8b88426"}, {file = "coverage-6.4.1-cp38-cp38-win32.whl", hash = "sha256:664a47ce62fe4bef9e2d2c430306e1428ecea207ffd68649e3b942fa8ea83b0b"},
{file = "coverage-6.4-cp38-cp38-win_amd64.whl", hash = "sha256:4002f9e8c1f286e986fe96ec58742b93484195defc01d5cc7809b8f7acb5ece3"}, {file = "coverage-6.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:26dff09fb0d82693ba9e6231248641d60ba606150d02ed45110f9ec26404ed1c"},
{file = "coverage-6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e4f52c272fdc82e7c65ff3f17a7179bc5f710ebc8ce8a5cadac81215e8326740"}, {file = "coverage-6.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d9c80df769f5ec05ad21ea34be7458d1dc51ff1fb4b2219e77fe24edf462d6df"},
{file = "coverage-6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b5578efe4038be02d76c344007b13119b2b20acd009a88dde8adec2de4f630b5"}, {file = "coverage-6.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:39ee53946bf009788108b4dd2894bf1349b4e0ca18c2016ffa7d26ce46b8f10d"},
{file = "coverage-6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8099ea680201c2221f8468c372198ceba9338a5fec0e940111962b03b3f716a"}, {file = "coverage-6.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5b66caa62922531059bc5ac04f836860412f7f88d38a476eda0a6f11d4724f4"},
{file = "coverage-6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a00441f5ea4504f5abbc047589d09e0dc33eb447dc45a1a527c8b74bfdd32c65"}, {file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd180ed867e289964404051a958f7cccabdeed423f91a899829264bb7974d3d3"},
{file = "coverage-6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e76bd16f0e31bc2b07e0fb1379551fcd40daf8cdf7e24f31a29e442878a827c"}, {file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84631e81dd053e8a0d4967cedab6db94345f1c36107c71698f746cb2636c63e3"},
{file = "coverage-6.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8d2e80dd3438e93b19e1223a9850fa65425e77f2607a364b6fd134fcd52dc9df"}, {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8c08da0bd238f2970230c2a0d28ff0e99961598cb2e810245d7fc5afcf1254e8"},
{file = "coverage-6.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:341e9c2008c481c5c72d0e0dbf64980a4b2238631a7f9780b0fe2e95755fb018"}, {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d42c549a8f41dc103a8004b9f0c433e2086add8a719da00e246e17cbe4056f72"},
{file = "coverage-6.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:21e6686a95025927775ac501e74f5940cdf6fe052292f3a3f7349b0abae6d00f"}, {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:309ce4a522ed5fca432af4ebe0f32b21d6d7ccbb0f5fcc99290e71feba67c264"},
{file = "coverage-6.4-cp39-cp39-win32.whl", hash = "sha256:968ed5407f9460bd5a591cefd1388cc00a8f5099de9e76234655ae48cfdbe2c3"}, {file = "coverage-6.4.1-cp39-cp39-win32.whl", hash = "sha256:fdb6f7bd51c2d1714cea40718f6149ad9be6a2ee7d93b19e9f00934c0f2a74d9"},
{file = "coverage-6.4-cp39-cp39-win_amd64.whl", hash = "sha256:e35217031e4b534b09f9b9a5841b9344a30a6357627761d4218818b865d45055"}, {file = "coverage-6.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:342d4aefd1c3e7f620a13f4fe563154d808b69cccef415415aece4c786665397"},
{file = "coverage-6.4-pp36.pp37.pp38-none-any.whl", hash = "sha256:e637ae0b7b481905358624ef2e81d7fb0b1af55f5ff99f9ba05442a444b11e45"}, {file = "coverage-6.4.1-pp36.pp37.pp38-none-any.whl", hash = "sha256:4803e7ccf93230accb928f3a68f00ffa80a88213af98ed338a57ad021ef06815"},
{file = "coverage-6.4.tar.gz", hash = "sha256:727dafd7f67a6e1cad808dc884bd9c5a2f6ef1f8f6d2f22b37b96cb0080d4f49"}, {file = "coverage-6.4.1.tar.gz", hash = "sha256:4321f075095a096e70aff1d002030ee612b65a205a0a0f5b815280d5dc58100c"},
] ]
cryptography = [ cryptography = [
{file = "cryptography-37.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:ef15c2df7656763b4ff20a9bc4381d8352e6640cfeb95c2972c38ef508e75181"}, {file = "cryptography-37.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:ef15c2df7656763b4ff20a9bc4381d8352e6640cfeb95c2972c38ef508e75181"},
@ -1742,7 +1742,7 @@ gitpython = [
{file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"}, {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"},
] ]
gpxpy = [ gpxpy = [
{file = "gpxpy-1.3.4.tar.gz", hash = "sha256:4a0f072ae5bdf9270c7450e452f93a6c5c91d888114e8d78868a8f163b0dbb15"}, {file = "gpxpy-1.5.0.tar.gz", hash = "sha256:e6993a8945eae07a833cd304b88bbc6c3c132d63b2bf4a9b0a5d9097616b8708"},
] ]
greenlet = [ greenlet = [
{file = "greenlet-1.1.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:58df5c2a0e293bf665a51f8a100d3e9956febfbf1d9aaf8c0677cf70218910c6"}, {file = "greenlet-1.1.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:58df5c2a0e293bf665a51f8a100d3e9956febfbf1d9aaf8c0677cf70218910c6"},
@ -1890,29 +1890,29 @@ mccabe = [
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
] ]
mypy = [ mypy = [
{file = "mypy-0.960-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3a3e525cd76c2c4f90f1449fd034ba21fcca68050ff7c8397bb7dd25dd8b8248"}, {file = "mypy-0.961-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:697540876638ce349b01b6786bc6094ccdaba88af446a9abb967293ce6eaa2b0"},
{file = "mypy-0.960-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7a76dc4f91e92db119b1be293892df8379b08fd31795bb44e0ff84256d34c251"}, {file = "mypy-0.961-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b117650592e1782819829605a193360a08aa99f1fc23d1d71e1a75a142dc7e15"},
{file = "mypy-0.960-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ffdad80a92c100d1b0fe3d3cf1a4724136029a29afe8566404c0146747114382"}, {file = "mypy-0.961-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bdd5ca340beffb8c44cb9dc26697628d1b88c6bddf5c2f6eb308c46f269bb6f3"},
{file = "mypy-0.960-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7d390248ec07fa344b9f365e6ed9d205bd0205e485c555bed37c4235c868e9d5"}, {file = "mypy-0.961-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3e09f1f983a71d0672bbc97ae33ee3709d10c779beb613febc36805a6e28bb4e"},
{file = "mypy-0.960-cp310-cp310-win_amd64.whl", hash = "sha256:925aa84369a07846b7f3b8556ccade1f371aa554f2bd4fb31cb97a24b73b036e"}, {file = "mypy-0.961-cp310-cp310-win_amd64.whl", hash = "sha256:e999229b9f3198c0c880d5e269f9f8129c8862451ce53a011326cad38b9ccd24"},
{file = "mypy-0.960-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:239d6b2242d6c7f5822163ee082ef7a28ee02e7ac86c35593ef923796826a385"}, {file = "mypy-0.961-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b24be97351084b11582fef18d79004b3e4db572219deee0212078f7cf6352723"},
{file = "mypy-0.960-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f1ba54d440d4feee49d8768ea952137316d454b15301c44403db3f2cb51af024"}, {file = "mypy-0.961-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f4a21d01fc0ba4e31d82f0fff195682e29f9401a8bdb7173891070eb260aeb3b"},
{file = "mypy-0.960-cp36-cp36m-win_amd64.whl", hash = "sha256:cb7752b24528c118a7403ee955b6a578bfcf5879d5ee91790667c8ea511d2085"}, {file = "mypy-0.961-cp36-cp36m-win_amd64.whl", hash = "sha256:439c726a3b3da7ca84a0199a8ab444cd8896d95012c4a6c4a0d808e3147abf5d"},
{file = "mypy-0.960-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:826a2917c275e2ee05b7c7b736c1e6549a35b7ea5a198ca457f8c2ebea2cbecf"}, {file = "mypy-0.961-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a0b53747f713f490affdceef835d8f0cb7285187a6a44c33821b6d1f46ed813"},
{file = "mypy-0.960-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3eabcbd2525f295da322dff8175258f3fc4c3eb53f6d1929644ef4d99b92e72d"}, {file = "mypy-0.961-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0e9f70df36405c25cc530a86eeda1e0867863d9471fe76d1273c783df3d35c2e"},
{file = "mypy-0.960-cp37-cp37m-win_amd64.whl", hash = "sha256:f47322796c412271f5aea48381a528a613f33e0a115452d03ae35d673e6064f8"}, {file = "mypy-0.961-cp37-cp37m-win_amd64.whl", hash = "sha256:b88f784e9e35dcaa075519096dc947a388319cb86811b6af621e3523980f1c8a"},
{file = "mypy-0.960-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2c7f8bb9619290836a4e167e2ef1f2cf14d70e0bc36c04441e41487456561409"}, {file = "mypy-0.961-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d5aaf1edaa7692490f72bdb9fbd941fbf2e201713523bdb3f4038be0af8846c6"},
{file = "mypy-0.960-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fbfb873cf2b8d8c3c513367febde932e061a5f73f762896826ba06391d932b2a"}, {file = "mypy-0.961-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9f5f5a74085d9a81a1f9c78081d60a0040c3efb3f28e5c9912b900adf59a16e6"},
{file = "mypy-0.960-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc537885891382e08129d9862553b3d00d4be3eb15b8cae9e2466452f52b0117"}, {file = "mypy-0.961-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f4b794db44168a4fc886e3450201365c9526a522c46ba089b55e1f11c163750d"},
{file = "mypy-0.960-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:481f98c6b24383188c928f33dd2f0776690807e12e9989dd0419edd5c74aa53b"}, {file = "mypy-0.961-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:64759a273d590040a592e0f4186539858c948302c653c2eac840c7a3cd29e51b"},
{file = "mypy-0.960-cp38-cp38-win_amd64.whl", hash = "sha256:29dc94d9215c3eb80ac3c2ad29d0c22628accfb060348fd23d73abe3ace6c10d"}, {file = "mypy-0.961-cp38-cp38-win_amd64.whl", hash = "sha256:63e85a03770ebf403291ec50097954cc5caf2a9205c888ce3a61bd3f82e17569"},
{file = "mypy-0.960-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:33d53a232bb79057f33332dbbb6393e68acbcb776d2f571ba4b1d50a2c8ba873"}, {file = "mypy-0.961-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f1332964963d4832a94bebc10f13d3279be3ce8f6c64da563d6ee6e2eeda932"},
{file = "mypy-0.960-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8d645e9e7f7a5da3ec3bbcc314ebb9bb22c7ce39e70367830eb3c08d0140b9ce"}, {file = "mypy-0.961-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:006be38474216b833eca29ff6b73e143386f352e10e9c2fbe76aa8549e5554f5"},
{file = "mypy-0.960-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:85cf2b14d32b61db24ade8ac9ae7691bdfc572a403e3cb8537da936e74713275"}, {file = "mypy-0.961-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9940e6916ed9371809b35b2154baf1f684acba935cd09928952310fbddaba648"},
{file = "mypy-0.960-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a85a20b43fa69efc0b955eba1db435e2ffecb1ca695fe359768e0503b91ea89f"}, {file = "mypy-0.961-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a5ea0875a049de1b63b972456542f04643daf320d27dc592d7c3d9cd5d9bf950"},
{file = "mypy-0.960-cp39-cp39-win_amd64.whl", hash = "sha256:0ebfb3f414204b98c06791af37a3a96772203da60636e2897408517fcfeee7a8"}, {file = "mypy-0.961-cp39-cp39-win_amd64.whl", hash = "sha256:1ece702f29270ec6af25db8cf6185c04c02311c6bb21a69f423d40e527b75c56"},
{file = "mypy-0.960-py3-none-any.whl", hash = "sha256:bfd4f6536bd384c27c392a8b8f790fd0ed5c0cf2f63fc2fed7bce56751d53026"}, {file = "mypy-0.961-py3-none-any.whl", hash = "sha256:03c6cc893e7563e7b2949b969e63f02c000b32502a1b4d1314cabe391aa87d66"},
{file = "mypy-0.960.tar.gz", hash = "sha256:d4fccf04c1acf750babd74252e0f2db6bd2ac3aa8fe960797d9f3ef41cf2bfd4"}, {file = "mypy-0.961.tar.gz", hash = "sha256:f730d56cb924d371c26b8eaddeea3cc07d78ff51c521c6d04899ac6904b75492"},
] ]
mypy-extensions = [ mypy-extensions = [
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
@ -2175,12 +2175,12 @@ recommonmark = [
{file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"}, {file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"},
] ]
redis = [ redis = [
{file = "redis-4.3.1-py3-none-any.whl", hash = "sha256:84316970995a7adb907a56754d2b92d88fc2d252963dc5ac34c88f0f1a22c25d"}, {file = "redis-4.3.3-py3-none-any.whl", hash = "sha256:f57f8df5d238a8ecf92f499b6b21467bfee6c13d89953c27edf1e2bc673622e7"},
{file = "redis-4.3.1.tar.gz", hash = "sha256:94b617b4cd296e94991146f66fc5559756fbefe9493604f0312e4d3298ac63e9"}, {file = "redis-4.3.3.tar.gz", hash = "sha256:2f7a57cf4af15cd543c4394bcbe2b9148db2606a37edba755368836e3a1d053e"},
] ]
requests = [ requests = [
{file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, {file = "requests-2.28.0-py3-none-any.whl", hash = "sha256:bc7861137fbce630f17b03d3ad02ad0bf978c844f3536d0edda6499dafce2b6f"},
{file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, {file = "requests-2.28.0.tar.gz", hash = "sha256:d568723a7ebd25875d8d1eaf5dfa068cd2fc8194b2e483d7b1f7c81918dbec6b"},
] ]
responses = [ responses = [
{file = "responses-0.21.0-py3-none-any.whl", hash = "sha256:2dcc863ba63963c0c3d9ee3fa9507cbe36b7d7b0fccb4f0bdfd9e96c539b1487"}, {file = "responses-0.21.0-py3-none-any.whl", hash = "sha256:2dcc863ba63963c0c3d9ee3fa9507cbe36b7d7b0fccb4f0bdfd9e96c539b1487"},
@ -2250,42 +2250,42 @@ sphinxcontrib-serializinghtml = [
{file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"},
] ]
sqlalchemy = [ sqlalchemy = [
{file = "SQLAlchemy-1.4.36-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:81e53bd383c2c33de9d578bfcc243f559bd3801a0e57f2bcc9a943c790662e0c"}, {file = "SQLAlchemy-1.4.37-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:d9050b0c4a7f5538650c74aaba5c80cd64450e41c206f43ea6d194ae6d060ff9"},
{file = "SQLAlchemy-1.4.36-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6e1fe00ee85c768807f2a139b83469c1e52a9ffd58a6eb51aa7aeb524325ab18"}, {file = "SQLAlchemy-1.4.37-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b4c92823889cf9846b972ee6db30c0e3a92c0ddfc76c6060a6cda467aa5fb694"},
{file = "SQLAlchemy-1.4.36-cp27-cp27m-win32.whl", hash = "sha256:d57ac32f8dc731fddeb6f5d1358b4ca5456e72594e664769f0a9163f13df2a31"}, {file = "SQLAlchemy-1.4.37-cp27-cp27m-win32.whl", hash = "sha256:b55932fd0e81b43f4aff397c8ad0b3c038f540af37930423ab8f47a20b117e4c"},
{file = "SQLAlchemy-1.4.36-cp27-cp27m-win_amd64.whl", hash = "sha256:fca8322e04b2dde722fcb0558682740eebd3bd239bea7a0d0febbc190e99dc15"}, {file = "SQLAlchemy-1.4.37-cp27-cp27m-win_amd64.whl", hash = "sha256:4a17c1a1152ca4c29d992714aa9df3054da3af1598e02134f2e7314a32ef69d8"},
{file = "SQLAlchemy-1.4.36-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:53d2d9ee93970c969bc4e3c78b1277d7129554642f6ffea039c282c7dc4577bc"}, {file = "SQLAlchemy-1.4.37-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ffe487570f47536b96eff5ef2b84034a8ba4e19aab5ab7647e677d94a119ea55"},
{file = "SQLAlchemy-1.4.36-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:f0394a3acfb8925db178f7728adb38c027ed7e303665b225906bfa8099dc1ce8"}, {file = "SQLAlchemy-1.4.37-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:78363f400fbda80f866e8e91d37d36fe6313ff847ded08674e272873c1377ea5"},
{file = "SQLAlchemy-1.4.36-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c606d8238feae2f360b8742ffbe67741937eb0a05b57f536948d198a3def96"}, {file = "SQLAlchemy-1.4.37-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ee34c85cbda7779d66abac392c306ec78c13f5c73a1f01b8b767916d4895d23"},
{file = "SQLAlchemy-1.4.36-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8d07fe2de0325d06e7e73281e9a9b5e259fbd7cbfbe398a0433cbb0082ad8fa7"}, {file = "SQLAlchemy-1.4.37-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8b38e088659b30c2ca0af63e5d139fad1779a7925d75075a08717a21c406c0f6"},
{file = "SQLAlchemy-1.4.36-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5041474dcab7973baa91ec1f3112049a9dd4652898d6a95a6a895ff5c58beb6b"}, {file = "SQLAlchemy-1.4.37-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6629c79967a6c92e33fad811599adf9bc5cee6e504a1027bbf9cc1b6fb2d276d"},
{file = "SQLAlchemy-1.4.36-cp310-cp310-win32.whl", hash = "sha256:be094460930087e50fd08297db9d7aadaed8408ad896baf758e9190c335632da"}, {file = "SQLAlchemy-1.4.37-cp310-cp310-win32.whl", hash = "sha256:2aac2a685feb9882d09f457f4e5586c885d578af4e97a2b759e91e8c457cbce5"},
{file = "SQLAlchemy-1.4.36-cp310-cp310-win_amd64.whl", hash = "sha256:64d796e9af522162f7f2bf7a3c5531a0a550764c426782797bbeed809d0646c5"}, {file = "SQLAlchemy-1.4.37-cp310-cp310-win_amd64.whl", hash = "sha256:7a44683cf97744a405103ef8fdd31199e9d7fc41b4a67e9044523b29541662b0"},
{file = "SQLAlchemy-1.4.36-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:a0ae3aa2e86a4613f2d4c49eb7da23da536e6ce80b2bfd60bbb2f55fc02b0b32"}, {file = "SQLAlchemy-1.4.37-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:cffc67cdd07f0e109a1fc83e333972ae423ea5ad414585b63275b66b870ea62b"},
{file = "SQLAlchemy-1.4.36-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d50cb71c1dbed70646d521a0975fb0f92b7c3f84c61fa59e07be23a1aaeecfc"}, {file = "SQLAlchemy-1.4.37-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17417327b87a0f703c9a20180f75e953315207d048159aff51822052f3e33e69"},
{file = "SQLAlchemy-1.4.36-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:16abf35af37a3d5af92725fc9ec507dd9e9183d261c2069b6606d60981ed1c6e"}, {file = "SQLAlchemy-1.4.37-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:aaa0e90e527066409c2ea5676282cf4afb4a40bb9dce0f56c8ec2768bff22a6e"},
{file = "SQLAlchemy-1.4.36-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5864a83bd345871ad9699ce466388f836db7572003d67d9392a71998092210e3"}, {file = "SQLAlchemy-1.4.37-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1d9fb3931e27d59166bb5c4dcc911400fee51082cfba66ceb19ac954ade068"},
{file = "SQLAlchemy-1.4.36-cp36-cp36m-win32.whl", hash = "sha256:fbf8c09fe9728168f8cc1b40c239eab10baf9c422c18be7f53213d70434dea43"}, {file = "SQLAlchemy-1.4.37-cp36-cp36m-win32.whl", hash = "sha256:0e7fd52e48e933771f177c2a1a484b06ea03774fc7741651ebdf19985a34037c"},
{file = "SQLAlchemy-1.4.36-cp36-cp36m-win_amd64.whl", hash = "sha256:6e859fa96605027bd50d8e966db1c4e1b03e7b3267abbc4b89ae658c99393c58"}, {file = "SQLAlchemy-1.4.37-cp36-cp36m-win_amd64.whl", hash = "sha256:eec39a17bab3f69c44c9df4e0ed87c7306f2d2bf1eca3070af644927ec4199fa"},
{file = "SQLAlchemy-1.4.36-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:166a3887ec355f7d2f12738f7fa25dc8ac541867147a255f790f2f41f614cb44"}, {file = "SQLAlchemy-1.4.37-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:caca6acf3f90893d7712ae2c6616ecfeac3581b4cc677c928a330ce6fbad4319"},
{file = "SQLAlchemy-1.4.36-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e885548da361aa3f8a9433db4cfb335b2107e533bf314359ae3952821d84b3e"}, {file = "SQLAlchemy-1.4.37-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50c8eaf44c3fed5ba6758d375de25f163e46137c39fda3a72b9ee1d1bb327dfc"},
{file = "SQLAlchemy-1.4.36-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5c90ef955d429966d84326d772eb34333178737ebb669845f1d529eb00c75e72"}, {file = "SQLAlchemy-1.4.37-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:139c50b9384e6d32a74fc4dcd0e9717f343ed38f95dbacf832c782c68e3862f3"},
{file = "SQLAlchemy-1.4.36-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a052bd9f53004f8993c624c452dfad8ec600f572dd0ed0445fbe64b22f5570e"}, {file = "SQLAlchemy-1.4.37-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4c3b009c9220ae6e33f17b45f43fb46b9a1d281d76118405af13e26376f2e11"},
{file = "SQLAlchemy-1.4.36-cp37-cp37m-win32.whl", hash = "sha256:dce3468bf1fc12374a1a732c9efd146ce034f91bb0482b602a9311cb6166a920"}, {file = "SQLAlchemy-1.4.37-cp37-cp37m-win32.whl", hash = "sha256:9785d6f962d2c925aeb06a7539ac9d16608877da6aeaaf341984b3693ae80a02"},
{file = "SQLAlchemy-1.4.36-cp37-cp37m-win_amd64.whl", hash = "sha256:6cb4c4f57a20710cea277edf720d249d514e587f796b75785ad2c25e1c0fed26"}, {file = "SQLAlchemy-1.4.37-cp37-cp37m-win_amd64.whl", hash = "sha256:3197441772dc3b1c6419f13304402f2418a18d7fe78000aa5a026e7100836739"},
{file = "SQLAlchemy-1.4.36-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:e74ce103b81c375c3853b436297952ef8d7863d801dcffb6728d01544e5191b5"}, {file = "SQLAlchemy-1.4.37-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:3862a069a24f354145e01a76c7c720c263d62405fe5bed038c46a7ce900f5dd6"},
{file = "SQLAlchemy-1.4.36-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b20c4178ead9bc398be479428568ff31b6c296eb22e75776273781a6551973f"}, {file = "SQLAlchemy-1.4.37-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e8706919829d455a9fa687c6bbd1b048e36fec3919a59f2d366247c2bfdbd9c"},
{file = "SQLAlchemy-1.4.36-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:af2587ae11400157753115612d6c6ad255143efba791406ad8a0cbcccf2edcb3"}, {file = "SQLAlchemy-1.4.37-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:06ec11a5e6a4b6428167d3ce33b5bd455c020c867dabe3e6951fa98836e0741d"},
{file = "SQLAlchemy-1.4.36-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83cf3077712be9f65c9aaa0b5bc47bc1a44789fd45053e2e3ecd59ff17c63fe9"}, {file = "SQLAlchemy-1.4.37-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d58f2d9d1a4b1459e8956a0153a4119da80f54ee5a9ea623cd568e99459a3ef1"},
{file = "SQLAlchemy-1.4.36-cp38-cp38-win32.whl", hash = "sha256:ce20f5da141f8af26c123ebaa1b7771835ca6c161225ce728962a79054f528c3"}, {file = "SQLAlchemy-1.4.37-cp38-cp38-win32.whl", hash = "sha256:d6927c9e3965b194acf75c8e0fb270b4d54512db171f65faae15ef418721996e"},
{file = "SQLAlchemy-1.4.36-cp38-cp38-win_amd64.whl", hash = "sha256:316c7e5304dda3e3ad711569ac5d02698bbc71299b168ac56a7076b86259f7ea"}, {file = "SQLAlchemy-1.4.37-cp38-cp38-win_amd64.whl", hash = "sha256:a91d0668cada27352432f15b92ac3d43e34d8f30973fa8b86f5e9fddee928f3b"},
{file = "SQLAlchemy-1.4.36-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:f522214f6749bc073262529c056f7dfd660f3b5ec4180c5354d985eb7219801e"}, {file = "SQLAlchemy-1.4.37-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:f9940528bf9c4df9e3c3872d23078b6b2da6431c19565637c09f1b88a427a684"},
{file = "SQLAlchemy-1.4.36-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ecac4db8c1aa4a269f5829df7e706639a24b780d2ac46b3e485cbbd27ec0028"}, {file = "SQLAlchemy-1.4.37-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29a742c29fea12259f1d2a9ee2eb7fe4694a85d904a4ac66d15e01177b17ad7f"},
{file = "SQLAlchemy-1.4.36-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b3db741beaa983d4cbf9087558620e7787106319f7e63a066990a70657dd6b35"}, {file = "SQLAlchemy-1.4.37-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7e579d6e281cc937bdb59917017ab98e618502067e04efb1d24ac168925e1d2a"},
{file = "SQLAlchemy-1.4.36-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ec89bf98cc6a0f5d1e28e3ad28e9be6f3b4bdbd521a4053c7ae8d5e1289a8a1"}, {file = "SQLAlchemy-1.4.37-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a940c551cfbd2e1e646ceea2777944425f5c3edff914bc808fe734d9e66f8d71"},
{file = "SQLAlchemy-1.4.36-cp39-cp39-win32.whl", hash = "sha256:e12532c4d3f614678623da5d852f038ace1f01869b89f003ed6fe8c793f0c6a3"}, {file = "SQLAlchemy-1.4.37-cp39-cp39-win32.whl", hash = "sha256:5e4e517ce72fad35cce364a01aff165f524449e9c959f1837dc71088afa2824c"},
{file = "SQLAlchemy-1.4.36-cp39-cp39-win_amd64.whl", hash = "sha256:cb441ca461bf97d00877b607f132772644b623518b39ced54da433215adce691"}, {file = "SQLAlchemy-1.4.37-cp39-cp39-win_amd64.whl", hash = "sha256:c37885f83b59e248bebe2b35beabfbea398cb40960cdc6d3a76eac863d4e1938"},
{file = "SQLAlchemy-1.4.36.tar.gz", hash = "sha256:64678ac321d64a45901ef2e24725ec5e783f1f4a588305e196431447e7ace243"}, {file = "SQLAlchemy-1.4.37.tar.gz", hash = "sha256:3688f92c62db6c5df268e2264891078f17ecb91e3141b400f2e28d0f75796dea"},
] ]
staticmap = [ staticmap = [
{file = "staticmap-0.5.5.tar.gz", hash = "sha256:007c507b4d42e00eaba179649753f2f8d69d4ece3028736e18d9e86493044387"}, {file = "staticmap-0.5.5.tar.gz", hash = "sha256:007c507b4d42e00eaba179649753f2f8d69d4ece3028736e18d9e86493044387"},
@ -2307,8 +2307,8 @@ tomli = [
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
] ]
trio = [ trio = [
{file = "trio-0.20.0-py3-none-any.whl", hash = "sha256:fb2d48e4eab0dfb786a472cd514aaadc71e3445b203bc300bad93daa75d77c1a"}, {file = "trio-0.21.0-py3-none-any.whl", hash = "sha256:4dc0bf9d5cc78767fc4516325b6d80cc0968705a31d0eec2ecd7cdda466265b0"},
{file = "trio-0.20.0.tar.gz", hash = "sha256:670a52d3115d0e879e1ac838a4eb999af32f858163e3a704fe4839de2a676070"}, {file = "trio-0.21.0.tar.gz", hash = "sha256:523f39b7b69eef73501cebfe1aafd400a9aad5b03543a0eded52952488ff1c13"},
] ]
trio-websocket = [ trio-websocket = [
{file = "trio-websocket-0.9.2.tar.gz", hash = "sha256:a3d34de8fac26023eee701ed1e7bf4da9a8326b61a62934ec9e53b64970fd8fe"}, {file = "trio-websocket-0.9.2.tar.gz", hash = "sha256:a3d34de8fac26023eee701ed1e7bf4da9a8326b61a62934ec9e53b64970fd8fe"},
@ -2341,16 +2341,16 @@ typed-ast = [
{file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"},
] ]
types-freezegun = [ types-freezegun = [
{file = "types-freezegun-1.1.9.tar.gz", hash = "sha256:6f05108d468baecadf999873bd37e57b25ceb35d35d3f83e7a742f25d6fe8b0e"}, {file = "types-freezegun-1.1.10.tar.gz", hash = "sha256:cb3a2d2eee950eacbaac0673ab50499823365ceb8c655babb1544a41446409ec"},
{file = "types_freezegun-1.1.9-py3-none-any.whl", hash = "sha256:fe1dd73372d96358dcb93e3aeb66d39f6ac63749e0724f13554cc145e2120efe"}, {file = "types_freezegun-1.1.10-py3-none-any.whl", hash = "sha256:fadebe72213e0674036153366205038e1f95c8ca96deb4ef9b71ddc15413543e"},
] ]
types-pytz = [ types-pytz = [
{file = "types-pytz-2021.3.8.tar.gz", hash = "sha256:41253a3a2bf028b6a3f17b58749a692d955af0f74e975de94f6f4d2d3cd01dbd"}, {file = "types-pytz-2021.3.8.tar.gz", hash = "sha256:41253a3a2bf028b6a3f17b58749a692d955af0f74e975de94f6f4d2d3cd01dbd"},
{file = "types_pytz-2021.3.8-py3-none-any.whl", hash = "sha256:aef4a917ab28c585d3f474bfce4f4b44b91e95d9d47d4de29dd845e0db8e3910"}, {file = "types_pytz-2021.3.8-py3-none-any.whl", hash = "sha256:aef4a917ab28c585d3f474bfce4f4b44b91e95d9d47d4de29dd845e0db8e3910"},
] ]
types-requests = [ types-requests = [
{file = "types-requests-2.27.29.tar.gz", hash = "sha256:fb453b3a76a48eca66381cea8004feaaea12835e838196f5c7ac87c75c5c19ef"}, {file = "types-requests-2.27.30.tar.gz", hash = "sha256:ca8d7cc549c3d10dbcb3c69c1b53e3ffd1270089c1001a65c1e9e1017eb5e704"},
{file = "types_requests-2.27.29-py3-none-any.whl", hash = "sha256:014f4f82db7b96c41feea9adaea30e68cd64c230eeab34b70c29bebb26ec74ac"}, {file = "types_requests-2.27.30-py3-none-any.whl", hash = "sha256:b9b6cd0a6e5d500e56419b79f44ec96f316e9375ff6c8ee566c39d25e9612621"},
] ]
types-urllib3 = [ types-urllib3 = [
{file = "types-urllib3-1.26.15.tar.gz", hash = "sha256:c89283541ef92e344b7f59f83ea9b5a295b16366ceee3f25ecfc5593c79f794e"}, {file = "types-urllib3-1.26.15.tar.gz", hash = "sha256:c89283541ef92e344b7f59f83ea9b5a295b16366ceee3f25ecfc5593c79f794e"},

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "fittrackee" name = "fittrackee"
version = "0.6.6" version = "0.6.7"
description = "Self-hosted outdoor workout/activity tracker" description = "Self-hosted outdoor workout/activity tracker"
authors = ["SamR1"] authors = ["SamR1"]
license = "AGPL-3.0" license = "AGPL-3.0"
@ -29,7 +29,7 @@ flask = "^2.1"
flask-bcrypt = "^1.0" flask-bcrypt = "^1.0"
flask-dramatiq = "^0.6.0" flask-dramatiq = "^0.6.0"
flask-migrate = "^3.1" flask-migrate = "^3.1"
gpxpy = "=1.3.4" gpxpy = "=1.5.0"
gunicorn = "^20.1" gunicorn = "^20.1"
humanize = "^4.1" humanize = "^4.1"
psycopg2-binary = "^2.9" psycopg2-binary = "^2.9"
@ -38,7 +38,7 @@ python-forecastio = "^1.4"
pytz = "^2022.1" pytz = "^2022.1"
shortuuid = "^1.0.9" shortuuid = "^1.0.9"
staticmap = "^0.5.4" staticmap = "^0.5.4"
SQLAlchemy = "1.4.36" SQLAlchemy = "1.4.37"
pyOpenSSL = "^22.0" pyOpenSSL = "^22.0"
ua-parser = "^0.10.0" ua-parser = "^0.10.0"
Authlib = "^1.0.1" Authlib = "^1.0.1"
@ -46,7 +46,7 @@ Authlib = "^1.0.1"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
black = "^22.3" black = "^22.3"
freezegun = "^1.2" freezegun = "^1.2"
mypy = "^0.960" mypy = "^0.961"
pytest = "^7.1" pytest = "^7.1"
pytest-black = "^0.3.12" pytest-black = "^0.3.12"
pytest-cov = "^3.0" pytest-cov = "^3.0"