Merge branch 'dev' into oauth2
11
CHANGELOG.md
@ -1,5 +1,16 @@
|
||||
# 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)
|
||||
|
||||
### Misc
|
||||
|
2
Makefile
@ -74,7 +74,7 @@ docker-stop:
|
||||
docker-compose -f docker-compose-dev.yml stop
|
||||
|
||||
docker-up:
|
||||
docker-compose -f docker-compose-dev.yml up fittrackeee
|
||||
docker-compose -f docker-compose-dev.yml up fittrackee
|
||||
|
||||
downgrade-db:
|
||||
$(FLASK) db downgrade --directory $(MIGRATIONS)
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Sphinx build info version 1
|
||||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
||||
config: 4cdeaae17c31d716c1f73eb46769543b
|
||||
config: e7f46ba7f0469b1929d2349b0c52c449
|
||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||
|
Before Width: | Height: | Size: 539 KiB After Width: | Height: | Size: 539 KiB |
Before Width: | Height: | Size: 368 KiB After Width: | Height: | Size: 368 KiB |
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 201 KiB |
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 103 KiB |
@ -1,5 +1,16 @@
|
||||
# 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)
|
||||
|
||||
### Misc
|
||||
|
@ -395,13 +395,13 @@ Production environment
|
||||
.. warning::
|
||||
| Note that FitTrackee is under heavy development, some features may be unstable.
|
||||
|
||||
- Download the last release (for now, it is the release v0.6.6):
|
||||
- Download the last release (for now, it is the release v0.6.7):
|
||||
|
||||
.. code:: bash
|
||||
|
||||
$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.6.tar.gz
|
||||
$ tar -xzf v0.6.6.tar.gz
|
||||
$ mv FitTrackee-0.6.6 FitTrackee
|
||||
$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.7.tar.gz
|
||||
$ tar -xzf v0.6.7.tar.gz
|
||||
$ mv FitTrackee-0.6.7 FitTrackee
|
||||
$ cd FitTrackee
|
||||
|
||||
- Create **.env** from example and update it
|
||||
@ -521,13 +521,13 @@ Prod environment
|
||||
|
||||
- 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
|
||||
|
||||
$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.6.tar.gz
|
||||
$ tar -xzf v0.6.6.tar.gz
|
||||
$ cp -R FitTrackee-0.6.6/* FitTrackee/
|
||||
$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.7.tar.gz
|
||||
$ tar -xzf v0.6.7.tar.gz
|
||||
$ cp -R FitTrackee-0.6.7/* FitTrackee/
|
||||
$ cd FitTrackee
|
||||
|
||||
- Update **.env** if needed (see `Environment variables <installation.html#environment-variables>`__).
|
||||
|
2
docs/_static/documentation_options.js
vendored
@ -1,6 +1,6 @@
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
||||
VERSION: '0.6.6',
|
||||
VERSION: '0.6.7',
|
||||
LANGUAGE: 'None',
|
||||
COLLAPSE_INDEX: false,
|
||||
BUILDER: 'html',
|
||||
|
@ -5,7 +5,7 @@
|
||||
<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/" />
|
||||
|
||||
<title>Authentication — FitTrackee 0.6.6
|
||||
<title>Authentication — FitTrackee 0.6.7
|
||||
documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
|
||||
@ -40,7 +40,7 @@
|
||||
</button>
|
||||
<a class="navbar-brand" href="../index.html">
|
||||
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>
|
||||
</div>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<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/" />
|
||||
|
||||
<title>Configuration — FitTrackee 0.6.6
|
||||
<title>Configuration — FitTrackee 0.6.7
|
||||
documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
|
||||
@ -40,7 +40,7 @@
|
||||
</button>
|
||||
<a class="navbar-brand" href="../index.html">
|
||||
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>
|
||||
</div>
|
||||
|
||||
@ -151,7 +151,7 @@
|
||||
<span class="w"> </span><span class="nt">"max_users"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nt">"max_zip_file_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">10485760</span><span class="p">,</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nt">"map_attribution"</span><span class="p">:</span><span class="w"> </span><span class="nt">"&copy; <a href=http://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nt">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0.6.6"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nt">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0.6.7"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="p">},</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w"></span>
|
||||
<span class="p">}</span><span class="w"></span>
|
||||
@ -191,7 +191,7 @@
|
||||
<span class="w"> </span><span class="nt">"max_users"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nt">"max_zip_file_size"</span><span class="p">:</span><span class="w"> </span><span class="mi">10485760</span><span class="p">,</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nt">"map_attribution"</span><span class="p">:</span><span class="w"> </span><span class="nt">"&copy; <a href=http://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nt">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0.6.6"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nt">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0.6.7"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="p">},</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nt">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"success"</span><span class="w"></span>
|
||||
<span class="p">}</span><span class="w"></span>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<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/" />
|
||||
|
||||
<title>API documentation — FitTrackee 0.6.6
|
||||
<title>API documentation — FitTrackee 0.6.7
|
||||
documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
|
||||
@ -40,7 +40,7 @@
|
||||
</button>
|
||||
<a class="navbar-brand" href="../index.html">
|
||||
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>
|
||||
</div>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<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/" />
|
||||
|
||||
<title>Records — FitTrackee 0.6.6
|
||||
<title>Records — FitTrackee 0.6.7
|
||||
documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
|
||||
@ -40,7 +40,7 @@
|
||||
</button>
|
||||
<a class="navbar-brand" href="../index.html">
|
||||
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>
|
||||
</div>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<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/" />
|
||||
|
||||
<title>Sports — FitTrackee 0.6.6
|
||||
<title>Sports — FitTrackee 0.6.7
|
||||
documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
|
||||
@ -40,7 +40,7 @@
|
||||
</button>
|
||||
<a class="navbar-brand" href="../index.html">
|
||||
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>
|
||||
</div>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<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/" />
|
||||
|
||||
<title>Statistics — FitTrackee 0.6.6
|
||||
<title>Statistics — FitTrackee 0.6.7
|
||||
documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
|
||||
@ -40,7 +40,7 @@
|
||||
</button>
|
||||
<a class="navbar-brand" href="../index.html">
|
||||
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>
|
||||
</div>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<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/" />
|
||||
|
||||
<title>Users — FitTrackee 0.6.6
|
||||
<title>Users — FitTrackee 0.6.7
|
||||
documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
|
||||
@ -40,7 +40,7 @@
|
||||
</button>
|
||||
<a class="navbar-brand" href="../index.html">
|
||||
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>
|
||||
</div>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<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/" />
|
||||
|
||||
<title>Workouts — FitTrackee 0.6.6
|
||||
<title>Workouts — FitTrackee 0.6.7
|
||||
documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
|
||||
@ -40,7 +40,7 @@
|
||||
</button>
|
||||
<a class="navbar-brand" href="../index.html">
|
||||
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>
|
||||
</div>
|
||||
|
||||
@ -284,7 +284,7 @@
|
||||
<dl class="http get">
|
||||
<dt class="sig sig-object http" id="get--api-workouts-(string-workout_short_id)">
|
||||
<span class="sig-name descname"><span class="pre">GET</span> </span><span class="sig-name descname"><span class="pre">/api/workouts/</span></span><span class="sig-paren">(</span><em class="property"><span class="pre">string:</span> </em><em class="sig-param"><span class="pre">workout_short_id</span></em><span class="sig-paren">)</span><a class="headerlink" href="#get--api-workouts-(string-workout_short_id)" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Get an workout</p>
|
||||
<dd><p>Get a workout</p>
|
||||
<p><strong>Example request</strong>:</p>
|
||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">GET</span> <span class="nn">/api/workouts/kjxavSTUrJvoAh2wvCeGEF</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
|
||||
</pre></div>
|
||||
@ -378,7 +378,7 @@
|
||||
<dl class="http get">
|
||||
<dt class="sig sig-object http" id="get--api-workouts-(string-workout_short_id)-gpx">
|
||||
<span class="sig-name descname"><span class="pre">GET</span> </span><span class="sig-name descname"><span class="pre">/api/workouts/</span></span><span class="sig-paren">(</span><em class="property"><span class="pre">string:</span> </em><em class="sig-param"><span class="pre">workout_short_id</span></em><span class="sig-paren">)</span><span class="sig-name descname"><span class="pre">/gpx</span></span><a class="headerlink" href="#get--api-workouts-(string-workout_short_id)-gpx" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Get gpx file for an workout displayed on map with Leaflet</p>
|
||||
<dd><p>Get gpx file for a workout displayed on map with Leaflet</p>
|
||||
<p><strong>Example request</strong>:</p>
|
||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">GET</span> <span class="nn">/api/workouts/kjxavSTUrJvoAh2wvCeGEF/gpx</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
|
||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
|
||||
@ -431,7 +431,7 @@
|
||||
<dl class="http get">
|
||||
<dt class="sig sig-object http" id="get--api-workouts-(string-workout_short_id)-chart_data">
|
||||
<span class="sig-name descname"><span class="pre">GET</span> </span><span class="sig-name descname"><span class="pre">/api/workouts/</span></span><span class="sig-paren">(</span><em class="property"><span class="pre">string:</span> </em><em class="sig-param"><span class="pre">workout_short_id</span></em><span class="sig-paren">)</span><span class="sig-name descname"><span class="pre">/chart_data</span></span><a class="headerlink" href="#get--api-workouts-(string-workout_short_id)-chart_data" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Get chart data from an workout gpx file, to display it with Recharts</p>
|
||||
<dd><p>Get chart data from a workout gpx file, to display it with Recharts</p>
|
||||
<p><strong>Example request</strong>:</p>
|
||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">GET</span> <span class="nn">/api/workouts/kjxavSTUrJvoAh2wvCeGEF/chart</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
|
||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
|
||||
@ -503,7 +503,7 @@
|
||||
<dl class="http get">
|
||||
<dt class="sig sig-object http" id="get--api-workouts-(string-workout_short_id)-chart_data-segment-(int-segment_id)">
|
||||
<span class="sig-name descname"><span class="pre">GET</span> </span><span class="sig-name descname"><span class="pre">/api/workouts/</span></span><span class="sig-paren">(</span><em class="property"><span class="pre">string:</span> </em><em class="sig-param"><span class="pre">workout_short_id</span></em><span class="sig-paren">)</span><span class="sig-name descname"><span class="pre">/chart_data/segment/</span></span><span class="sig-paren">(</span><em class="property"><span class="pre">int:</span> </em><em class="sig-param"><span class="pre">segment_id</span></em><span class="sig-paren">)</span><a class="headerlink" href="#get--api-workouts-(string-workout_short_id)-chart_data-segment-(int-segment_id)" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Get chart data from an workout gpx file, to display it with Recharts</p>
|
||||
<dd><p>Get chart data from a workout gpx file, to display it with Recharts</p>
|
||||
<p><strong>Example request</strong>:</p>
|
||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">GET</span> <span class="nn">/api/workouts/kjxavSTUrJvoAh2wvCeGEF/chart/segment/0</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
|
||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
|
||||
@ -573,7 +573,7 @@
|
||||
<dl class="http get">
|
||||
<dt class="sig sig-object http" id="get--api-workouts-(string-workout_short_id)-gpx-segment-(int-segment_id)">
|
||||
<span class="sig-name descname"><span class="pre">GET</span> </span><span class="sig-name descname"><span class="pre">/api/workouts/</span></span><span class="sig-paren">(</span><em class="property"><span class="pre">string:</span> </em><em class="sig-param"><span class="pre">workout_short_id</span></em><span class="sig-paren">)</span><span class="sig-name descname"><span class="pre">/gpx/segment/</span></span><span class="sig-paren">(</span><em class="property"><span class="pre">int:</span> </em><em class="sig-param"><span class="pre">segment_id</span></em><span class="sig-paren">)</span><a class="headerlink" href="#get--api-workouts-(string-workout_short_id)-gpx-segment-(int-segment_id)" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Get gpx file for an workout segment displayed on map with Leaflet</p>
|
||||
<dd><p>Get gpx file for a workout segment displayed on map with Leaflet</p>
|
||||
<p><strong>Example request</strong>:</p>
|
||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">GET</span> <span class="nn">/api/workouts/kjxavSTUrJvoAh2wvCeGEF/gpx/segment/0</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
|
||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
|
||||
@ -724,7 +724,7 @@
|
||||
<dl class="http post">
|
||||
<dt class="sig sig-object http" id="post--api-workouts">
|
||||
<span class="sig-name descname"><span class="pre">POST</span> </span><span class="sig-name descname"><span class="pre">/api/workouts</span></span><a class="headerlink" href="#post--api-workouts" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Post an workout with a gpx file</p>
|
||||
<dd><p>Post a workout with a gpx file</p>
|
||||
<p><strong>Example request</strong>:</p>
|
||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">POST</span> <span class="nn">/api/workouts/</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
|
||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">multipart/form-data</span>
|
||||
@ -847,7 +847,7 @@
|
||||
<dl class="http post">
|
||||
<dt class="sig sig-object http" id="post--api-workouts-no_gpx">
|
||||
<span class="sig-name descname"><span class="pre">POST</span> </span><span class="sig-name descname"><span class="pre">/api/workouts/no_gpx</span></span><a class="headerlink" href="#post--api-workouts-no_gpx" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Post an workout without gpx file</p>
|
||||
<dd><p>Post a workout without gpx file</p>
|
||||
<p><strong>Example request</strong>:</p>
|
||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">POST</span> <span class="nn">/api/workouts/no_gpx</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
|
||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
|
||||
@ -935,7 +935,8 @@
|
||||
<dl class="field-list simple">
|
||||
<dt class="field-odd">Request JSON Object</dt>
|
||||
<dd class="field-odd"><ul class="simple">
|
||||
<li><p><strong>workout_date</strong> (<em>string</em>) – workout date (format: <code class="docutils literal notranslate"><span class="pre">%Y-%m-%d</span> <span class="pre">%H:%M</span></code>)</p></li>
|
||||
<li><p><strong>workout_date</strong> (<em>string</em>) – workout date, in user timezone
|
||||
(format: <code class="docutils literal notranslate"><span class="pre">%Y-%m-%d</span> <span class="pre">%H:%M</span></code>)</p></li>
|
||||
<li><p><strong>distance</strong> (<em>float</em>) – workout distance in km</p></li>
|
||||
<li><p><strong>duration</strong> (<em>integer</em>) – workout duration in seconds</p></li>
|
||||
<li><p><strong>notes</strong> (<em>string</em>) – notes (not mandatory)</p></li>
|
||||
@ -967,7 +968,7 @@
|
||||
<dl class="http patch">
|
||||
<dt class="sig sig-object http" id="patch--api-workouts-(string-workout_short_id)">
|
||||
<span class="sig-name descname"><span class="pre">PATCH</span> </span><span class="sig-name descname"><span class="pre">/api/workouts/</span></span><span class="sig-paren">(</span><em class="property"><span class="pre">string:</span> </em><em class="sig-param"><span class="pre">workout_short_id</span></em><span class="sig-paren">)</span><a class="headerlink" href="#patch--api-workouts-(string-workout_short_id)" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Update an workout</p>
|
||||
<dd><p>Update a workout</p>
|
||||
<p><strong>Example request</strong>:</p>
|
||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">PATCH</span> <span class="nn">/api/workouts/1</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
|
||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
|
||||
@ -1060,7 +1061,8 @@
|
||||
</dd>
|
||||
<dt class="field-even">Request JSON Object</dt>
|
||||
<dd class="field-even"><ul class="simple">
|
||||
<li><p><strong>workout_date</strong> (<em>string</em>) – workout date (format: <code class="docutils literal notranslate"><span class="pre">%Y-%m-%d</span> <span class="pre">%H:%M</span></code>)
|
||||
<li><p><strong>workout_date</strong> (<em>string</em>) – workout date in user timezone
|
||||
(format: <code class="docutils literal notranslate"><span class="pre">%Y-%m-%d</span> <span class="pre">%H:%M</span></code>)
|
||||
(only for workout without gpx)</p></li>
|
||||
<li><p><strong>distance</strong> (<em>float</em>) – workout distance in km
|
||||
(only for workout without gpx)</p></li>
|
||||
@ -1096,7 +1098,7 @@
|
||||
<dl class="http delete">
|
||||
<dt class="sig sig-object http" id="delete--api-workouts-(string-workout_short_id)">
|
||||
<span class="sig-name descname"><span class="pre">DELETE</span> </span><span class="sig-name descname"><span class="pre">/api/workouts/</span></span><span class="sig-paren">(</span><em class="property"><span class="pre">string:</span> </em><em class="sig-param"><span class="pre">workout_short_id</span></em><span class="sig-paren">)</span><a class="headerlink" href="#delete--api-workouts-(string-workout_short_id)" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Delete an workout</p>
|
||||
<dd><p>Delete a workout</p>
|
||||
<p><strong>Example request</strong>:</p>
|
||||
<div class="highlight-http notranslate"><div class="highlight"><pre><span></span><span class="nf">DELETE</span> <span class="nn">/api/workouts/kjxavSTUrJvoAh2wvCeGEF</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.1</span>
|
||||
<span class="na">Content-Type</span><span class="o">:</span> <span class="l">application/json</span>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<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/" />
|
||||
|
||||
<title>Command line interface — FitTrackee 0.6.6
|
||||
<title>Command line interface — FitTrackee 0.6.7
|
||||
documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
|
||||
@ -40,7 +40,7 @@
|
||||
</button>
|
||||
<a class="navbar-brand" href="index.html">
|
||||
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>
|
||||
</div>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<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/" />
|
||||
|
||||
<title>Features — FitTrackee 0.6.6
|
||||
<title>Features — FitTrackee 0.6.7
|
||||
documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
|
||||
@ -40,7 +40,7 @@
|
||||
</button>
|
||||
<a class="navbar-brand" href="index.html">
|
||||
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>
|
||||
</div>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Index — FitTrackee 0.6.6
|
||||
<title>Index — FitTrackee 0.6.7
|
||||
documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
|
||||
@ -37,7 +37,7 @@
|
||||
</button>
|
||||
<a class="navbar-brand" href="index.html">
|
||||
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>
|
||||
</div>
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>HTTP Routing Table — FitTrackee 0.6.6
|
||||
<title>HTTP Routing Table — FitTrackee 0.6.7
|
||||
documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
|
||||
@ -44,7 +44,7 @@
|
||||
</button>
|
||||
<a class="navbar-brand" href="index.html">
|
||||
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>
|
||||
</div>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<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/" />
|
||||
|
||||
<title>FitTrackee — FitTrackee 0.6.6
|
||||
<title>FitTrackee — FitTrackee 0.6.7
|
||||
documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
|
||||
@ -39,7 +39,7 @@
|
||||
</button>
|
||||
<a class="navbar-brand" href="#">
|
||||
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>
|
||||
</div>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<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/" />
|
||||
|
||||
<title>Installation — FitTrackee 0.6.6
|
||||
<title>Installation — FitTrackee 0.6.7
|
||||
documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
|
||||
@ -40,7 +40,7 @@
|
||||
</button>
|
||||
<a class="navbar-brand" href="index.html">
|
||||
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>
|
||||
</div>
|
||||
|
||||
@ -674,11 +674,11 @@ $ make install-db
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.6.tar.gz
|
||||
$ tar -xzf v0.6.6.tar.gz
|
||||
$ mv FitTrackee-0.6.6 FitTrackee
|
||||
<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.7.tar.gz
|
||||
$ mv FitTrackee-0.6.7 FitTrackee
|
||||
$ <span class="nb">cd</span> FitTrackee
|
||||
</pre></div>
|
||||
</div>
|
||||
@ -798,11 +798,11 @@ $ <span class="nb">source</span> .env
|
||||
<ul class="simple">
|
||||
<li><p>Stop the application</p></li>
|
||||
<li><p>Change to the directory where FitTrackee directory is located</p></li>
|
||||
<li><p>Download the last release (for now, it is the release v0.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>
|
||||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.6.tar.gz
|
||||
$ tar -xzf v0.6.6.tar.gz
|
||||
$ cp -R FitTrackee-0.6.6/* FitTrackee/
|
||||
<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.7.tar.gz
|
||||
$ cp -R FitTrackee-0.6.7/* FitTrackee/
|
||||
$ <span class="nb">cd</span> FitTrackee
|
||||
</pre></div>
|
||||
</div>
|
||||
|
BIN
docs/objects.inv
@ -4,7 +4,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Search — FitTrackee 0.6.6
|
||||
<title>Search — FitTrackee 0.6.7
|
||||
documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
|
||||
@ -44,7 +44,7 @@
|
||||
</button>
|
||||
<a class="navbar-brand" href="index.html">
|
||||
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>
|
||||
</div>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<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/" />
|
||||
|
||||
<title>Administrator — FitTrackee 0.6.6
|
||||
<title>Administrator — FitTrackee 0.6.7
|
||||
documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
|
||||
@ -40,7 +40,7 @@
|
||||
</button>
|
||||
<a class="navbar-brand" href="../index.html">
|
||||
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>
|
||||
</div>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<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/" />
|
||||
|
||||
<title>Troubleshooting — FitTrackee 0.6.6
|
||||
<title>Troubleshooting — FitTrackee 0.6.7
|
||||
documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
|
||||
@ -40,7 +40,7 @@
|
||||
</button>
|
||||
<a class="navbar-brand" href="../index.html">
|
||||
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>
|
||||
</div>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
<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/" />
|
||||
|
||||
<title>User — FitTrackee 0.6.6
|
||||
<title>User — FitTrackee 0.6.7
|
||||
documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
|
||||
@ -40,7 +40,7 @@
|
||||
</button>
|
||||
<a class="navbar-brand" href="../index.html">
|
||||
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>
|
||||
</div>
|
||||
|
||||
|
Before Width: | Height: | Size: 539 KiB After Width: | Height: | Size: 539 KiB |
Before Width: | Height: | Size: 368 KiB After Width: | Height: | Size: 368 KiB |
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 201 KiB |
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 103 KiB |
@ -395,13 +395,13 @@ Production environment
|
||||
.. warning::
|
||||
| Note that FitTrackee is under heavy development, some features may be unstable.
|
||||
|
||||
- Download the last release (for now, it is the release v0.6.6):
|
||||
- Download the last release (for now, it is the release v0.6.7):
|
||||
|
||||
.. code:: bash
|
||||
|
||||
$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.6.tar.gz
|
||||
$ tar -xzf v0.6.6.tar.gz
|
||||
$ mv FitTrackee-0.6.6 FitTrackee
|
||||
$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.7.tar.gz
|
||||
$ tar -xzf v0.6.7.tar.gz
|
||||
$ mv FitTrackee-0.6.7 FitTrackee
|
||||
$ cd FitTrackee
|
||||
|
||||
- Create **.env** from example and update it
|
||||
@ -521,13 +521,13 @@ Prod environment
|
||||
|
||||
- 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
|
||||
|
||||
$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.6.tar.gz
|
||||
$ tar -xzf v0.6.6.tar.gz
|
||||
$ cp -R FitTrackee-0.6.6/* FitTrackee/
|
||||
$ wget https://github.com/SamR1/FitTrackee/archive/v0.6.7.tar.gz
|
||||
$ tar -xzf v0.6.7.tar.gz
|
||||
$ cp -R FitTrackee-0.6.7/* FitTrackee/
|
||||
$ cd FitTrackee
|
||||
|
||||
- Update **.env** if needed (see `Environment variables <installation.html#environment-variables>`__).
|
||||
|
@ -21,7 +21,7 @@ from werkzeug.middleware.proxy_fix import ProxyFix
|
||||
from fittrackee.emails.email import EmailService
|
||||
from fittrackee.request import CustomRequest
|
||||
|
||||
VERSION = __version__ = '0.6.6'
|
||||
VERSION = __version__ = '0.6.7'
|
||||
db = SQLAlchemy()
|
||||
bcrypt = Bcrypt()
|
||||
migrate = Migrate()
|
||||
|
@ -48,7 +48,7 @@ def get_application_config() -> Union[Dict, HttpResponse]:
|
||||
"max_users": 0,
|
||||
"max_zip_file_size": 10485760,
|
||||
"map_attribution": "© <a href=http://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors"
|
||||
"version": "0.6.6"
|
||||
"version": "0.6.7"
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
@ -98,7 +98,7 @@ def update_application_config(auth_user: User) -> Union[Dict, HttpResponse]:
|
||||
"max_users": 10,
|
||||
"max_zip_file_size": 10485760,
|
||||
"map_attribution": "© <a href=http://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors"
|
||||
"version": "0.6.6"
|
||||
"version": "0.6.7"
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
|
2
fittrackee/dist/index.html
vendored
@ -1 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="/favicon.ico"><![endif]--><link rel="stylesheet" href="/static/css/fork-awesome.min.css"/><link rel="stylesheet" href="/static/css/leaflet.css"/><title>FitTrackee</title><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>
|
2
fittrackee/dist/service-worker.js
vendored
2
fittrackee/dist/service-worker.js.map
vendored
114
fittrackee/tests/fixtures/fixtures_workouts.py
vendored
@ -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()
|
||||
def gpx_file_wo_track() -> str:
|
||||
return (
|
||||
|
@ -2,7 +2,7 @@ from unittest.mock import call, patch
|
||||
|
||||
import pytest
|
||||
from flask import Flask
|
||||
from gpxpy.gpx import MovingData
|
||||
from gpxpy.gpx import IGNORE_TOP_SPEED_PERCENTILES, MovingData
|
||||
from werkzeug.datastructures import FileStorage
|
||||
|
||||
from fittrackee.users.models import User, UserSportPreference
|
||||
@ -55,7 +55,12 @@ class TestStoppedSpeedThreshold:
|
||||
assert gpx_track_segment_mock.call_args_list[0] == call(
|
||||
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
|
||||
self,
|
||||
@ -85,4 +90,9 @@ class TestStoppedSpeedThreshold:
|
||||
assert gpx_track_segment_mock.call_args_list[0] == call(
|
||||
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
|
||||
)
|
||||
|
@ -1,9 +1,27 @@
|
||||
from datetime import datetime
|
||||
from statistics import mean
|
||||
from typing import List
|
||||
from typing import List, Union
|
||||
|
||||
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:
|
||||
@ -30,3 +48,68 @@ class TestWorkoutAverageSpeed:
|
||||
assert get_average_speed(
|
||||
nb_workouts, total_average_speed, workout_average_speed
|
||||
) == 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
|
||||
|
@ -969,7 +969,7 @@ class TestGetWorkoutsWithFiltersAndPagination(ApiTestCaseMixin):
|
||||
|
||||
|
||||
class TestGetWorkout(ApiTestCaseMixin):
|
||||
def test_it_gets_an_workout(
|
||||
def test_it_gets_a_workout(
|
||||
self,
|
||||
app: Flask,
|
||||
user_1: User,
|
||||
@ -1105,7 +1105,7 @@ class TestGetWorkout(ApiTestCaseMixin):
|
||||
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,
|
||||
app: Flask,
|
||||
user_1: User,
|
||||
@ -1125,7 +1125,7 @@ class TestGetWorkout(ApiTestCaseMixin):
|
||||
data = self.assert_500(response)
|
||||
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,
|
||||
app: Flask,
|
||||
user_1: User,
|
||||
|
@ -2,7 +2,7 @@ import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
from io import BytesIO
|
||||
from typing import Dict
|
||||
from typing import Dict, Optional
|
||||
from unittest.mock import Mock
|
||||
|
||||
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]['distance'] == 0.3
|
||||
assert data['data']['workouts'][0]['max_alt'] == 998.0
|
||||
assert (
|
||||
data['data']['workouts'][0]['max_speed'] is None
|
||||
) # not enough points
|
||||
assert data['data']['workouts'][0]['max_speed'] == 5.25
|
||||
assert data['data']['workouts'][0]['min_alt'] == 975.0
|
||||
assert data['data']['workouts'][0]['moving'] == '0:03:55'
|
||||
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['distance'] == 0.113
|
||||
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['moving'] == '0:01:30'
|
||||
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['distance'] == 0.186
|
||||
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['moving'] == '0:02:25'
|
||||
assert segment['pauses'] is None
|
||||
|
||||
records = data['data']['workouts'][0]['records']
|
||||
assert len(records) == 3
|
||||
assert len(records) == 4
|
||||
assert records[0]['sport_id'] == 1
|
||||
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]['value'] == '0:03:55'
|
||||
assert records[0]['value'] == 5.25
|
||||
assert records[1]['sport_id'] == 1
|
||||
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]['value'] == 0.3
|
||||
assert records[1]['value'] == '0:03:55'
|
||||
assert records[2]['sport_id'] == 1
|
||||
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]['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:
|
||||
@ -222,7 +225,7 @@ class TestPostWorkoutWithGpx(ApiTestCaseMixin, CallArgsMixin):
|
||||
|
||||
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
|
||||
) -> None:
|
||||
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_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,
|
||||
app: Flask,
|
||||
user_1: User,
|
||||
@ -281,7 +284,7 @@ class TestPostWorkoutWithGpx(ApiTestCaseMixin, CallArgsMixin):
|
||||
)
|
||||
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,
|
||||
app: Flask,
|
||||
user_1: User,
|
||||
@ -315,6 +318,41 @@ class TestPostWorkoutWithGpx(ApiTestCaseMixin, CallArgsMixin):
|
||||
)
|
||||
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(
|
||||
'input_description,input_notes',
|
||||
[
|
||||
@ -606,7 +644,7 @@ class TestPostWorkoutWithoutGpx(ApiTestCaseMixin):
|
||||
|
||||
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
|
||||
) -> None:
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
@ -953,12 +991,12 @@ class TestPostAndGetWorkoutWithGpx(ApiTestCaseMixin):
|
||||
|
||||
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
|
||||
) -> None:
|
||||
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,
|
||||
app: Flask,
|
||||
user_1: User,
|
||||
@ -969,7 +1007,7 @@ class TestPostAndGetWorkoutWithGpx(ApiTestCaseMixin):
|
||||
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
|
||||
) -> None:
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
@ -1000,7 +1038,7 @@ class TestPostAndGetWorkoutWithGpx(ApiTestCaseMixin):
|
||||
assert data['message'] == ''
|
||||
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
|
||||
) -> None:
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
@ -1125,7 +1163,7 @@ class TestPostAndGetWorkoutWithGpx(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
|
||||
) -> None:
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
@ -1158,7 +1196,7 @@ class TestPostAndGetWorkoutWithoutGpx(ApiTestCaseMixin):
|
||||
assert len(data['data']['workouts']) == 1
|
||||
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
|
||||
) -> None:
|
||||
client, auth_token = self.get_test_client_and_auth_token(
|
||||
@ -1194,7 +1232,7 @@ class TestPostAndGetWorkoutWithoutGpx(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
|
||||
) -> None:
|
||||
user_1.timezone = 'Europe/Paris'
|
||||
|
@ -9,7 +9,7 @@ from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
|
||||
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:
|
||||
@ -56,7 +56,7 @@ def assert_workout_data_with_gpx(data: Dict, sport_id: int) -> None:
|
||||
|
||||
|
||||
class TestEditWorkoutWithGpx(ApiTestCaseMixin):
|
||||
def test_it_updates_title_for_an_workout_with_gpx(
|
||||
def test_it_updates_title_for_a_workout_with_gpx(
|
||||
self,
|
||||
app: Flask,
|
||||
user_1: User,
|
||||
@ -64,7 +64,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin):
|
||||
sport_2_running: Sport,
|
||||
gpx_file: str,
|
||||
) -> 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()
|
||||
|
||||
response = client.patch(
|
||||
@ -100,7 +100,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin):
|
||||
sport_2_running: Sport,
|
||||
gpx_file: str,
|
||||
) -> 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()
|
||||
|
||||
response = client.patch(
|
||||
@ -124,7 +124,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin):
|
||||
sport_2_running: Sport,
|
||||
gpx_file: str,
|
||||
) -> None:
|
||||
token, workout_short_id = post_an_workout(
|
||||
token, workout_short_id = post_a_workout(
|
||||
app, gpx_file, notes=uuid4().hex
|
||||
)
|
||||
client = app.test_client()
|
||||
@ -142,7 +142,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin):
|
||||
assert len(data['data']['workouts']) == 1
|
||||
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,
|
||||
app: Flask,
|
||||
user_1: User,
|
||||
@ -151,7 +151,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin):
|
||||
sport_2_running: Sport,
|
||||
gpx_file: str,
|
||||
) -> 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(
|
||||
app, user_2.email
|
||||
)
|
||||
@ -173,7 +173,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin):
|
||||
sport_2_running: Sport,
|
||||
gpx_file: str,
|
||||
) -> 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()
|
||||
|
||||
response = client.patch(
|
||||
@ -194,7 +194,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin):
|
||||
def test_it_returns_400_if_payload_is_empty(
|
||||
self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str
|
||||
) -> 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()
|
||||
|
||||
response = client.patch(
|
||||
@ -209,7 +209,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin):
|
||||
def test_it_raises_500_if_sport_does_not_exists(
|
||||
self, app: Flask, user_1: User, sport_1_cycling: Sport, gpx_file: str
|
||||
) -> 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()
|
||||
|
||||
response = client.patch(
|
||||
@ -223,7 +223,7 @@ class TestEditWorkoutWithGpx(ApiTestCaseMixin):
|
||||
|
||||
|
||||
class TestEditWorkoutWithoutGpx(ApiTestCaseMixin):
|
||||
def test_it_updates_an_workout_wo_gpx(
|
||||
def test_it_updates_a_workout_wo_gpx(
|
||||
self,
|
||||
app: Flask,
|
||||
user_1: User,
|
||||
@ -364,7 +364,7 @@ class TestEditWorkoutWithoutGpx(ApiTestCaseMixin):
|
||||
assert len(data['data']['workouts']) == 1
|
||||
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,
|
||||
app: Flask,
|
||||
user_1: User,
|
||||
@ -393,7 +393,7 @@ class TestEditWorkoutWithoutGpx(ApiTestCaseMixin):
|
||||
|
||||
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,
|
||||
app: Flask,
|
||||
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]['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,
|
||||
app: Flask,
|
||||
user_1: User,
|
||||
|
@ -7,7 +7,7 @@ from fittrackee.users.models import User
|
||||
from fittrackee.workouts.models import Sport, Workout
|
||||
|
||||
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:
|
||||
@ -16,10 +16,10 @@ def get_gpx_filepath(workout_id: int) -> str:
|
||||
|
||||
|
||||
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
|
||||
) -> 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()
|
||||
|
||||
response = client.delete(
|
||||
@ -29,7 +29,7 @@ class TestDeleteWorkoutWithGpx(ApiTestCaseMixin):
|
||||
|
||||
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,
|
||||
app: Flask,
|
||||
user_1: User,
|
||||
@ -37,7 +37,7 @@ class TestDeleteWorkoutWithGpx(ApiTestCaseMixin):
|
||||
sport_1_cycling: Sport,
|
||||
gpx_file: str,
|
||||
) -> 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(
|
||||
app, user_2.email
|
||||
)
|
||||
@ -64,10 +64,10 @@ class TestDeleteWorkoutWithGpx(ApiTestCaseMixin):
|
||||
data = self.assert_404(response)
|
||||
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
|
||||
) -> 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()
|
||||
gpx_filepath = get_gpx_filepath(1)
|
||||
gpx_filepath = get_absolute_file_path(gpx_filepath)
|
||||
@ -82,7 +82,7 @@ class TestDeleteWorkoutWithGpx(ApiTestCaseMixin):
|
||||
|
||||
|
||||
class TestDeleteWorkoutWithoutGpx(ApiTestCaseMixin):
|
||||
def test_it_deletes_an_workout_wo_gpx(
|
||||
def test_it_deletes_a_workout_wo_gpx(
|
||||
self,
|
||||
app: Flask,
|
||||
user_1: User,
|
||||
@ -98,7 +98,7 @@ class TestDeleteWorkoutWithoutGpx(ApiTestCaseMixin):
|
||||
)
|
||||
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,
|
||||
app: Flask,
|
||||
user_1: User,
|
||||
|
@ -12,7 +12,7 @@ def get_random_short_id() -> str:
|
||||
return encode_uuid(uuid4())
|
||||
|
||||
|
||||
def post_an_workout(
|
||||
def post_a_workout(
|
||||
app: Flask, gpx_file: str, notes: Optional[str] = None
|
||||
) -> Tuple[str, str]:
|
||||
client = app.test_client()
|
||||
|
@ -1,5 +1,5 @@
|
||||
from datetime import timedelta
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
import gpxpy.gpx
|
||||
|
||||
@ -16,9 +16,9 @@ def open_gpx_file(gpx_file: str) -> Optional[gpxpy.gpx.GPX]:
|
||||
|
||||
|
||||
def get_gpx_data(
|
||||
parsed_gpx: gpxpy.gpx,
|
||||
parsed_gpx: Union[gpxpy.gpx.GPX, gpxpy.gpx.GPXTrackSegment],
|
||||
max_speed: float,
|
||||
start: int,
|
||||
start: Union[datetime, None],
|
||||
stopped_time_between_seg: timedelta,
|
||||
stopped_speed_threshold: float,
|
||||
) -> Dict:
|
||||
@ -32,7 +32,8 @@ def get_gpx_data(
|
||||
|
||||
duration = parsed_gpx.get_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()
|
||||
@ -43,18 +44,24 @@ def get_gpx_data(
|
||||
gpx_data['uphill'] = hill.uphill
|
||||
gpx_data['downhill'] = hill.downhill
|
||||
|
||||
mv = parsed_gpx.get_moving_data(
|
||||
moving_data = parsed_gpx.get_moving_data(
|
||||
stopped_speed_threshold=stopped_speed_threshold
|
||||
)
|
||||
gpx_data['moving_time'] = timedelta(seconds=mv.moving_time)
|
||||
gpx_data['stop_time'] = (
|
||||
timedelta(seconds=mv.stopped_time) + stopped_time_between_seg
|
||||
)
|
||||
distance = mv.moving_distance + mv.stopped_distance
|
||||
gpx_data['distance'] = distance / 1000
|
||||
if moving_data:
|
||||
gpx_data['moving_time'] = timedelta(seconds=moving_data.moving_time)
|
||||
gpx_data['stop_time'] = (
|
||||
timedelta(seconds=moving_data.stopped_time)
|
||||
+ stopped_time_between_seg
|
||||
)
|
||||
distance = moving_data.moving_distance + moving_data.stopped_distance
|
||||
gpx_data['distance'] = distance / 1000
|
||||
|
||||
average_speed = distance / mv.moving_time if mv.moving_time > 0 else 0
|
||||
gpx_data['average_speed'] = (average_speed / 1000) * 3600
|
||||
average_speed = (
|
||||
distance / moving_data.moving_time
|
||||
if moving_data.moving_time > 0
|
||||
else 0
|
||||
)
|
||||
gpx_data['average_speed'] = (average_speed / 1000) * 3600
|
||||
|
||||
return gpx_data
|
||||
|
||||
@ -72,9 +79,9 @@ def get_gpx_info(
|
||||
if gpx is None:
|
||||
raise WorkoutGPXException('not found', 'No gpx file')
|
||||
|
||||
gpx_data = {'name': gpx.tracks[0].name, 'segments': []}
|
||||
max_speed = 0
|
||||
start = 0
|
||||
gpx_data: Dict = {'name': gpx.tracks[0].name, 'segments': []}
|
||||
max_speed = 0.0
|
||||
start: Optional[datetime] = None
|
||||
map_data = []
|
||||
weather_data = []
|
||||
segments_nb = len(gpx.tracks[0].segments)
|
||||
@ -83,14 +90,15 @@ def get_gpx_info(
|
||||
stopped_time_between_seg = no_stopped_time
|
||||
|
||||
for segment_idx, segment in enumerate(gpx.tracks[0].segments):
|
||||
segment_start = 0
|
||||
segment_start: Optional[datetime] = None
|
||||
segment_points_nb = len(segment.points)
|
||||
for point_idx, point in enumerate(segment.points):
|
||||
if point_idx == 0:
|
||||
segment_start = point.time
|
||||
# first gpx point => get weather
|
||||
if start == 0:
|
||||
if start is None:
|
||||
start = point.time
|
||||
if update_weather_data:
|
||||
if point.time and update_weather_data:
|
||||
weather_data.append(get_weather(point))
|
||||
|
||||
# if a previous segment exists, calculate stopped time between
|
||||
@ -108,13 +116,19 @@ def get_gpx_info(
|
||||
|
||||
if update_map_data:
|
||||
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
|
||||
).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:
|
||||
max_speed = segment_max_speed
|
||||
if segment_max_speed > max_speed:
|
||||
max_speed = segment_max_speed
|
||||
else:
|
||||
segment_max_speed = 0.0
|
||||
|
||||
segment_data = get_gpx_data(
|
||||
segment,
|
||||
@ -137,12 +151,16 @@ def get_gpx_info(
|
||||
|
||||
if update_map_data:
|
||||
bounds = gpx.get_bounds()
|
||||
gpx_data['bounds'] = [
|
||||
bounds.min_latitude,
|
||||
bounds.min_longitude,
|
||||
bounds.max_latitude,
|
||||
bounds.max_longitude,
|
||||
]
|
||||
gpx_data['bounds'] = (
|
||||
[
|
||||
bounds.min_latitude,
|
||||
bounds.min_longitude,
|
||||
bounds.max_latitude,
|
||||
bounds.max_longitude,
|
||||
]
|
||||
if bounds
|
||||
else []
|
||||
)
|
||||
|
||||
return gpx_data, map_data, weather_data
|
||||
|
||||
@ -222,7 +240,11 @@ def get_chart_data(
|
||||
'latitude': point.latitude,
|
||||
'longitude': point.longitude,
|
||||
'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
|
||||
|
@ -3,18 +3,22 @@ from typing import Dict, Optional
|
||||
|
||||
import forecastio
|
||||
import pytz
|
||||
from gpxpy.gpx import GPXRoutePoint
|
||||
from gpxpy.gpx import GPXTrackPoint
|
||||
|
||||
from fittrackee import appLog
|
||||
|
||||
API_KEY = os.getenv('WEATHER_API_KEY')
|
||||
|
||||
|
||||
def get_weather(point: GPXRoutePoint) -> Optional[Dict]:
|
||||
if not API_KEY or API_KEY == '':
|
||||
def get_weather(point: GPXTrackPoint) -> Optional[Dict]:
|
||||
if not API_KEY or not point.time:
|
||||
return None
|
||||
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(
|
||||
API_KEY,
|
||||
point.latitude,
|
||||
|
@ -22,31 +22,42 @@ from .gpx import get_gpx_info
|
||||
from .maps import generate_map, get_map_hash
|
||||
|
||||
|
||||
def get_datetime_with_tz(
|
||||
timezone: str, workout_date: datetime, gpx_data: Optional[Dict] = None
|
||||
) -> Tuple[Optional[datetime], datetime]:
|
||||
def get_workout_datetime(
|
||||
workout_date: Union[datetime, str],
|
||||
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
|
||||
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)
|
||||
workout_date_with_user_tz = 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(
|
||||
@ -57,25 +68,32 @@ def get_datetime_from_request_args(
|
||||
|
||||
date_from_str = params.get('from')
|
||||
if date_from_str:
|
||||
date_from = datetime.strptime(date_from_str, '%Y-%m-%d')
|
||||
_, date_from = get_datetime_with_tz(user.timezone, date_from)
|
||||
date_from, _ = get_workout_datetime(
|
||||
workout_date=date_from_str,
|
||||
user_timezone=user.timezone,
|
||||
date_str_format='%Y-%m-%d',
|
||||
)
|
||||
date_to_str = params.get('to')
|
||||
if date_to_str:
|
||||
date_to = datetime.strptime(
|
||||
f'{date_to_str} 23:59:59', '%Y-%m-%d %H:%M:%S'
|
||||
date_to, _ = get_workout_datetime(
|
||||
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
|
||||
|
||||
|
||||
def _remove_microseconds(delta: timedelta) -> timedelta:
|
||||
return delta - timedelta(microseconds=delta.microseconds)
|
||||
|
||||
|
||||
def update_workout_data(
|
||||
workout: Union[Workout, WorkoutSegment], gpx_data: Dict
|
||||
) -> Union[Workout, WorkoutSegment]:
|
||||
"""
|
||||
Update workout or workout segment with data from gpx file
|
||||
"""
|
||||
workout.pauses = gpx_data['stop_time']
|
||||
workout.moving = gpx_data['moving_time']
|
||||
workout.pauses = _remove_microseconds(gpx_data['stop_time'])
|
||||
workout.moving = _remove_microseconds(gpx_data['moving_time'])
|
||||
workout.min_alt = gpx_data['elevation_min']
|
||||
workout.max_alt = gpx_data['elevation_max']
|
||||
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
|
||||
provided
|
||||
"""
|
||||
workout_date = (
|
||||
gpx_data['start']
|
||||
workout_date, workout_date_tz = get_workout_datetime(
|
||||
workout_date=gpx_data['start']
|
||||
if gpx_data
|
||||
else datetime.strptime(workout_data['workout_date'], '%Y-%m-%d %H:%M')
|
||||
)
|
||||
workout_date_tz, workout_date = get_datetime_with_tz(
|
||||
user.timezone, workout_date, gpx_data
|
||||
else workout_data['workout_date'],
|
||||
date_str_format=None if gpx_data else '%Y-%m-%d %H:%M',
|
||||
user_timezone=user.timezone,
|
||||
with_timezone=True,
|
||||
)
|
||||
|
||||
duration = (
|
||||
gpx_data['duration']
|
||||
_remove_microseconds(gpx_data['duration'])
|
||||
if gpx_data
|
||||
else timedelta(seconds=workout_data['duration'])
|
||||
)
|
||||
@ -202,11 +220,10 @@ def edit_workout(
|
||||
workout.notes = workout_data.get('notes')
|
||||
if not workout.gpx:
|
||||
if workout_data.get('workout_date'):
|
||||
workout_date = datetime.strptime(
|
||||
workout_data['workout_date'], '%Y-%m-%d %H:%M'
|
||||
)
|
||||
_, workout.workout_date = get_datetime_with_tz(
|
||||
auth_user.timezone, workout_date
|
||||
workout.workout_date, _ = get_workout_datetime(
|
||||
workout_date=workout_data.get('workout_date', ''),
|
||||
date_str_format='%Y-%m-%d %H:%M',
|
||||
user_timezone=auth_user.timezone,
|
||||
)
|
||||
|
||||
if workout_data.get('duration'):
|
||||
|
@ -303,7 +303,7 @@ def get_workout(
|
||||
auth_user: User, workout_short_id: str
|
||||
) -> Union[Dict, HttpResponse]:
|
||||
"""
|
||||
Get an workout
|
||||
Get a workout
|
||||
|
||||
**Example request**:
|
||||
|
||||
@ -405,7 +405,7 @@ def get_workout_data(
|
||||
data_type: str,
|
||||
segment_id: Optional[int] = None,
|
||||
) -> 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 = Workout.query.filter_by(uuid=workout_uuid).first()
|
||||
if not workout:
|
||||
@ -467,7 +467,7 @@ def get_workout_gpx(
|
||||
auth_user: User, workout_short_id: str
|
||||
) -> 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**:
|
||||
|
||||
@ -517,7 +517,7 @@ def get_workout_chart_data(
|
||||
auth_user: User, workout_short_id: str
|
||||
) -> 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**:
|
||||
|
||||
@ -587,7 +587,7 @@ def get_segment_gpx(
|
||||
auth_user: User, workout_short_id: str, segment_id: int
|
||||
) -> 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**:
|
||||
|
||||
@ -639,7 +639,7 @@ def get_segment_chart_data(
|
||||
auth_user: User, workout_short_id: str, segment_id: int
|
||||
) -> 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**:
|
||||
|
||||
@ -851,7 +851,7 @@ def get_map_tile(s: str, z: str, x: str, y: str) -> Tuple[Response, int]:
|
||||
@require_auth(scopes='write')
|
||||
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**:
|
||||
|
||||
@ -1021,7 +1021,7 @@ def post_workout_no_gpx(
|
||||
auth_user: User,
|
||||
) -> Union[Tuple[Dict, int], HttpResponse]:
|
||||
"""
|
||||
Post an workout without gpx file
|
||||
Post a workout without gpx file
|
||||
|
||||
**Example request**:
|
||||
|
||||
@ -1111,7 +1111,8 @@ def post_workout_no_gpx(
|
||||
"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 integer duration: workout duration in seconds
|
||||
:<json string notes: notes (not mandatory)
|
||||
@ -1169,7 +1170,7 @@ def update_workout(
|
||||
auth_user: User, workout_short_id: str
|
||||
) -> Union[Dict, HttpResponse]:
|
||||
"""
|
||||
Update an workout
|
||||
Update a workout
|
||||
|
||||
**Example request**:
|
||||
|
||||
@ -1261,7 +1262,8 @@ def update_workout(
|
||||
|
||||
: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)
|
||||
:<json float distance: workout distance in km
|
||||
(only for workout without gpx)
|
||||
@ -1316,7 +1318,7 @@ def delete_workout(
|
||||
auth_user: User, workout_short_id: str
|
||||
) -> Union[Tuple[Dict, int], HttpResponse]:
|
||||
"""
|
||||
Delete an workout
|
||||
Delete a workout
|
||||
|
||||
**Example request**:
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "fittrackee_client",
|
||||
"version": "0.6.6",
|
||||
"version": "0.6.7",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
|
264
poetry.lock
generated
@ -8,11 +8,11 @@ python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "alembic"
|
||||
version = "1.7.7"
|
||||
version = "1.8.0"
|
||||
description = "A database migration tool for SQLAlchemy."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
importlib-metadata = {version = "*", markers = "python_version < \"3.9\""}
|
||||
@ -206,14 +206,14 @@ test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "6.4"
|
||||
version = "6.4.1"
|
||||
description = "Code coverage measurement for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[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]
|
||||
toml = ["tomli"]
|
||||
@ -397,11 +397,11 @@ typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.8\""
|
||||
|
||||
[[package]]
|
||||
name = "gpxpy"
|
||||
version = "1.3.4"
|
||||
version = "1.5.0"
|
||||
description = "GPX file parser and GPS track manipulation library"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "greenlet"
|
||||
@ -580,7 +580,7 @@ python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "0.960"
|
||||
version = "0.961"
|
||||
description = "Optional static typing for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@ -1001,7 +1001,7 @@ sphinx = ">=1.3.1"
|
||||
|
||||
[[package]]
|
||||
name = "redis"
|
||||
version = "4.3.1"
|
||||
version = "4.3.3"
|
||||
description = "Python client for Redis database and key-value store"
|
||||
category = "main"
|
||||
optional = false
|
||||
@ -1020,20 +1020,20 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.27.1"
|
||||
version = "2.28.0"
|
||||
description = "Python HTTP for Humans."
|
||||
category = "main"
|
||||
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]
|
||||
certifi = ">=2017.4.17"
|
||||
charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""}
|
||||
idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""}
|
||||
charset-normalizer = ">=2.0.0,<2.1.0"
|
||||
idna = ">=2.5,<4"
|
||||
urllib3 = ">=1.21.1,<1.27"
|
||||
|
||||
[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)"]
|
||||
|
||||
[[package]]
|
||||
@ -1238,7 +1238,7 @@ test = ["pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "sqlalchemy"
|
||||
version = "1.4.36"
|
||||
version = "1.4.37"
|
||||
description = "Database Abstraction Library"
|
||||
category = "main"
|
||||
optional = false
|
||||
@ -1263,7 +1263,7 @@ mysql_connector = ["mysql-connector-python"]
|
||||
oracle = ["cx_oracle (>=7,<8)", "cx_oracle (>=7)"]
|
||||
postgresql = ["psycopg2 (>=2.7)"]
|
||||
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_psycopg2cffi = ["psycopg2cffi"]
|
||||
pymysql = ["pymysql (<1)", "pymysql"]
|
||||
@ -1325,7 +1325,7 @@ python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "trio"
|
||||
version = "0.20.0"
|
||||
version = "0.21.0"
|
||||
description = "A friendly Python library for async concurrency and I/O"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@ -1363,7 +1363,7 @@ python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "types-freezegun"
|
||||
version = "1.1.9"
|
||||
version = "1.1.10"
|
||||
description = "Typing stubs for freezegun"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@ -1379,7 +1379,7 @@ python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "types-requests"
|
||||
version = "2.27.29"
|
||||
version = "2.27.30"
|
||||
description = "Typing stubs for requests"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@ -1477,7 +1477,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.7"
|
||||
content-hash = "84bb7759a500c94555fb8d8e5ca25bd98b287f6c53c80eb259bcc3c0d31b1031"
|
||||
content-hash = "6d019761837f6c9003d5749a29af885eed271bc8409c9b3dcb07f290247f7437"
|
||||
|
||||
[metadata.files]
|
||||
alabaster = [
|
||||
@ -1485,8 +1485,8 @@ alabaster = [
|
||||
{file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"},
|
||||
]
|
||||
alembic = [
|
||||
{file = "alembic-1.7.7-py3-none-any.whl", hash = "sha256:29be0856ec7591c39f4e1cb10f198045d890e6e2274cf8da80cb5e721a09642b"},
|
||||
{file = "alembic-1.7.7.tar.gz", hash = "sha256:4961248173ead7ce8a21efb3de378f13b8398e6630fab0eb258dc74a8af24c58"},
|
||||
{file = "alembic-1.8.0-py3-none-any.whl", hash = "sha256:b5ae4bbfc7d1302ed413989d39474d102e7cfa158f6d5969d2497955ffe85a30"},
|
||||
{file = "alembic-1.8.0.tar.gz", hash = "sha256:a2d4d90da70b30e70352cd9455e35873a255a31402a438fe24815758d7a0e5e1"},
|
||||
]
|
||||
async-generator = [
|
||||
{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"},
|
||||
]
|
||||
coverage = [
|
||||
{file = "coverage-6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:50ed480b798febce113709846b11f5d5ed1e529c88d8ae92f707806c50297abf"},
|
||||
{file = "coverage-6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:26f8f92699756cb7af2b30720de0c5bb8d028e923a95b6d0c891088025a1ac8f"},
|
||||
{file = "coverage-6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60c2147921da7f4d2d04f570e1838db32b95c5509d248f3fe6417e91437eaf41"},
|
||||
{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-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-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a022394996419142b33a0cf7274cb444c01d2bb123727c4bb0b9acabcb515dea"},
|
||||
{file = "coverage-6.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5a78cf2c43b13aa6b56003707c5203f28585944c277c1f3f109c7b041b16bd39"},
|
||||
{file = "coverage-6.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9229d074e097f21dfe0643d9d0140ee7433814b3f0fc3706b4abffd1e3038632"},
|
||||
{file = "coverage-6.4-cp310-cp310-win32.whl", hash = "sha256:fb45fe08e1abc64eb836d187b20a59172053999823f7f6ef4f18a819c44ba16f"},
|
||||
{file = "coverage-6.4-cp310-cp310-win_amd64.whl", hash = "sha256:3cfd07c5889ddb96a401449109a8b97a165be9d67077df6802f59708bfb07720"},
|
||||
{file = "coverage-6.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:03014a74023abaf5a591eeeaf1ac66a73d54eba178ff4cb1fa0c0a44aae70383"},
|
||||
{file = "coverage-6.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c82f2cd69c71698152e943f4a5a6b83a3ab1db73b88f6e769fabc86074c3b08"},
|
||||
{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-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-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c53ad261dfc8695062fc8811ac7c162bd6096a05a19f26097f411bdf5747aee7"},
|
||||
{file = "coverage-6.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:eef5292b60b6de753d6e7f2d128d5841c7915fb1e3321c3a1fe6acfe76c38052"},
|
||||
{file = "coverage-6.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:543e172ce4c0de533fa892034cce260467b213c0ea8e39da2f65f9a477425211"},
|
||||
{file = "coverage-6.4-cp37-cp37m-win32.whl", hash = "sha256:00c8544510f3c98476bbd58201ac2b150ffbcce46a8c3e4fb89ebf01998f806a"},
|
||||
{file = "coverage-6.4-cp37-cp37m-win_amd64.whl", hash = "sha256:b84ab65444dcc68d761e95d4d70f3cfd347ceca5a029f2ffec37d4f124f61311"},
|
||||
{file = "coverage-6.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d548edacbf16a8276af13063a2b0669d58bbcfca7c55a255f84aac2870786a61"},
|
||||
{file = "coverage-6.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:033ebec282793bd9eb988d0271c211e58442c31077976c19c442e24d827d356f"},
|
||||
{file = "coverage-6.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:742fb8b43835078dd7496c3c25a1ec8d15351df49fb0037bffb4754291ef30ce"},
|
||||
{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-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-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:62d382f7d77eeeaff14b30516b17bcbe80f645f5cf02bb755baac376591c653c"},
|
||||
{file = "coverage-6.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:016d7f5cf1c8c84f533a3c1f8f36126fbe00b2ec0ccca47cc5731c3723d327c6"},
|
||||
{file = "coverage-6.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:69432946f154c6add0e9ede03cc43b96e2ef2733110a77444823c053b1ff5166"},
|
||||
{file = "coverage-6.4-cp38-cp38-win32.whl", hash = "sha256:83bd142cdec5e4a5c4ca1d4ff6fa807d28460f9db919f9f6a31babaaa8b88426"},
|
||||
{file = "coverage-6.4-cp38-cp38-win_amd64.whl", hash = "sha256:4002f9e8c1f286e986fe96ec58742b93484195defc01d5cc7809b8f7acb5ece3"},
|
||||
{file = "coverage-6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e4f52c272fdc82e7c65ff3f17a7179bc5f710ebc8ce8a5cadac81215e8326740"},
|
||||
{file = "coverage-6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b5578efe4038be02d76c344007b13119b2b20acd009a88dde8adec2de4f630b5"},
|
||||
{file = "coverage-6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8099ea680201c2221f8468c372198ceba9338a5fec0e940111962b03b3f716a"},
|
||||
{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-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-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8d2e80dd3438e93b19e1223a9850fa65425e77f2607a364b6fd134fcd52dc9df"},
|
||||
{file = "coverage-6.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:341e9c2008c481c5c72d0e0dbf64980a4b2238631a7f9780b0fe2e95755fb018"},
|
||||
{file = "coverage-6.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:21e6686a95025927775ac501e74f5940cdf6fe052292f3a3f7349b0abae6d00f"},
|
||||
{file = "coverage-6.4-cp39-cp39-win32.whl", hash = "sha256:968ed5407f9460bd5a591cefd1388cc00a8f5099de9e76234655ae48cfdbe2c3"},
|
||||
{file = "coverage-6.4-cp39-cp39-win_amd64.whl", hash = "sha256:e35217031e4b534b09f9b9a5841b9344a30a6357627761d4218818b865d45055"},
|
||||
{file = "coverage-6.4-pp36.pp37.pp38-none-any.whl", hash = "sha256:e637ae0b7b481905358624ef2e81d7fb0b1af55f5ff99f9ba05442a444b11e45"},
|
||||
{file = "coverage-6.4.tar.gz", hash = "sha256:727dafd7f67a6e1cad808dc884bd9c5a2f6ef1f8f6d2f22b37b96cb0080d4f49"},
|
||||
{file = "coverage-6.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f1d5aa2703e1dab4ae6cf416eb0095304f49d004c39e9db1d86f57924f43006b"},
|
||||
{file = "coverage-6.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ce1b258493cbf8aec43e9b50d89982346b98e9ffdfaae8ae5793bc112fb0068"},
|
||||
{file = "coverage-6.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c4e737f60c6936460c5be330d296dd5b48b3963f48634c53b3f7deb0f34ec4"},
|
||||
{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.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.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e57816f8ffe46b1df8f12e1b348f06d164fd5219beba7d9433ba79608ef011cc"},
|
||||
{file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:01c5615d13f3dd3aa8543afc069e5319cfa0c7d712f6e04b920431e5c564a749"},
|
||||
{file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75ab269400706fab15981fd4bd5080c56bd5cc07c3bccb86aab5e1d5a88dc8f4"},
|
||||
{file = "coverage-6.4.1-cp310-cp310-win32.whl", hash = "sha256:a7f3049243783df2e6cc6deafc49ea123522b59f464831476d3d1448e30d72df"},
|
||||
{file = "coverage-6.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:ee2ddcac99b2d2aec413e36d7a429ae9ebcadf912946b13ffa88e7d4c9b712d6"},
|
||||
{file = "coverage-6.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb73e0011b8793c053bfa85e53129ba5f0250fdc0392c1591fd35d915ec75c46"},
|
||||
{file = "coverage-6.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106c16dfe494de3193ec55cac9640dd039b66e196e4641fa8ac396181578b982"},
|
||||
{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.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.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cec3a0f75c8f1031825e19cd86ee787e87cf03e4fd2865c79c057092e69e3a3b"},
|
||||
{file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:129cd05ba6f0d08a766d942a9ed4b29283aff7b2cccf5b7ce279d50796860bb3"},
|
||||
{file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bf5601c33213d3cb19d17a796f8a14a9eaa5e87629a53979a5981e3e3ae166f6"},
|
||||
{file = "coverage-6.4.1-cp37-cp37m-win32.whl", hash = "sha256:269eaa2c20a13a5bf17558d4dc91a8d078c4fa1872f25303dddcbba3a813085e"},
|
||||
{file = "coverage-6.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f02cbbf8119db68455b9d763f2f8737bb7db7e43720afa07d8eb1604e5c5ae28"},
|
||||
{file = "coverage-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ffa9297c3a453fba4717d06df579af42ab9a28022444cae7fa605af4df612d54"},
|
||||
{file = "coverage-6.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:145f296d00441ca703a659e8f3eb48ae39fb083baba2d7ce4482fb2723e050d9"},
|
||||
{file = "coverage-6.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d44996140af8b84284e5e7d398e589574b376fb4de8ccd28d82ad8e3bea13"},
|
||||
{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.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.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9b3e07152b4563722be523e8cd0b209e0d1a373022cfbde395ebb6575bf6790d"},
|
||||
{file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1480ff858b4113db2718848d7b2d1b75bc79895a9c22e76a221b9d8d62496428"},
|
||||
{file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:865d69ae811a392f4d06bde506d531f6a28a00af36f5c8649684a9e5e4a85c83"},
|
||||
{file = "coverage-6.4.1-cp38-cp38-win32.whl", hash = "sha256:664a47ce62fe4bef9e2d2c430306e1428ecea207ffd68649e3b942fa8ea83b0b"},
|
||||
{file = "coverage-6.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:26dff09fb0d82693ba9e6231248641d60ba606150d02ed45110f9ec26404ed1c"},
|
||||
{file = "coverage-6.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d9c80df769f5ec05ad21ea34be7458d1dc51ff1fb4b2219e77fe24edf462d6df"},
|
||||
{file = "coverage-6.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:39ee53946bf009788108b4dd2894bf1349b4e0ca18c2016ffa7d26ce46b8f10d"},
|
||||
{file = "coverage-6.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5b66caa62922531059bc5ac04f836860412f7f88d38a476eda0a6f11d4724f4"},
|
||||
{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.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.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8c08da0bd238f2970230c2a0d28ff0e99961598cb2e810245d7fc5afcf1254e8"},
|
||||
{file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d42c549a8f41dc103a8004b9f0c433e2086add8a719da00e246e17cbe4056f72"},
|
||||
{file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:309ce4a522ed5fca432af4ebe0f32b21d6d7ccbb0f5fcc99290e71feba67c264"},
|
||||
{file = "coverage-6.4.1-cp39-cp39-win32.whl", hash = "sha256:fdb6f7bd51c2d1714cea40718f6149ad9be6a2ee7d93b19e9f00934c0f2a74d9"},
|
||||
{file = "coverage-6.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:342d4aefd1c3e7f620a13f4fe563154d808b69cccef415415aece4c786665397"},
|
||||
{file = "coverage-6.4.1-pp36.pp37.pp38-none-any.whl", hash = "sha256:4803e7ccf93230accb928f3a68f00ffa80a88213af98ed338a57ad021ef06815"},
|
||||
{file = "coverage-6.4.1.tar.gz", hash = "sha256:4321f075095a096e70aff1d002030ee612b65a205a0a0f5b815280d5dc58100c"},
|
||||
]
|
||||
cryptography = [
|
||||
{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"},
|
||||
]
|
||||
gpxpy = [
|
||||
{file = "gpxpy-1.3.4.tar.gz", hash = "sha256:4a0f072ae5bdf9270c7450e452f93a6c5c91d888114e8d78868a8f163b0dbb15"},
|
||||
{file = "gpxpy-1.5.0.tar.gz", hash = "sha256:e6993a8945eae07a833cd304b88bbc6c3c132d63b2bf4a9b0a5d9097616b8708"},
|
||||
]
|
||||
greenlet = [
|
||||
{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"},
|
||||
]
|
||||
mypy = [
|
||||
{file = "mypy-0.960-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3a3e525cd76c2c4f90f1449fd034ba21fcca68050ff7c8397bb7dd25dd8b8248"},
|
||||
{file = "mypy-0.960-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7a76dc4f91e92db119b1be293892df8379b08fd31795bb44e0ff84256d34c251"},
|
||||
{file = "mypy-0.960-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ffdad80a92c100d1b0fe3d3cf1a4724136029a29afe8566404c0146747114382"},
|
||||
{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.960-cp310-cp310-win_amd64.whl", hash = "sha256:925aa84369a07846b7f3b8556ccade1f371aa554f2bd4fb31cb97a24b73b036e"},
|
||||
{file = "mypy-0.960-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:239d6b2242d6c7f5822163ee082ef7a28ee02e7ac86c35593ef923796826a385"},
|
||||
{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.960-cp36-cp36m-win_amd64.whl", hash = "sha256:cb7752b24528c118a7403ee955b6a578bfcf5879d5ee91790667c8ea511d2085"},
|
||||
{file = "mypy-0.960-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:826a2917c275e2ee05b7c7b736c1e6549a35b7ea5a198ca457f8c2ebea2cbecf"},
|
||||
{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.960-cp37-cp37m-win_amd64.whl", hash = "sha256:f47322796c412271f5aea48381a528a613f33e0a115452d03ae35d673e6064f8"},
|
||||
{file = "mypy-0.960-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2c7f8bb9619290836a4e167e2ef1f2cf14d70e0bc36c04441e41487456561409"},
|
||||
{file = "mypy-0.960-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fbfb873cf2b8d8c3c513367febde932e061a5f73f762896826ba06391d932b2a"},
|
||||
{file = "mypy-0.960-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cc537885891382e08129d9862553b3d00d4be3eb15b8cae9e2466452f52b0117"},
|
||||
{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.960-cp38-cp38-win_amd64.whl", hash = "sha256:29dc94d9215c3eb80ac3c2ad29d0c22628accfb060348fd23d73abe3ace6c10d"},
|
||||
{file = "mypy-0.960-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:33d53a232bb79057f33332dbbb6393e68acbcb776d2f571ba4b1d50a2c8ba873"},
|
||||
{file = "mypy-0.960-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8d645e9e7f7a5da3ec3bbcc314ebb9bb22c7ce39e70367830eb3c08d0140b9ce"},
|
||||
{file = "mypy-0.960-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:85cf2b14d32b61db24ade8ac9ae7691bdfc572a403e3cb8537da936e74713275"},
|
||||
{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.960-cp39-cp39-win_amd64.whl", hash = "sha256:0ebfb3f414204b98c06791af37a3a96772203da60636e2897408517fcfeee7a8"},
|
||||
{file = "mypy-0.960-py3-none-any.whl", hash = "sha256:bfd4f6536bd384c27c392a8b8f790fd0ed5c0cf2f63fc2fed7bce56751d53026"},
|
||||
{file = "mypy-0.960.tar.gz", hash = "sha256:d4fccf04c1acf750babd74252e0f2db6bd2ac3aa8fe960797d9f3ef41cf2bfd4"},
|
||||
{file = "mypy-0.961-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:697540876638ce349b01b6786bc6094ccdaba88af446a9abb967293ce6eaa2b0"},
|
||||
{file = "mypy-0.961-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b117650592e1782819829605a193360a08aa99f1fc23d1d71e1a75a142dc7e15"},
|
||||
{file = "mypy-0.961-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bdd5ca340beffb8c44cb9dc26697628d1b88c6bddf5c2f6eb308c46f269bb6f3"},
|
||||
{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.961-cp310-cp310-win_amd64.whl", hash = "sha256:e999229b9f3198c0c880d5e269f9f8129c8862451ce53a011326cad38b9ccd24"},
|
||||
{file = "mypy-0.961-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b24be97351084b11582fef18d79004b3e4db572219deee0212078f7cf6352723"},
|
||||
{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.961-cp36-cp36m-win_amd64.whl", hash = "sha256:439c726a3b3da7ca84a0199a8ab444cd8896d95012c4a6c4a0d808e3147abf5d"},
|
||||
{file = "mypy-0.961-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a0b53747f713f490affdceef835d8f0cb7285187a6a44c33821b6d1f46ed813"},
|
||||
{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.961-cp37-cp37m-win_amd64.whl", hash = "sha256:b88f784e9e35dcaa075519096dc947a388319cb86811b6af621e3523980f1c8a"},
|
||||
{file = "mypy-0.961-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d5aaf1edaa7692490f72bdb9fbd941fbf2e201713523bdb3f4038be0af8846c6"},
|
||||
{file = "mypy-0.961-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9f5f5a74085d9a81a1f9c78081d60a0040c3efb3f28e5c9912b900adf59a16e6"},
|
||||
{file = "mypy-0.961-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f4b794db44168a4fc886e3450201365c9526a522c46ba089b55e1f11c163750d"},
|
||||
{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.961-cp38-cp38-win_amd64.whl", hash = "sha256:63e85a03770ebf403291ec50097954cc5caf2a9205c888ce3a61bd3f82e17569"},
|
||||
{file = "mypy-0.961-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f1332964963d4832a94bebc10f13d3279be3ce8f6c64da563d6ee6e2eeda932"},
|
||||
{file = "mypy-0.961-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:006be38474216b833eca29ff6b73e143386f352e10e9c2fbe76aa8549e5554f5"},
|
||||
{file = "mypy-0.961-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9940e6916ed9371809b35b2154baf1f684acba935cd09928952310fbddaba648"},
|
||||
{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.961-cp39-cp39-win_amd64.whl", hash = "sha256:1ece702f29270ec6af25db8cf6185c04c02311c6bb21a69f423d40e527b75c56"},
|
||||
{file = "mypy-0.961-py3-none-any.whl", hash = "sha256:03c6cc893e7563e7b2949b969e63f02c000b32502a1b4d1314cabe391aa87d66"},
|
||||
{file = "mypy-0.961.tar.gz", hash = "sha256:f730d56cb924d371c26b8eaddeea3cc07d78ff51c521c6d04899ac6904b75492"},
|
||||
]
|
||||
mypy-extensions = [
|
||||
{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"},
|
||||
]
|
||||
redis = [
|
||||
{file = "redis-4.3.1-py3-none-any.whl", hash = "sha256:84316970995a7adb907a56754d2b92d88fc2d252963dc5ac34c88f0f1a22c25d"},
|
||||
{file = "redis-4.3.1.tar.gz", hash = "sha256:94b617b4cd296e94991146f66fc5559756fbefe9493604f0312e4d3298ac63e9"},
|
||||
{file = "redis-4.3.3-py3-none-any.whl", hash = "sha256:f57f8df5d238a8ecf92f499b6b21467bfee6c13d89953c27edf1e2bc673622e7"},
|
||||
{file = "redis-4.3.3.tar.gz", hash = "sha256:2f7a57cf4af15cd543c4394bcbe2b9148db2606a37edba755368836e3a1d053e"},
|
||||
]
|
||||
requests = [
|
||||
{file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
|
||||
{file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
|
||||
{file = "requests-2.28.0-py3-none-any.whl", hash = "sha256:bc7861137fbce630f17b03d3ad02ad0bf978c844f3536d0edda6499dafce2b6f"},
|
||||
{file = "requests-2.28.0.tar.gz", hash = "sha256:d568723a7ebd25875d8d1eaf5dfa068cd2fc8194b2e483d7b1f7c81918dbec6b"},
|
||||
]
|
||||
responses = [
|
||||
{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"},
|
||||
]
|
||||
sqlalchemy = [
|
||||
{file = "SQLAlchemy-1.4.36-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:81e53bd383c2c33de9d578bfcc243f559bd3801a0e57f2bcc9a943c790662e0c"},
|
||||
{file = "SQLAlchemy-1.4.36-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6e1fe00ee85c768807f2a139b83469c1e52a9ffd58a6eb51aa7aeb524325ab18"},
|
||||
{file = "SQLAlchemy-1.4.36-cp27-cp27m-win32.whl", hash = "sha256:d57ac32f8dc731fddeb6f5d1358b4ca5456e72594e664769f0a9163f13df2a31"},
|
||||
{file = "SQLAlchemy-1.4.36-cp27-cp27m-win_amd64.whl", hash = "sha256:fca8322e04b2dde722fcb0558682740eebd3bd239bea7a0d0febbc190e99dc15"},
|
||||
{file = "SQLAlchemy-1.4.36-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:53d2d9ee93970c969bc4e3c78b1277d7129554642f6ffea039c282c7dc4577bc"},
|
||||
{file = "SQLAlchemy-1.4.36-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:f0394a3acfb8925db178f7728adb38c027ed7e303665b225906bfa8099dc1ce8"},
|
||||
{file = "SQLAlchemy-1.4.36-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c606d8238feae2f360b8742ffbe67741937eb0a05b57f536948d198a3def96"},
|
||||
{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.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.36-cp310-cp310-win32.whl", hash = "sha256:be094460930087e50fd08297db9d7aadaed8408ad896baf758e9190c335632da"},
|
||||
{file = "SQLAlchemy-1.4.36-cp310-cp310-win_amd64.whl", hash = "sha256:64d796e9af522162f7f2bf7a3c5531a0a550764c426782797bbeed809d0646c5"},
|
||||
{file = "SQLAlchemy-1.4.36-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:a0ae3aa2e86a4613f2d4c49eb7da23da536e6ce80b2bfd60bbb2f55fc02b0b32"},
|
||||
{file = "SQLAlchemy-1.4.36-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d50cb71c1dbed70646d521a0975fb0f92b7c3f84c61fa59e07be23a1aaeecfc"},
|
||||
{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.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.36-cp36-cp36m-win32.whl", hash = "sha256:fbf8c09fe9728168f8cc1b40c239eab10baf9c422c18be7f53213d70434dea43"},
|
||||
{file = "SQLAlchemy-1.4.36-cp36-cp36m-win_amd64.whl", hash = "sha256:6e859fa96605027bd50d8e966db1c4e1b03e7b3267abbc4b89ae658c99393c58"},
|
||||
{file = "SQLAlchemy-1.4.36-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:166a3887ec355f7d2f12738f7fa25dc8ac541867147a255f790f2f41f614cb44"},
|
||||
{file = "SQLAlchemy-1.4.36-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e885548da361aa3f8a9433db4cfb335b2107e533bf314359ae3952821d84b3e"},
|
||||
{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.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.36-cp37-cp37m-win32.whl", hash = "sha256:dce3468bf1fc12374a1a732c9efd146ce034f91bb0482b602a9311cb6166a920"},
|
||||
{file = "SQLAlchemy-1.4.36-cp37-cp37m-win_amd64.whl", hash = "sha256:6cb4c4f57a20710cea277edf720d249d514e587f796b75785ad2c25e1c0fed26"},
|
||||
{file = "SQLAlchemy-1.4.36-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:e74ce103b81c375c3853b436297952ef8d7863d801dcffb6728d01544e5191b5"},
|
||||
{file = "SQLAlchemy-1.4.36-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b20c4178ead9bc398be479428568ff31b6c296eb22e75776273781a6551973f"},
|
||||
{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.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.36-cp38-cp38-win32.whl", hash = "sha256:ce20f5da141f8af26c123ebaa1b7771835ca6c161225ce728962a79054f528c3"},
|
||||
{file = "SQLAlchemy-1.4.36-cp38-cp38-win_amd64.whl", hash = "sha256:316c7e5304dda3e3ad711569ac5d02698bbc71299b168ac56a7076b86259f7ea"},
|
||||
{file = "SQLAlchemy-1.4.36-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:f522214f6749bc073262529c056f7dfd660f3b5ec4180c5354d985eb7219801e"},
|
||||
{file = "SQLAlchemy-1.4.36-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ecac4db8c1aa4a269f5829df7e706639a24b780d2ac46b3e485cbbd27ec0028"},
|
||||
{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.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.36-cp39-cp39-win32.whl", hash = "sha256:e12532c4d3f614678623da5d852f038ace1f01869b89f003ed6fe8c793f0c6a3"},
|
||||
{file = "SQLAlchemy-1.4.36-cp39-cp39-win_amd64.whl", hash = "sha256:cb441ca461bf97d00877b607f132772644b623518b39ced54da433215adce691"},
|
||||
{file = "SQLAlchemy-1.4.36.tar.gz", hash = "sha256:64678ac321d64a45901ef2e24725ec5e783f1f4a588305e196431447e7ace243"},
|
||||
{file = "SQLAlchemy-1.4.37-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:d9050b0c4a7f5538650c74aaba5c80cd64450e41c206f43ea6d194ae6d060ff9"},
|
||||
{file = "SQLAlchemy-1.4.37-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b4c92823889cf9846b972ee6db30c0e3a92c0ddfc76c6060a6cda467aa5fb694"},
|
||||
{file = "SQLAlchemy-1.4.37-cp27-cp27m-win32.whl", hash = "sha256:b55932fd0e81b43f4aff397c8ad0b3c038f540af37930423ab8f47a20b117e4c"},
|
||||
{file = "SQLAlchemy-1.4.37-cp27-cp27m-win_amd64.whl", hash = "sha256:4a17c1a1152ca4c29d992714aa9df3054da3af1598e02134f2e7314a32ef69d8"},
|
||||
{file = "SQLAlchemy-1.4.37-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ffe487570f47536b96eff5ef2b84034a8ba4e19aab5ab7647e677d94a119ea55"},
|
||||
{file = "SQLAlchemy-1.4.37-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:78363f400fbda80f866e8e91d37d36fe6313ff847ded08674e272873c1377ea5"},
|
||||
{file = "SQLAlchemy-1.4.37-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ee34c85cbda7779d66abac392c306ec78c13f5c73a1f01b8b767916d4895d23"},
|
||||
{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.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.37-cp310-cp310-win32.whl", hash = "sha256:2aac2a685feb9882d09f457f4e5586c885d578af4e97a2b759e91e8c457cbce5"},
|
||||
{file = "SQLAlchemy-1.4.37-cp310-cp310-win_amd64.whl", hash = "sha256:7a44683cf97744a405103ef8fdd31199e9d7fc41b4a67e9044523b29541662b0"},
|
||||
{file = "SQLAlchemy-1.4.37-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:cffc67cdd07f0e109a1fc83e333972ae423ea5ad414585b63275b66b870ea62b"},
|
||||
{file = "SQLAlchemy-1.4.37-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17417327b87a0f703c9a20180f75e953315207d048159aff51822052f3e33e69"},
|
||||
{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.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.37-cp36-cp36m-win32.whl", hash = "sha256:0e7fd52e48e933771f177c2a1a484b06ea03774fc7741651ebdf19985a34037c"},
|
||||
{file = "SQLAlchemy-1.4.37-cp36-cp36m-win_amd64.whl", hash = "sha256:eec39a17bab3f69c44c9df4e0ed87c7306f2d2bf1eca3070af644927ec4199fa"},
|
||||
{file = "SQLAlchemy-1.4.37-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:caca6acf3f90893d7712ae2c6616ecfeac3581b4cc677c928a330ce6fbad4319"},
|
||||
{file = "SQLAlchemy-1.4.37-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50c8eaf44c3fed5ba6758d375de25f163e46137c39fda3a72b9ee1d1bb327dfc"},
|
||||
{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.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.37-cp37-cp37m-win32.whl", hash = "sha256:9785d6f962d2c925aeb06a7539ac9d16608877da6aeaaf341984b3693ae80a02"},
|
||||
{file = "SQLAlchemy-1.4.37-cp37-cp37m-win_amd64.whl", hash = "sha256:3197441772dc3b1c6419f13304402f2418a18d7fe78000aa5a026e7100836739"},
|
||||
{file = "SQLAlchemy-1.4.37-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:3862a069a24f354145e01a76c7c720c263d62405fe5bed038c46a7ce900f5dd6"},
|
||||
{file = "SQLAlchemy-1.4.37-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e8706919829d455a9fa687c6bbd1b048e36fec3919a59f2d366247c2bfdbd9c"},
|
||||
{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.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.37-cp38-cp38-win32.whl", hash = "sha256:d6927c9e3965b194acf75c8e0fb270b4d54512db171f65faae15ef418721996e"},
|
||||
{file = "SQLAlchemy-1.4.37-cp38-cp38-win_amd64.whl", hash = "sha256:a91d0668cada27352432f15b92ac3d43e34d8f30973fa8b86f5e9fddee928f3b"},
|
||||
{file = "SQLAlchemy-1.4.37-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:f9940528bf9c4df9e3c3872d23078b6b2da6431c19565637c09f1b88a427a684"},
|
||||
{file = "SQLAlchemy-1.4.37-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29a742c29fea12259f1d2a9ee2eb7fe4694a85d904a4ac66d15e01177b17ad7f"},
|
||||
{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.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.37-cp39-cp39-win32.whl", hash = "sha256:5e4e517ce72fad35cce364a01aff165f524449e9c959f1837dc71088afa2824c"},
|
||||
{file = "SQLAlchemy-1.4.37-cp39-cp39-win_amd64.whl", hash = "sha256:c37885f83b59e248bebe2b35beabfbea398cb40960cdc6d3a76eac863d4e1938"},
|
||||
{file = "SQLAlchemy-1.4.37.tar.gz", hash = "sha256:3688f92c62db6c5df268e2264891078f17ecb91e3141b400f2e28d0f75796dea"},
|
||||
]
|
||||
staticmap = [
|
||||
{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"},
|
||||
]
|
||||
trio = [
|
||||
{file = "trio-0.20.0-py3-none-any.whl", hash = "sha256:fb2d48e4eab0dfb786a472cd514aaadc71e3445b203bc300bad93daa75d77c1a"},
|
||||
{file = "trio-0.20.0.tar.gz", hash = "sha256:670a52d3115d0e879e1ac838a4eb999af32f858163e3a704fe4839de2a676070"},
|
||||
{file = "trio-0.21.0-py3-none-any.whl", hash = "sha256:4dc0bf9d5cc78767fc4516325b6d80cc0968705a31d0eec2ecd7cdda466265b0"},
|
||||
{file = "trio-0.21.0.tar.gz", hash = "sha256:523f39b7b69eef73501cebfe1aafd400a9aad5b03543a0eded52952488ff1c13"},
|
||||
]
|
||||
trio-websocket = [
|
||||
{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"},
|
||||
]
|
||||
types-freezegun = [
|
||||
{file = "types-freezegun-1.1.9.tar.gz", hash = "sha256:6f05108d468baecadf999873bd37e57b25ceb35d35d3f83e7a742f25d6fe8b0e"},
|
||||
{file = "types_freezegun-1.1.9-py3-none-any.whl", hash = "sha256:fe1dd73372d96358dcb93e3aeb66d39f6ac63749e0724f13554cc145e2120efe"},
|
||||
{file = "types-freezegun-1.1.10.tar.gz", hash = "sha256:cb3a2d2eee950eacbaac0673ab50499823365ceb8c655babb1544a41446409ec"},
|
||||
{file = "types_freezegun-1.1.10-py3-none-any.whl", hash = "sha256:fadebe72213e0674036153366205038e1f95c8ca96deb4ef9b71ddc15413543e"},
|
||||
]
|
||||
types-pytz = [
|
||||
{file = "types-pytz-2021.3.8.tar.gz", hash = "sha256:41253a3a2bf028b6a3f17b58749a692d955af0f74e975de94f6f4d2d3cd01dbd"},
|
||||
{file = "types_pytz-2021.3.8-py3-none-any.whl", hash = "sha256:aef4a917ab28c585d3f474bfce4f4b44b91e95d9d47d4de29dd845e0db8e3910"},
|
||||
]
|
||||
types-requests = [
|
||||
{file = "types-requests-2.27.29.tar.gz", hash = "sha256:fb453b3a76a48eca66381cea8004feaaea12835e838196f5c7ac87c75c5c19ef"},
|
||||
{file = "types_requests-2.27.29-py3-none-any.whl", hash = "sha256:014f4f82db7b96c41feea9adaea30e68cd64c230eeab34b70c29bebb26ec74ac"},
|
||||
{file = "types-requests-2.27.30.tar.gz", hash = "sha256:ca8d7cc549c3d10dbcb3c69c1b53e3ffd1270089c1001a65c1e9e1017eb5e704"},
|
||||
{file = "types_requests-2.27.30-py3-none-any.whl", hash = "sha256:b9b6cd0a6e5d500e56419b79f44ec96f316e9375ff6c8ee566c39d25e9612621"},
|
||||
]
|
||||
types-urllib3 = [
|
||||
{file = "types-urllib3-1.26.15.tar.gz", hash = "sha256:c89283541ef92e344b7f59f83ea9b5a295b16366ceee3f25ecfc5593c79f794e"},
|
||||
|
@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "fittrackee"
|
||||
version = "0.6.6"
|
||||
version = "0.6.7"
|
||||
description = "Self-hosted outdoor workout/activity tracker"
|
||||
authors = ["SamR1"]
|
||||
license = "AGPL-3.0"
|
||||
@ -29,7 +29,7 @@ flask = "^2.1"
|
||||
flask-bcrypt = "^1.0"
|
||||
flask-dramatiq = "^0.6.0"
|
||||
flask-migrate = "^3.1"
|
||||
gpxpy = "=1.3.4"
|
||||
gpxpy = "=1.5.0"
|
||||
gunicorn = "^20.1"
|
||||
humanize = "^4.1"
|
||||
psycopg2-binary = "^2.9"
|
||||
@ -38,7 +38,7 @@ python-forecastio = "^1.4"
|
||||
pytz = "^2022.1"
|
||||
shortuuid = "^1.0.9"
|
||||
staticmap = "^0.5.4"
|
||||
SQLAlchemy = "1.4.36"
|
||||
SQLAlchemy = "1.4.37"
|
||||
pyOpenSSL = "^22.0"
|
||||
ua-parser = "^0.10.0"
|
||||
Authlib = "^1.0.1"
|
||||
@ -46,7 +46,7 @@ Authlib = "^1.0.1"
|
||||
[tool.poetry.dev-dependencies]
|
||||
black = "^22.3"
|
||||
freezegun = "^1.2"
|
||||
mypy = "^0.960"
|
||||
mypy = "^0.961"
|
||||
pytest = "^7.1"
|
||||
pytest-black = "^0.3.12"
|
||||
pytest-cov = "^3.0"
|
||||
|