Merge branch 'release-v0.7.11'

This commit is contained in:
Sam 2022-12-31 18:39:31 +01:00
commit 22631f61e0
101 changed files with 1635 additions and 718 deletions

View File

@ -19,7 +19,7 @@ export DATABASE_TEST_URL=postgresql://fittrackee:fittrackee@fittrackee-db:5432/f
export REDIS_URL=redis://redis:6379
# API rate limits
# export API_RATE_LIMITS=300 per 5 minutes
# export API_RATE_LIMITS="300 per 5 minutes"
# Emails
export UI_URL=http://0.0.0.0:5000
@ -31,6 +31,11 @@ export WORKERS_PROCESSES=2
# Workouts
# export TILE_SERVER_URL=
# export STATICMAP_SUBDOMAINS=
# export MAP_ATTRIBUTION=
# export DEFAULT_STATICMAP=False
# Weather
# available weather API providers: darksky, visualcrossing
# export WEATHER_API_PROVIDER=
# export WEATHER_API_KEY=

View File

@ -20,7 +20,7 @@ export UPLOAD_FOLDER=
# export REDIS_URL=
# API rate limits
# export API_RATE_LIMITS=300 per 5 minutes
# export API_RATE_LIMITS="300 per 5 minutes"
# Emails
export UI_URL=
@ -33,4 +33,8 @@ export SENDER_EMAIL=
# export STATICMAP_SUBDOMAINS=
# export MAP_ATTRIBUTION=
# export DEFAULT_STATICMAP=False
# Weather
# available weather API providers: darksky, visualcrossing
# export WEATHER_API_PROVIDER=
# export WEATHER_API_KEY=

View File

@ -1,7 +1,28 @@
# Change log
## Version 0.7.11 (2022/12/31)
### Features and enhancements
* [PR#265](https://github.com/SamR1/FitTrackee/pull/265) - Implementing alternative weather API (VisualCrossing.com)
**Note**: A new environment variable must be to set to configure the weather data provider: `WEATHER_API_PROVIDER` (see [documentation](https://samr1.github.io/FitTrackee/installation.html#weather-data))
### Translations
* [PR#287](https://github.com/SamR1/FitTrackee/pull/287) - Translations update from Hosted Weblate (Dutch)
* [PR#289](https://github.com/SamR1/FitTrackee/pull/289) - Translations update from Hosted Weblate (German)
Thanks to the contributors:
- @bjornclauw
- @jat255
- @qwerty287
## Version 0.7.10 (2022/12/21)
FitTrackee is now available in Italian (thanks to @dperruso).
### Features and enhancements
* [#92](https://github.com/SamR1/FitTrackee/issues/92) - Add ascent and descent parameters in workout import without GPX file

View File

@ -1 +1 @@
0.7.10
0.7.11

View File

@ -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: 5abf538cacc290d4cea92d1f42946aba
config: 042de095012a0d21fe180df7971496bf
tags: 645f666f9bcd5a90fca523b33c5a78b7

Binary file not shown.

Before

Width:  |  Height:  |  Size: 567 KiB

After

Width:  |  Height:  |  Size: 567 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 378 KiB

After

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 83 KiB

View File

@ -1,7 +1,28 @@
# Change log
## Version 0.7.11 (2022/12/31)
### Features and enhancements
* [PR#265](https://github.com/SamR1/FitTrackee/pull/265) - Implementing alternative weather API (VisualCrossing.com)
**Note**: A new environment variable must be to set to configure the weather data provider: `WEATHER_API_PROVIDER` (see [documentation](https://samr1.github.io/FitTrackee/installation.html#weather-data))
### Translations
* [PR#287](https://github.com/SamR1/FitTrackee/pull/287) - Translations update from Hosted Weblate (Dutch)
* [PR#289](https://github.com/SamR1/FitTrackee/pull/289) - Translations update from Hosted Weblate (German)
Thanks to the contributors:
- @bjornclauw
- @jat255
- @qwerty287
## Version 0.7.10 (2022/12/21)
FitTrackee is now available in Italian (thanks to @dperruso).
### Features and enhancements
* [#92](https://github.com/SamR1/FitTrackee/issues/92) - Add ascent and descent parameters in workout import without GPX file

View File

@ -43,7 +43,7 @@ Workouts
| Ascent and descent can also be provided (*new in 0.7.10*).
- | A workout with a gpx file can be displayed with map and charts (speed and elevation).
| Controls allow full screen view and position reset (*new in 0.5.5*).
- | If DarkSky API key is provided, weather is displayed in workout detail.
- | If **DarkSky API** or **Visual Crossing** (*new in 0.7.11*) API key is provided, weather is displayed in workout detail. Data source is displayed in About page.
| Wind is displayed, with arrow indicating direction (a tooltip can be displayed with the direction that the wind is coming **from**) (*new in 0.5.5*).
- Segments can be displayed.
- Workout gpx file can be downloaded (*new in 0.5.1*)

View File

@ -28,7 +28,7 @@ Prerequisites
- optional
- Redis for task queue (if email sending is enabled) and API rate limits
- SMTP provider (if email sending is enabled)
- API key from `Dark Sky <https://darksky.net/dev>`__ (deprecated, DarkSky will stop on March 31st, 2023)
- API key from a `weather data provider <installation.html#weather-data>`__
- `Poetry <https://poetry.eustace.io>`__ (for installation from sources only)
- `Yarn <https://yarnpkg.com>`__ (for development only)
- Docker and Docker Compose (for development or evaluation purposes)
@ -172,7 +172,7 @@ deployment method.
Number of processes used by **Dramatiq**.
.. envvar:: API_RATE_LIMITS 🆕
.. envvar:: API_RATE_LIMITS
.. versionadded:: 0.7.0
@ -229,7 +229,14 @@ deployment method.
.. versionchanged:: 0.4.0 ⚠️ replaces ``WEATHER_API``
**Dark Sky** API key for weather data (not mandatory).
Weather API key (not mandatory), see ``WEATHER_API_PROVIDER``.
.. envvar:: WEATHER_API_PROVIDER 🆕
.. versionadded:: 0.7.11
Provider for weather data (not mandatory), see `Weather data <installation.html#weather-data>`__.
.. envvar:: VUE_APP_API_URL
@ -302,8 +309,8 @@ For instance, to set OSM France tile server, the expected values are:
The subdomain will be chosen randomly.
API rate limits 🆕
^^^^^^^^^^^^^^^^^^
API rate limits
^^^^^^^^^^^^^^^
.. versionadded:: 0.7.0
| API rate limits are managed by `Flask-Limiter <https://flask-limiter.readthedocs.io/en/stable>`_, based on IP with fixed window strategy.
@ -338,6 +345,21 @@ API rate limits 🆕
limits Enumerate details about all routes with rate limits
Weather data
^^^^^^^^^^^^
.. versionchanged:: 0.7.11
The following weather data providers are supported by **FitTrackee**:
- `Dark Sky <https://darksky.net>`__ (deprecated, will stop on March 31st, 2023)
- `Visual Crossing <https://www.visualcrossing.com>`__ (**note**: historical data are provided on hourly period)
To configure a weather provider, set the following environment variables:
- ``WEATHER_API_PROVIDER``: ``darksky`` for **Dark Sky** or ``visualcrossing`` for **Visual Crossing**
- ``WEATHER_API_KEY``: the key to the corresponding weather provider
Installation
~~~~~~~~~~~~
@ -480,13 +502,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.7.10):
- Download the last release (for now, it is the release v0.7.11):
.. code:: bash
$ wget https://github.com/SamR1/FitTrackee/archive/v0.7.10.tar.gz
$ tar -xzf v0.7.10.tar.gz
$ mv FitTrackee-0.7.10 FitTrackee
$ wget https://github.com/SamR1/FitTrackee/archive/v0.7.11.tar.gz
$ tar -xzf v0.7.11.tar.gz
$ mv FitTrackee-0.7.11 FitTrackee
$ cd FitTrackee
- Create **.env** from example and update it
@ -606,13 +628,13 @@ Prod environment
- Change to the directory where FitTrackee directory is located
- Download the last release (for now, it is the release v0.7.10) and overwrite existing files:
- Download the last release (for now, it is the release v0.7.11) and overwrite existing files:
.. code:: bash
$ wget https://github.com/SamR1/FitTrackee/archive/v0.7.10.tar.gz
$ tar -xzf v0.7.10.tar.gz
$ cp -R FitTrackee-0.7.10/* FitTrackee/
$ wget https://github.com/SamR1/FitTrackee/archive/v0.7.11.tar.gz
$ tar -xzf v0.7.11.tar.gz
$ cp -R FitTrackee-0.7.11/* FitTrackee/
$ cd FitTrackee
- Update **.env** if needed (see `Environment variables <installation.html#environment-variables>`__).

View File

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

View File

@ -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.19: https://docutils.sourceforge.io/" />
<title>Authentication &#8212; FitTrackee 0.7.10
<title>Authentication &#8212; FitTrackee 0.7.11
documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
@ -43,7 +43,7 @@
</button>
<a class="navbar-brand" href="../index.html">
FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -1239,7 +1239,7 @@ for other reasons.</p></li>
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

View File

@ -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.19: https://docutils.sourceforge.io/" />
<title>Configuration &#8212; FitTrackee 0.7.10
<title>Configuration &#8212; FitTrackee 0.7.11
documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
@ -43,7 +43,7 @@
</button>
<a class="navbar-brand" href="../index.html">
FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -154,8 +154,9 @@
<span class="w"> </span><span class="nt">&quot;max_single_file_size&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1048576</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;max_users&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;max_zip_file_size&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">10485760</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;map_attribution&quot;</span><span class="p">:</span><span class="w"> </span><span class="nt">&quot;&amp;copy; &lt;a href=http://www.openstreetmap.org/copyright&gt;OpenStreetMap&lt;/a&gt; contributors&quot;</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;version&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;0.7.10&quot;</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;map_attribution&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;&amp;copy; &lt;a href=http://www.openstreetmap.org/copyright&gt;OpenStreetMap&lt;/a&gt; contributors&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;version&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;0.7.11&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;weather_provider&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;status&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;success&quot;</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
@ -195,8 +196,9 @@
<span class="w"> </span><span class="nt">&quot;max_single_file_size&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1048576</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;max_users&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;max_zip_file_size&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">10485760</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;map_attribution&quot;</span><span class="p">:</span><span class="w"> </span><span class="nt">&quot;&amp;copy; &lt;a href=http://www.openstreetmap.org/copyright&gt;OpenStreetMap&lt;/a&gt; contributors&quot;</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;version&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;0.7.10&quot;</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;map_attribution&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;&amp;copy; &lt;a href=http://www.openstreetmap.org/copyright&gt;OpenStreetMap&lt;/a&gt; contributors&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;version&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;0.7.11&quot;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;weather_provider&quot;</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></span>
<span class="w"> </span><span class="nt">&quot;status&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;success&quot;</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
@ -279,7 +281,7 @@
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

View File

@ -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.19: https://docutils.sourceforge.io/" />
<title>API documentation &#8212; FitTrackee 0.7.10
<title>API documentation &#8212; FitTrackee 0.7.11
documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
@ -43,7 +43,7 @@
</button>
<a class="navbar-brand" href="../index.html">
FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -160,7 +160,7 @@
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

View File

@ -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.19: https://docutils.sourceforge.io/" />
<title>OAuth2 &#8212; FitTrackee 0.7.10
<title>OAuth2 &#8212; FitTrackee 0.7.11
documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
@ -43,7 +43,7 @@
</button>
<a class="navbar-brand" href="../index.html">
FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -694,7 +694,7 @@ are supported by FitTrackee)</p></li>
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

View File

@ -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.19: https://docutils.sourceforge.io/" />
<title>Records &#8212; FitTrackee 0.7.10
<title>Records &#8212; FitTrackee 0.7.11
documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
@ -43,7 +43,7 @@
</button>
<a class="navbar-brand" href="../index.html">
FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -262,7 +262,7 @@
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

View File

@ -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.19: https://docutils.sourceforge.io/" />
<title>Sports &#8212; FitTrackee 0.7.10
<title>Sports &#8212; FitTrackee 0.7.11
documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
@ -43,7 +43,7 @@
</button>
<a class="navbar-brand" href="../index.html">
FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -492,7 +492,7 @@
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

View File

@ -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.19: https://docutils.sourceforge.io/" />
<title>Statistics &#8212; FitTrackee 0.7.10
<title>Statistics &#8212; FitTrackee 0.7.11
documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
@ -43,7 +43,7 @@
</button>
<a class="navbar-brand" href="../index.html">
FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -419,7 +419,7 @@
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

View File

@ -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.19: https://docutils.sourceforge.io/" />
<title>Users &#8212; FitTrackee 0.7.10
<title>Users &#8212; FitTrackee 0.7.11
documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
@ -43,7 +43,7 @@
</button>
<a class="navbar-brand" href="../index.html">
FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -663,7 +663,7 @@ one admin.</p>
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

View File

@ -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.19: https://docutils.sourceforge.io/" />
<title>Workouts &#8212; FitTrackee 0.7.10
<title>Workouts &#8212; FitTrackee 0.7.11
documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
@ -43,7 +43,7 @@
</button>
<a class="navbar-brand" href="../index.html">
FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -1187,7 +1187,7 @@ must be provided with ascent)</p></li>
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

View File

@ -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.19: https://docutils.sourceforge.io/" />
<title>Third-party applications &#8212; FitTrackee 0.7.10
<title>Third-party applications &#8212; FitTrackee 0.7.11
documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
@ -43,7 +43,7 @@
</button>
<a class="navbar-brand" href="index.html">
FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -259,7 +259,7 @@ It is recommended to use PKCE to provide a better security.</p>
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

File diff suppressed because it is too large Load Diff

View File

@ -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.19: https://docutils.sourceforge.io/" />
<title>Command line interface &#8212; FitTrackee 0.7.10
<title>Command line interface &#8212; FitTrackee 0.7.11
documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
@ -43,7 +43,7 @@
</button>
<a class="navbar-brand" href="index.html">
FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -288,7 +288,7 @@ Commands:
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

View File

@ -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.19: https://docutils.sourceforge.io/" />
<title>Features &#8212; FitTrackee 0.7.10
<title>Features &#8212; FitTrackee 0.7.11
documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
@ -43,7 +43,7 @@
</button>
<a class="navbar-brand" href="index.html">
FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -206,7 +206,7 @@
</div>
</li>
<li><div class="line-block">
<div class="line">If DarkSky API key is provided, weather is displayed in workout detail.</div>
<div class="line">If <strong>DarkSky API</strong> or <strong>Visual Crossing</strong> (<em>new in 0.7.11</em>) API key is provided, weather is displayed in workout detail. Data source is displayed in About page.</div>
<div class="line">Wind is displayed, with arrow indicating direction (a tooltip can be displayed with the direction that the wind is coming <strong>from</strong>) (<em>new in 0.5.5</em>).</div>
</div>
</li>
@ -400,7 +400,7 @@ A user with an inactive account cannot log in. (<em>new in 0.6.0</em>)</p></li>
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

View File

@ -4,7 +4,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Index &#8212; FitTrackee 0.7.10
<title>Index &#8212; FitTrackee 0.7.11
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.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -126,7 +126,7 @@
environment variable
<ul>
<li><a href="installation.html#envvar-API_RATE_LIMITS">API_RATE_LIMITS 🆕</a>
<li><a href="installation.html#envvar-API_RATE_LIMITS">API_RATE_LIMITS</a>
</li>
<li><a href="installation.html#envvar-APP_LOG">APP_LOG</a>
</li>
@ -167,6 +167,8 @@
<li><a href="installation.html#envvar-VUE_APP_API_URL">VUE_APP_API_URL</a>
</li>
<li><a href="installation.html#envvar-WEATHER_API_KEY">WEATHER_API_KEY</a>
</li>
<li><a href="installation.html#envvar-WEATHER_API_PROVIDER">WEATHER_API_PROVIDER 🆕</a>
</li>
<li><a href="installation.html#envvar-WORKERS_PROCESSES">WORKERS_PROCESSES</a>
</li>
@ -188,7 +190,7 @@
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

View File

@ -4,7 +4,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>HTTP Routing Table &#8212; FitTrackee 0.7.10
<title>HTTP Routing Table &#8212; FitTrackee 0.7.11
documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
@ -47,7 +47,7 @@
</button>
<a class="navbar-brand" href="index.html">
FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -410,7 +410,7 @@
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

View File

@ -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.19: https://docutils.sourceforge.io/" />
<title>FitTrackee &#8212; FitTrackee 0.7.10
<title>FitTrackee &#8212; FitTrackee 0.7.11
documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
@ -42,7 +42,7 @@
</button>
<a class="navbar-brand" href="#">
FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -180,7 +180,7 @@ Map</a>.</div>
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

View File

@ -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.19: https://docutils.sourceforge.io/" />
<title>Installation &#8212; FitTrackee 0.7.10
<title>Installation &#8212; FitTrackee 0.7.11
documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
@ -43,7 +43,7 @@
</button>
<a class="navbar-brand" href="index.html">
FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -87,7 +87,8 @@
<li><a class="reference internal" href="#environment-variables">Environment variables</a><ul>
<li><a class="reference internal" href="#emails">Emails</a></li>
<li><a class="reference internal" href="#map-tile-server">Map tile server</a></li>
<li><a class="reference internal" href="#api-rate-limits">API rate limits 🆕</a></li>
<li><a class="reference internal" href="#api-rate-limits">API rate limits</a></li>
<li><a class="reference internal" href="#weather-data">Weather data</a></li>
</ul>
</li>
<li><a class="reference internal" href="#id2">Installation</a><ul>
@ -209,7 +210,7 @@
<dt>optional</dt><dd><ul>
<li><p>Redis for task queue (if email sending is enabled) and API rate limits</p></li>
<li><p>SMTP provider (if email sending is enabled)</p></li>
<li><p>API key from <a class="reference external" href="https://darksky.net/dev">Dark Sky</a> (deprecated, DarkSky will stop on March 31st, 2023)</p></li>
<li><p>API key from a <a class="reference external" href="installation.html#weather-data">weather data provider</a></p></li>
<li><p><a class="reference external" href="https://poetry.eustace.io">Poetry</a> (for installation from sources only)</p></li>
<li><p><a class="reference external" href="https://yarnpkg.com">Yarn</a> (for development only)</p></li>
<li><p>Docker and Docker Compose (for development or evaluation purposes)</p></li>
@ -428,7 +429,7 @@ see <a class="reference external" href="https://docs.sqlalchemy.org/en/13/core/p
<dl class="std envvar">
<dt class="sig sig-object std" id="envvar-API_RATE_LIMITS">
<span class="sig-name descname"><span class="pre">API_RATE_LIMITS</span> <span class="pre">🆕</span></span><a class="headerlink" href="#envvar-API_RATE_LIMITS" title="Permalink to this definition"></a></dt>
<span class="sig-name descname"><span class="pre">API_RATE_LIMITS</span></span><a class="headerlink" href="#envvar-API_RATE_LIMITS" title="Permalink to this definition"></a></dt>
<dd><div class="versionadded">
<p><span class="versionmodified added">New in version 0.7.0.</span></p>
</div>
@ -518,7 +519,16 @@ see <a class="reference external" href="https://docs.sqlalchemy.org/en/13/core/p
<dd><div class="versionchanged">
<p><span class="versionmodified changed">Changed in version 0.4.0: </span>⚠️ replaces <code class="docutils literal notranslate"><span class="pre">WEATHER_API</span></code></p>
</div>
<p><strong>Dark Sky</strong> API key for weather data (not mandatory).</p>
<p>Weather API key (not mandatory), see <code class="docutils literal notranslate"><span class="pre">WEATHER_API_PROVIDER</span></code>.</p>
</dd></dl>
<dl class="std envvar">
<dt class="sig sig-object std" id="envvar-WEATHER_API_PROVIDER">
<span class="sig-name descname"><span class="pre">WEATHER_API_PROVIDER</span> <span class="pre">🆕</span></span><a class="headerlink" href="#envvar-WEATHER_API_PROVIDER" title="Permalink to this definition"></a></dt>
<dd><div class="versionadded">
<p><span class="versionmodified added">New in version 0.7.11.</span></p>
</div>
<p>Provider for weather data (not mandatory), see <a class="reference external" href="installation.html#weather-data">Weather data</a>.</p>
</dd></dl>
<dl class="std envvar">
@ -604,7 +614,7 @@ The tile server can be changed by updating <code class="docutils literal notrans
<p>The subdomain will be chosen randomly.</p>
</section>
<section id="api-rate-limits">
<h3>API rate limits 🆕<a class="headerlink" href="#api-rate-limits" title="Permalink to this heading"></a></h3>
<h3>API rate limits<a class="headerlink" href="#api-rate-limits" title="Permalink to this heading"></a></h3>
<div class="versionadded">
<p><span class="versionmodified added">New in version 0.7.0.</span></p>
</div>
@ -642,6 +652,22 @@ Commands:
</pre></div>
</div>
</section>
<section id="weather-data">
<h3>Weather data<a class="headerlink" href="#weather-data" title="Permalink to this heading"></a></h3>
<div class="versionchanged">
<p><span class="versionmodified changed">Changed in version 0.7.11.</span></p>
</div>
<p>The following weather data providers are supported by <strong>FitTrackee</strong>:</p>
<ul class="simple">
<li><p><a class="reference external" href="https://darksky.net">Dark Sky</a> (deprecated, will stop on March 31st, 2023)</p></li>
<li><p><a class="reference external" href="https://www.visualcrossing.com">Visual Crossing</a> (<strong>note</strong>: historical data are provided on hourly period)</p></li>
</ul>
<p>To configure a weather provider, set the following environment variables:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">WEATHER_API_PROVIDER</span></code>: <code class="docutils literal notranslate"><span class="pre">darksky</span></code> for <strong>Dark Sky</strong> or <code class="docutils literal notranslate"><span class="pre">visualcrossing</span></code> for <strong>Visual Crossing</strong></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">WEATHER_API_KEY</span></code>: the key to the corresponding weather provider</p></li>
</ul>
</section>
</section>
<section id="id2">
<h2>Installation<a class="headerlink" href="#id2" title="Permalink to this heading"></a></h2>
@ -795,11 +821,11 @@ $ make install-db
</div>
</div>
<ul class="simple">
<li><p>Download the last release (for now, it is the release v0.7.10):</p></li>
<li><p>Download the last release (for now, it is the release v0.7.11):</p></li>
</ul>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ wget https://github.com/SamR1/FitTrackee/archive/v0.7.10.tar.gz
$ tar -xzf v0.7.10.tar.gz
$ mv FitTrackee-0.7.10 FitTrackee
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ wget https://github.com/SamR1/FitTrackee/archive/v0.7.11.tar.gz
$ tar -xzf v0.7.11.tar.gz
$ mv FitTrackee-0.7.11 FitTrackee
$ <span class="nb">cd</span> FitTrackee
</pre></div>
</div>
@ -919,11 +945,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.7.10) and overwrite existing files:</p></li>
<li><p>Download the last release (for now, it is the release v0.7.11) 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.7.10.tar.gz
$ tar -xzf v0.7.10.tar.gz
$ cp -R FitTrackee-0.7.10/* FitTrackee/
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>$ wget https://github.com/SamR1/FitTrackee/archive/v0.7.11.tar.gz
$ tar -xzf v0.7.11.tar.gz
$ cp -R FitTrackee-0.7.11/* FitTrackee/
$ <span class="nb">cd</span> FitTrackee
</pre></div>
</div>
@ -1182,7 +1208,7 @@ $ make docker-test-python <span class="c1"># run unit tests on API</span>
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

Binary file not shown.

View File

@ -4,7 +4,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Search &#8212; FitTrackee 0.7.10
<title>Search &#8212; FitTrackee 0.7.11
documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="_static/bootstrap-sphinx.css" />
@ -47,7 +47,7 @@
</button>
<a class="navbar-brand" href="index.html">
FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -154,7 +154,7 @@
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

File diff suppressed because one or more lines are too long

View File

@ -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.19: https://docutils.sourceforge.io/" />
<title>Administrator &#8212; FitTrackee 0.7.10
<title>Administrator &#8212; FitTrackee 0.7.11
documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
@ -43,7 +43,7 @@
</button>
<a class="navbar-brand" href="../index.html">
FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -174,7 +174,7 @@
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

View File

@ -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.19: https://docutils.sourceforge.io/" />
<title>Troubleshooting &#8212; FitTrackee 0.7.10
<title>Troubleshooting &#8212; FitTrackee 0.7.11
documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
@ -43,7 +43,7 @@
</button>
<a class="navbar-brand" href="../index.html">
FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -157,7 +157,7 @@
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

View File

@ -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.19: https://docutils.sourceforge.io/" />
<title>User &#8212; FitTrackee 0.7.10
<title>User &#8212; FitTrackee 0.7.11
documentation</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/bootstrap-sphinx.css" />
@ -43,7 +43,7 @@
</button>
<a class="navbar-brand" href="../index.html">
FitTrackee</a>
<span class="navbar-text navbar-version pull-left"><b>0.7.10
<span class="navbar-text navbar-version pull-left"><b>0.7.11
</b></span>
</div>
@ -148,7 +148,7 @@
</p>
<p>
&copy; Copyright 2018 - 2022, SamR1 <a rel="me" href="https://fosstodon.org/@FitTrackee"><i class="fa fa-mastodon" aria-hidden="true"></i></a>.
Last updated on Dec 21, 2022.<br/>
Last updated on Dec 31, 2022.<br/>
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 5.3.0.<br/>
</p>
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 567 KiB

After

Width:  |  Height:  |  Size: 567 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 378 KiB

After

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 83 KiB

View File

@ -43,7 +43,7 @@ Workouts
| Ascent and descent can also be provided (*new in 0.7.10*).
- | A workout with a gpx file can be displayed with map and charts (speed and elevation).
| Controls allow full screen view and position reset (*new in 0.5.5*).
- | If DarkSky API key is provided, weather is displayed in workout detail.
- | If **DarkSky API** or **Visual Crossing** (*new in 0.7.11*) API key is provided, weather is displayed in workout detail. Data source is displayed in About page.
| Wind is displayed, with arrow indicating direction (a tooltip can be displayed with the direction that the wind is coming **from**) (*new in 0.5.5*).
- Segments can be displayed.
- Workout gpx file can be downloaded (*new in 0.5.1*)

View File

@ -28,7 +28,7 @@ Prerequisites
- optional
- Redis for task queue (if email sending is enabled) and API rate limits
- SMTP provider (if email sending is enabled)
- API key from `Dark Sky <https://darksky.net/dev>`__ (deprecated, DarkSky will stop on March 31st, 2023)
- API key from a `weather data provider <installation.html#weather-data>`__
- `Poetry <https://poetry.eustace.io>`__ (for installation from sources only)
- `Yarn <https://yarnpkg.com>`__ (for development only)
- Docker and Docker Compose (for development or evaluation purposes)
@ -172,7 +172,7 @@ deployment method.
Number of processes used by **Dramatiq**.
.. envvar:: API_RATE_LIMITS 🆕
.. envvar:: API_RATE_LIMITS
.. versionadded:: 0.7.0
@ -229,7 +229,14 @@ deployment method.
.. versionchanged:: 0.4.0 ⚠️ replaces ``WEATHER_API``
**Dark Sky** API key for weather data (not mandatory).
Weather API key (not mandatory), see ``WEATHER_API_PROVIDER``.
.. envvar:: WEATHER_API_PROVIDER 🆕
.. versionadded:: 0.7.11
Provider for weather data (not mandatory), see `Weather data <installation.html#weather-data>`__.
.. envvar:: VUE_APP_API_URL
@ -302,8 +309,8 @@ For instance, to set OSM France tile server, the expected values are:
The subdomain will be chosen randomly.
API rate limits 🆕
^^^^^^^^^^^^^^^^^^
API rate limits
^^^^^^^^^^^^^^^
.. versionadded:: 0.7.0
| API rate limits are managed by `Flask-Limiter <https://flask-limiter.readthedocs.io/en/stable>`_, based on IP with fixed window strategy.
@ -338,6 +345,21 @@ API rate limits 🆕
limits Enumerate details about all routes with rate limits
Weather data
^^^^^^^^^^^^
.. versionchanged:: 0.7.11
The following weather data providers are supported by **FitTrackee**:
- `Dark Sky <https://darksky.net>`__ (deprecated, will stop on March 31st, 2023)
- `Visual Crossing <https://www.visualcrossing.com>`__ (**note**: historical data are provided on hourly period)
To configure a weather provider, set the following environment variables:
- ``WEATHER_API_PROVIDER``: ``darksky`` for **Dark Sky** or ``visualcrossing`` for **Visual Crossing**
- ``WEATHER_API_KEY``: the key to the corresponding weather provider
Installation
~~~~~~~~~~~~
@ -480,13 +502,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.7.10):
- Download the last release (for now, it is the release v0.7.11):
.. code:: bash
$ wget https://github.com/SamR1/FitTrackee/archive/v0.7.10.tar.gz
$ tar -xzf v0.7.10.tar.gz
$ mv FitTrackee-0.7.10 FitTrackee
$ wget https://github.com/SamR1/FitTrackee/archive/v0.7.11.tar.gz
$ tar -xzf v0.7.11.tar.gz
$ mv FitTrackee-0.7.11 FitTrackee
$ cd FitTrackee
- Create **.env** from example and update it
@ -606,13 +628,13 @@ Prod environment
- Change to the directory where FitTrackee directory is located
- Download the last release (for now, it is the release v0.7.10) and overwrite existing files:
- Download the last release (for now, it is the release v0.7.11) and overwrite existing files:
.. code:: bash
$ wget https://github.com/SamR1/FitTrackee/archive/v0.7.10.tar.gz
$ tar -xzf v0.7.10.tar.gz
$ cp -R FitTrackee-0.7.10/* FitTrackee/
$ wget https://github.com/SamR1/FitTrackee/archive/v0.7.11.tar.gz
$ tar -xzf v0.7.11.tar.gz
$ cp -R FitTrackee-0.7.11/* FitTrackee/
$ cd FitTrackee
- Update **.env** if needed (see `Environment variables <installation.html#environment-variables>`__).

View File

@ -25,7 +25,7 @@ from werkzeug.middleware.proxy_fix import ProxyFix
from fittrackee.emails.email import EmailService
from fittrackee.request import CustomRequest
VERSION = __version__ = '0.7.10'
VERSION = __version__ = '0.7.11'
REDIS_URL = os.getenv('REDIS_URL', 'redis://')
API_RATE_LIMITS = os.environ.get('API_RATE_LIMITS', '300 per 5 minutes').split(
','

View File

@ -47,8 +47,9 @@ def get_application_config() -> Union[Dict, HttpResponse]:
"max_single_file_size": 1048576,
"max_users": 0,
"max_zip_file_size": 10485760,
"map_attribution": "&copy; <a href=http://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors"
"version": "0.7.10"
"map_attribution": "&copy; <a href=http://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors",
"version": "0.7.11",
"weather_provider": null
},
"status": "success"
}
@ -99,8 +100,9 @@ def update_application_config(auth_user: User) -> Union[Dict, HttpResponse]:
"max_single_file_size": 1048576,
"max_users": 10,
"max_zip_file_size": 10485760,
"map_attribution": "&copy; <a href=http://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors"
"version": "0.7.10"
"map_attribution": "&copy; <a href=http://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors",
"version": "0.7.11",
"weather_provider": null
},
"status": "success"
}

View File

@ -1,3 +1,4 @@
import os
from typing import Dict
from flask import current_app
@ -43,6 +44,7 @@ class AppConfig(BaseModel):
return current_app.config['TILE_SERVER']['ATTRIBUTION']
def serialize(self) -> Dict:
weather_provider = os.getenv('WEATHER_API_PROVIDER', '').lower()
return {
'admin_contact': self.admin_contact,
'gpx_limit_import': self.gpx_limit_import,
@ -53,6 +55,11 @@ class AppConfig(BaseModel):
'max_users': self.max_users,
'map_attribution': self.map_attribution,
'version': VERSION,
'weather_provider': (
weather_provider
if weather_provider in ['darksky', 'visualcrossing']
else None
),
}

View File

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="/favicon.ico"><![endif]--><link rel="stylesheet" href="/static/css/fork-awesome.min.css"/><link rel="stylesheet" href="/static/css/leaflet.css"/><title>FitTrackee</title><script defer="defer" src="/static/js/chunk-vendors.a82f8875.js"></script><script defer="defer" src="/static/js/app.c4c614bd.js"></script><link href="/static/css/app.2461bfc8.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.5bcd2175.js"></script><script defer="defer" src="/static/js/app.02c11a8c.js"></script><link href="/static/css/app.92a77a8d.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png"><link rel="manifest" href="/manifest.json"><meta name="theme-color" content="#4DBA87"><meta name="apple-mobile-web-app-capable" content="no"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="apple-mobile-web-app-title" content="fittrackee_client"><link rel="apple-touch-icon" href="/img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#4DBA87"><meta name="msapplication-TileImage" content="/img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><noscript><strong>We're sorry but FitTrackee doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
"use strict";(self["webpackChunkfittrackee_client"]=self["webpackChunkfittrackee_client"]||[]).push([[193],{7885:function(e,s,t){t.r(s),t.d(s,{default:function(){return A}});var a=t(6252),r=t(2262),l=t(3577),o=(t(7658),t(9150)),n=t(436);const c={class:"chart-menu"},i={class:"chart-arrow"},u={class:"time-frames custom-checkboxes-group"},d={class:"time-frames-checkboxes custom-checkboxes"},p=["id","name","checked","onInput"],m={class:"chart-arrow"};var v=(0,a.aZ)({__name:"StatsMenu",emits:["arrowClick","timeFrameUpdate"],setup(e,{emit:s}){const t=(0,r.iH)("month"),o=["week","month","year"];function n(e){t.value=e,s("timeFrameUpdate",e)}return(e,r)=>((0,a.wg)(),(0,a.iD)("div",c,[(0,a._)("div",i,[(0,a._)("i",{class:"fa fa-chevron-left","aria-hidden":"true",onClick:r[0]||(r[0]=e=>s("arrowClick",!0))})]),(0,a._)("div",u,[(0,a._)("div",d,[((0,a.wg)(),(0,a.iD)(a.HY,null,(0,a.Ko)(o,(s=>(0,a._)("div",{class:"time-frame custom-checkbox",key:s},[(0,a._)("label",null,[(0,a._)("input",{type:"radio",id:s,name:s,checked:t.value===s,onInput:e=>n(s)},null,40,p),(0,a._)("span",null,(0,l.zw)(e.$t(`statistics.TIME_FRAMES.${s}`)),1)])]))),64))])]),(0,a._)("div",m,[(0,a._)("i",{class:"fa fa-chevron-right","aria-hidden":"true",onClick:r[1]||(r[1]=e=>s("arrowClick",!1))})])]))}}),k=t(3744);const _=(0,k.Z)(v,[["__scopeId","data-v-22d55de2"]]);var S=_,w=t(631);const f={class:"sports-menu"},h=["id","name","checked","onInput"],U={class:"sport-label"};var b=(0,a.aZ)({__name:"StatsSportsMenu",props:{userSports:null,selectedSportIds:{default:()=>[]}},emits:["selectedSportIdsUpdate"],setup(e,{emit:s}){const t=e,{t:n}=(0,o.QT)(),c=(0,a.f3)("sportColors"),{selectedSportIds:i}=(0,r.BK)(t),u=(0,a.Fl)((()=>(0,w.xH)(t.userSports,n)));function d(e){s("selectedSportIdsUpdate",e)}return(e,s)=>{const t=(0,a.up)("SportImage");return(0,a.wg)(),(0,a.iD)("div",f,[((0,a.wg)(!0),(0,a.iD)(a.HY,null,(0,a.Ko)((0,r.SU)(u),(e=>((0,a.wg)(),(0,a.iD)("label",{type:"checkbox",key:e.id,style:(0,l.j5)({color:e.color?e.color:(0,r.SU)(c)[e.label]})},[(0,a._)("input",{type:"checkbox",id:e.id,name:e.label,checked:(0,r.SU)(i).includes(e.id),onInput:s=>d(e.id)},null,40,h),(0,a.Wm)(t,{"sport-label":e.label,color:e.color},null,8,["sport-label","color"]),(0,a._)("span",U,(0,l.zw)(e.translatedLabel),1)],4)))),128))])}}});const I=b;var g=I,T=t(9318);const y={key:0,id:"user-statistics"};var C=(0,a.aZ)({__name:"index",props:{sports:null,user:null},setup(e){const s=e,{t:t}=(0,o.QT)(),{sports:l,user:c}=(0,r.BK)(s),i=(0,r.iH)("month"),u=(0,r.iH)(v(i.value)),d=(0,a.Fl)((()=>(0,w.xH)(s.sports,t))),p=(0,r.iH)(_(s.sports));function m(e){i.value=e,u.value=v(i.value)}function v(e){return(0,T.aZ)(new Date,e,s.user.weekm)}function k(e){u.value=(0,T.FN)(u.value,e,s.user.weekm)}function _(e){return e.map((e=>e.id))}function f(e){p.value.includes(e)?p.value=p.value.filter((s=>s!==e)):p.value.push(e)}return(0,a.YP)((()=>s.sports),(e=>{p.value=_(e)})),(e,s)=>(0,r.SU)(d)?((0,a.wg)(),(0,a.iD)("div",y,[(0,a.Wm)(S,{onTimeFrameUpdate:m,onArrowClick:k}),(0,a.Wm)(n.Z,{sports:(0,r.SU)(l),user:(0,r.SU)(c),chartParams:u.value,"displayed-sport-ids":p.value,fullStats:!0},null,8,["sports","user","chartParams","displayed-sport-ids"]),(0,a.Wm)(g,{"selected-sport-ids":p.value,"user-sports":(0,r.SU)(l),onSelectedSportIdsUpdate:f},null,8,["selected-sport-ids","user-sports"])])):(0,a.kq)("",!0)}});const F=(0,k.Z)(C,[["__scopeId","data-v-30799d13"]]);var Z=F,x=t(5630),D=t(5801),H=t(9917);const E={id:"statistics",class:"view"},R={key:0,class:"container"};var W=(0,a.aZ)({__name:"StatisticsView",setup(e){const s=(0,H.o)(),t=(0,a.Fl)((()=>s.getters[D.YN.GETTERS.AUTH_USER_PROFILE])),o=(0,a.Fl)((()=>s.getters[D.O8.GETTERS.SPORTS].filter((e=>t.value.sports_list.includes(e.id)))));return(e,s)=>{const n=(0,a.up)("Card");return(0,a.wg)(),(0,a.iD)("div",E,[(0,r.SU)(t).username?((0,a.wg)(),(0,a.iD)("div",R,[(0,a.Wm)(n,null,{title:(0,a.w5)((()=>[(0,a.Uk)((0,l.zw)(e.$t("statistics.STATISTICS")),1)])),content:(0,a.w5)((()=>[(0,a.Wm)(Z,{class:(0,l.C_)({"stats-disabled":0===(0,r.SU)(t).nb_workouts}),user:(0,r.SU)(t),sports:(0,r.SU)(o)},null,8,["class","user","sports"])])),_:1}),0===(0,r.SU)(t).nb_workouts?((0,a.wg)(),(0,a.j4)(x.Z,{key:0})):(0,a.kq)("",!0)])):(0,a.kq)("",!0)])}}});const P=(0,k.Z)(W,[["__scopeId","data-v-2e341d4e"]]);var A=P}}]);
//# sourceMappingURL=statistics.31ee4368.js.map
//# sourceMappingURL=statistics.99fc9524.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,4 @@
import pytest
from flask import Flask
from fittrackee import VERSION
@ -6,7 +7,10 @@ from fittrackee.users.models import User
class TestConfigModel:
def test_application_config(self, app: Flask) -> None:
def test_application_config(
self, app: Flask, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setenv('WEATHER_API_PROVIDER', 'darksky')
app_config = AppConfig.query.first()
app_config.admin_contact = 'admin@example.com'
@ -40,6 +44,7 @@ class TestConfigModel:
== app_config.map_attribution
)
assert serialized_app_config['version'] == VERSION
assert serialized_app_config['weather_provider'] == 'darksky'
def test_it_returns_registration_disabled_when_users_count_exceeds_limit(
self, app: Flask, user_1: User, user_2: User
@ -58,3 +63,28 @@ class TestConfigModel:
serialized_app_config = app_config.serialize()
assert serialized_app_config['is_email_sending_enabled'] is False
@pytest.mark.parametrize(
'input_weather_api_provider, expected_weather_provider',
[
('darksky', 'darksky'),
('Visualcrossing', 'visualcrossing'),
('invalid_provider', None),
('', None),
],
)
def test_it_returns_weather_provider(
self,
app: Flask,
input_weather_api_provider: str,
expected_weather_provider: str,
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv('WEATHER_API_PROVIDER', input_weather_api_provider)
app_config = AppConfig.query.first()
serialized_app_config = app_config.serialize()
assert (
serialized_app_config['weather_provider']
== expected_weather_provider
)

View File

@ -1,6 +1,7 @@
import os
import shutil
from typing import Generator, Optional, Union
from typing import Generator, Iterator, Optional, Union
from unittest.mock import patch
import pytest
from flask import current_app
@ -8,6 +9,13 @@ from flask import current_app
from fittrackee import create_app, db, limiter
from fittrackee.application.models import AppConfig
from fittrackee.application.utils import update_app_config_from_database
from fittrackee.workouts.utils.gpx import weather_service
@pytest.fixture(autouse=True)
def default_weather_service(monkeypatch: pytest.MonkeyPatch) -> Iterator[None]:
with patch.object(weather_service, 'get_weather', return_value=None):
yield
def get_app_config(
@ -82,7 +90,6 @@ def get_app(
@pytest.fixture
def app(monkeypatch: pytest.MonkeyPatch) -> Generator:
monkeypatch.setenv('EMAIL_URL', 'smtp://none:none@0.0.0.0:1025')
monkeypatch.setenv('WEATHER_API_KEY', '')
if os.getenv('TILE_SERVER_URL'):
monkeypatch.delenv('TILE_SERVER_URL')
if os.getenv('STATICMAP_SUBDOMAINS'):

View File

@ -0,0 +1,344 @@
from datetime import datetime
from typing import Dict, Optional
from unittest.mock import Mock, patch, sentinel
import pytest
import pytz
import requests
from gpxpy.gpx import GPXTrackPoint
from fittrackee.tests.mixins import CallArgsMixin
from fittrackee.tests.utils import random_string
from fittrackee.workouts.utils.weather.dark_sky import DarkSky
from fittrackee.workouts.utils.weather.visual_crossing import VisualCrossing
from fittrackee.workouts.utils.weather.weather_service import WeatherService
VISUAL_CROSSING_RESPONSE = {
"queryCost": 1,
"latitude": 48.866667,
"longitude": 2.333333,
"resolvedAddress": "48.866667,2.333333",
"address": "48.866667,2.333333",
"timezone": "Europe/Paris",
"tzoffset": 1.0,
"days": [
{
"datetime": "2022-11-15",
"datetimeEpoch": 1668466800,
"temp": 10.4,
"humidity": 93.3,
"windspeed": 18.9,
"winddir": 179.4,
"conditions": "Rain, Partially cloudy",
"description": "Partly cloudy throughout the day with rain.",
"icon": "rain",
}
],
"currentConditions": {
"datetime": "13:00:00",
"datetimeEpoch": 1668513600,
"temp": 11.3,
"humidity": 93.1,
"windspeed": 14.0,
"winddir": 161.9,
"conditions": "Rain, Overcast",
"icon": "rain",
},
}
class WeatherTestCase:
api_key = random_string()
@staticmethod
def get_gpx_point(time: Optional[datetime] = None) -> GPXTrackPoint:
return GPXTrackPoint(latitude=48.866667, longitude=2.333333, time=time)
class TestDarksky(WeatherTestCase):
def test_it_calls_forecast_io_with_datetime_in_utc_when_naive_datetime_is_provided( # noqa
self,
) -> None:
naive_point_time = datetime(
year=2022, month=6, day=11, hour=10, minute=23, second=00
)
point = self.get_gpx_point(naive_point_time)
darksky = DarkSky(api_key=self.api_key)
with patch(
'fittrackee.workouts.utils.weather.dark_sky.forecastio'
) as forecast_io_mock:
darksky.get_weather(point)
forecast_io_mock.load_forecast.assert_called_with(
self.api_key,
point.latitude,
point.longitude,
time=datetime(
year=2022,
month=6,
day=11,
hour=10,
minute=23,
second=00,
tzinfo=pytz.utc,
),
units='si',
)
def test_it_calls_forecast_io_with_provided_datetime_when_with_timezone(
self,
) -> None:
paris_point_time = datetime(
year=2022,
month=6,
day=11,
hour=10,
minute=23,
second=00,
).astimezone(pytz.timezone('Europe/Paris'))
point = self.get_gpx_point(paris_point_time)
darksky = DarkSky(api_key=self.api_key)
with patch(
'fittrackee.workouts.utils.weather.dark_sky.forecastio'
) as forecast_io_mock:
darksky.get_weather(point)
forecast_io_mock.load_forecast.assert_called_with(
self.api_key,
point.latitude,
point.longitude,
time=paris_point_time,
units='si',
)
def test_it_returns_forecast_currently_data(self) -> None:
darksky = DarkSky(api_key=self.api_key)
with patch(
'fittrackee.workouts.utils.weather.dark_sky.forecastio'
) as forecast_io_mock:
forecast_io_mock.load_forecast().currently.return_value = sentinel
weather_data = darksky.get_weather(
self.get_gpx_point(datetime.utcnow())
)
assert weather_data == {
'icon': sentinel.icon,
'temperature': sentinel.temperature,
'humidity': sentinel.humidity,
'wind': sentinel.windSpeed,
'windBearing': sentinel.windBearing,
}
class TestVisualCrossingGetTimestamp(WeatherTestCase):
def test_it_returns_expected_timestamp_as_integer(self) -> None:
time = datetime.utcnow()
visual_crossing = VisualCrossing(api_key=self.api_key)
timestamp = visual_crossing._get_timestamp(time)
assert isinstance(timestamp, int)
@pytest.mark.parametrize(
'input_datetime,expected_datetime',
[
('2020-12-15T13:00:00', '2020-12-15T13:00:00'),
('2020-12-15T13:29:59', '2020-12-15T13:00:00'),
('2020-12-15T13:30:00', '2020-12-15T14:00:00'),
('2020-12-15T13:59:59', '2020-12-15T14:00:00'),
],
)
def test_it_returns_rounded_time(
self, input_datetime: str, expected_datetime: str
) -> None:
time = datetime.strptime(input_datetime, '%Y-%m-%dT%H:%M:%S')
visual_crossing = VisualCrossing(api_key=self.api_key)
timestamp = visual_crossing._get_timestamp(time)
assert (
timestamp
== datetime.strptime(
expected_datetime, '%Y-%m-%dT%H:%M:%S'
).timestamp()
)
class TestVisualCrossingGetWeather(WeatherTestCase, CallArgsMixin):
@staticmethod
def get_response() -> Mock:
response_mock = Mock()
response_mock.raise_for_status = Mock()
response_mock.json = Mock()
response_mock.json.return_value = VISUAL_CROSSING_RESPONSE
return response_mock
def test_it_calls_api_with_time_and_point_location(self) -> None:
time = datetime(
year=2022,
month=11,
day=15,
hour=12,
minute=00,
second=00,
tzinfo=pytz.utc,
)
point = self.get_gpx_point(time)
visual_crossing = VisualCrossing(api_key=self.api_key)
with patch.object(requests, 'get') as get_mock:
visual_crossing.get_weather(point)
args = self.get_args(get_mock.call_args)
assert args[0] == (
'https://weather.visualcrossing.com/VisualCrossingWebServices/'
f'rest/services/timeline/{point.latitude},{point.longitude}/'
f'{int(point.time.timestamp())}' # type: ignore
)
def test_it_calls_api_with_expected_params(self) -> None:
visual_crossing = VisualCrossing(api_key=self.api_key)
with patch.object(requests, 'get') as get_mock:
visual_crossing.get_weather(self.get_gpx_point(datetime.utcnow()))
kwargs = self.get_kwargs(get_mock.call_args)
assert kwargs.get('params') == {
'key': self.api_key,
'iconSet': 'icons1',
'unitGroup': 'metric',
'contentType': 'json',
'elements': (
'datetime,datetimeEpoch,temp,humidity,windspeed,'
'winddir,conditions,description,icon'
),
'include': 'current',
}
def test_it_returns_data_from_current_conditions(self) -> None:
point = self.get_gpx_point(
datetime(
year=2022,
month=11,
day=15,
hour=13,
minute=00,
second=00,
tzinfo=pytz.utc,
).astimezone(pytz.timezone('Europe/Paris'))
)
visual_crossing = VisualCrossing(api_key=self.api_key)
with patch.object(requests, 'get', return_value=self.get_response()):
weather_data = visual_crossing.get_weather(point)
current_conditions: Dict = VISUAL_CROSSING_RESPONSE[ # type: ignore
'currentConditions'
]
assert weather_data == {
'icon': current_conditions['icon'],
'temperature': current_conditions['temp'],
'humidity': current_conditions['humidity'] / 100,
'wind': (current_conditions['windspeed'] * 1000) / 3600,
'windBearing': current_conditions['winddir'],
}
class TestWeatherService(WeatherTestCase):
@pytest.mark.parametrize(
'input_api_key,input_provider',
[
('', 'darksky'),
('valid_api_key', ''),
('valid_api_key', 'invalid_provider'),
],
)
def test_weather_api_is_none_when_configuration_is_invalid(
self,
monkeypatch: pytest.MonkeyPatch,
input_api_key: str,
input_provider: str,
) -> None:
monkeypatch.setenv('WEATHER_API_KEY', input_api_key)
monkeypatch.setenv('WEATHER_API_PROVIDER', input_provider)
weather_service = WeatherService()
assert weather_service.weather_api is None
@pytest.mark.parametrize(
'input_provider',
['darksky', 'DARKSKY'],
)
def test_weather_api_is_darksky_when_configured(
self,
monkeypatch: pytest.MonkeyPatch,
input_provider: str,
) -> None:
monkeypatch.setenv('WEATHER_API_KEY', 'valid_api_key')
monkeypatch.setenv('WEATHER_API_PROVIDER', input_provider)
weather_service = WeatherService()
assert isinstance(weather_service.weather_api, DarkSky)
@pytest.mark.parametrize(
'input_provider',
['visualcrossing', 'VisualCrossing'],
)
def test_weather_api_is_visualcrossing_when_configured(
self,
monkeypatch: pytest.MonkeyPatch,
input_provider: str,
) -> None:
monkeypatch.setenv('WEATHER_API_KEY', 'valid_api_key')
monkeypatch.setenv('WEATHER_API_PROVIDER', input_provider)
weather_service = WeatherService()
assert isinstance(weather_service.weather_api, VisualCrossing)
def test_it_returns_none_when_no_weather_api(self) -> None:
weather_service = WeatherService()
weather_service.weather_api = None
point = self.get_gpx_point(datetime.utcnow())
weather_data = weather_service.get_weather(point)
assert weather_data is None
def test_it_returns_none_when_point_time_is_none(self) -> None:
weather_service = WeatherService()
weather_service.weather_api = DarkSky('api_key')
point = self.get_gpx_point(None)
weather_data = weather_service.get_weather(point)
assert weather_data is None
def test_it_returns_none_when_weather_api_raises_exception(self) -> None:
weather_api = Mock()
weather_api.get_weather = Mock()
weather_api.get_weather.side_effect = Exception()
weather_service = WeatherService()
weather_service.weather_api = weather_api
point = self.get_gpx_point(datetime.utcnow())
weather_data = weather_service.get_weather(point)
assert weather_data is None
def test_it_returns_weather_data(self) -> None:
weather_api = Mock()
weather_api.get_weather = Mock()
weather_api.get_weather.return_value = sentinel
weather_service = WeatherService()
weather_service.weather_api = weather_api
point = self.get_gpx_point(datetime.utcnow())
weather_data = weather_service.get_weather(point)
assert weather_data == sentinel

View File

@ -4,7 +4,9 @@ from typing import Any, Dict, List, Optional, Tuple, Union
import gpxpy.gpx
from ..exceptions import WorkoutGPXException
from .weather import get_weather
from .weather import WeatherService
weather_service = WeatherService()
def open_gpx_file(gpx_file: str) -> Optional[gpxpy.gpx.GPX]:
@ -99,7 +101,7 @@ def get_gpx_info(
if start is None:
start = point.time
if point.time and update_weather_data:
weather_data.append(get_weather(point))
weather_data.append(weather_service.get_weather(point))
# if a previous segment exists, calculate stopped time between
# the two segments
@ -114,7 +116,7 @@ def get_gpx_info(
# last gpx point => get weather
if segment_idx == (segments_nb - 1) and update_weather_data:
weather_data.append(get_weather(point))
weather_data.append(weather_service.get_weather(point))
if update_map_data:
map_data.append([point.longitude, point.latitude])

View File

@ -1,40 +0,0 @@
import os
from typing import Dict, Optional
import forecastio
import pytz
from gpxpy.gpx import GPXTrackPoint
from fittrackee import appLog
API_KEY = os.getenv('WEATHER_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)
if point.time.tzinfo is None
else point.time.astimezone(pytz.utc)
)
forecast = forecastio.load_forecast(
API_KEY,
point.latitude,
point.longitude,
time=point_time,
units='si',
)
weather = forecast.currently()
return {
'summary': weather.summary,
'icon': weather.icon,
'temperature': weather.temperature,
'humidity': weather.humidity,
'wind': weather.windSpeed,
'windBearing': weather.windBearing,
}
except Exception as e:
appLog.error(e)
return None

View File

@ -0,0 +1,5 @@
from .weather_service import WeatherService
__all__ = [
'WeatherService',
]

View File

@ -0,0 +1,50 @@
from abc import ABC, abstractmethod
from datetime import datetime
from typing import Dict, Optional
from gpxpy.gpx import GPXTrackPoint
class BaseWeather(ABC):
def __init__(self, api_key: str) -> None:
self.api_key: str = api_key
@abstractmethod
def _get_data(
self, latitude: float, longitude: float, time: datetime
) -> Optional[Dict]:
# Expected dict:
# {
# "humidity": 0.69,
# "icon": "partly-cloudy-day",
# "temperature": 12.26,
# "wind": 3.49,
# "windBearing": 315
# }
#
# FitTrackee expects the following units:
# temperature: Celsius,
# humidity: in fraction (rather than percent)
# windSpeed: m/s
# windBearing: direction wind is from in degrees (0 is north)
#
# Expected icon values (for UI):
# - "clear-day",
# - "clear-night",
# - "cloudy",
# - "fog",
# - "partly-cloudy-day",
# - "partly-cloudy-night",
# - "rain",
# - "sleet",
# - "snow",
# - "wind"
pass
def get_weather(self, point: GPXTrackPoint) -> Optional[Dict]:
if not point.time:
# if there's no time associated with the point,
# we cannot get weather
return None
return self._get_data(point.latitude, point.longitude, point.time)

View File

@ -0,0 +1,37 @@
from datetime import datetime
from typing import Dict, Optional
import forecastio
import pytz
from .base_weather import BaseWeather
class DarkSky(BaseWeather):
# Deprecated (API will end on March 31st, 2023)
def _get_data(
self, latitude: float, longitude: float, time: datetime
) -> Optional[Dict]:
# get point time in UTC
point_time = (
pytz.utc.localize(time)
if time.tzinfo is None # naive datetime
else time
)
forecast = forecastio.load_forecast(
self.api_key,
latitude,
longitude,
time=point_time,
units='si',
)
weather = forecast.currently()
return {
'humidity': weather.humidity,
'icon': weather.icon,
'temperature': weather.temperature,
'wind': weather.windSpeed,
'windBearing': weather.windBearing,
}

View File

@ -0,0 +1,84 @@
from datetime import datetime, timedelta
from typing import Dict, Optional
import requests
from fittrackee import appLog
from .base_weather import BaseWeather
class VisualCrossing(BaseWeather):
def __init__(self, api_key: str):
super().__init__(api_key)
self.base_url = (
'https://weather.visualcrossing.com/'
'VisualCrossingWebServices/rest/services'
)
self.params = {
"key": self.api_key,
"iconSet": "icons1", # default value, same as Darksky
"unitGroup": "metric",
"contentType": "json",
"elements": (
"datetime,datetimeEpoch,temp,humidity,windspeed,"
"winddir,conditions,description,icon"
),
"include": "current", # to get only specific time data
}
@staticmethod
def _get_timestamp(time: datetime) -> int:
# The results are returned in the currentConditions field and are
# truncated to the hour requested (i.e. 2020-10-19T13:59:00 will return
# data at 2020-10-19T13:00:00).
# first, round datetime to nearest hour by truncating, and then adding
# an hour if the "real" time's number of minutes is 30 or more (we do
# this since the API only truncates)
trunc_time = time.replace(
second=0, microsecond=0, minute=0, hour=time.hour
) + timedelta(hours=time.minute // 30)
appLog.debug(
f'VC_weather: truncated time {time} ({time.timestamp()})'
f' to {trunc_time} ({trunc_time.timestamp()})'
)
return int(trunc_time.timestamp())
def _get_data(
self, latitude: float, longitude: float, time: datetime
) -> Optional[Dict]:
# All requests to the Timeline Weather API use the following the form:
# https://weather.visualcrossing.com/VisualCrossingWebServices/rest
# /services/timeline/[location]/[date1]/[date2]?key=YOUR_API_KEY
# location (required) is the address, partial address or
# latitude,longitude location for
# which to retrieve weather data. You can also use US ZIP Codes.
# date1 (optional) is the start date for which to retrieve weather
# data. All dates and times are in local time of the **location**
# specified.
url = (
f"{self.base_url}/timeline/{latitude},{longitude}"
f"/{self._get_timestamp(time)}"
)
appLog.debug(
f'VC_weather: getting weather from {url}'.replace(
self.api_key, '*****'
)
)
r = requests.get(url, params=self.params)
r.raise_for_status()
res = r.json()
weather = res['currentConditions']
data = {
'icon': weather['icon'],
'temperature': weather['temp'],
'humidity': weather['humidity'] / 100,
'wind': weather['windspeed'] * 1000 / (60 * 60), # km/h to m/s
'windBearing': weather['winddir'],
}
return data

View File

@ -0,0 +1,44 @@
import os
from typing import Dict, Optional, Union
from gpxpy.gpx import GPXTrackPoint
from fittrackee import appLog
from .dark_sky import DarkSky
from .visual_crossing import VisualCrossing
class WeatherService:
"""
Available API:
- DarkSky (deprecated, will end on March 31st, 2023)
- VisualCrossing
"""
def __init__(self) -> None:
self.weather_api = self._get_weather_api()
@staticmethod
def _get_weather_api() -> Union[DarkSky, VisualCrossing, None]:
weather_api_key: str = os.getenv('WEATHER_API_KEY', '')
weather_api_provider: str = os.getenv(
'WEATHER_API_PROVIDER', ''
).lower()
if not weather_api_key:
return None
if weather_api_provider == 'darksky': # deprecated
return DarkSky(weather_api_key)
if weather_api_provider == 'visualcrossing':
return VisualCrossing(weather_api_key)
return None
def get_weather(self, point: GPXTrackPoint) -> Optional[Dict]:
if not self.weather_api:
return None
try:
return self.weather_api.get_weather(point)
except Exception as e:
appLog.error(e)
return None

View File

@ -1,6 +1,6 @@
{
"name": "fittrackee_client",
"version": "0.7.10",
"version": "0.7.11",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
@ -11,7 +11,7 @@
"i18n:report": "vue-cli-service i18n:report --src \"./src/**/*.?(js|vue)\" --locales \"./src/locales/**/*.json\""
},
"dependencies": {
"@tmcw/togeojson": "^5.5.0",
"@tmcw/togeojson": "^5.6.0",
"@vue-leaflet/vue-leaflet": "^0.7.0",
"@zxcvbn-ts/core": "^2.0.5",
"@zxcvbn-ts/language-common": "^2.0.1",
@ -22,14 +22,14 @@
"axios": "^1.2.1",
"chart.js": "^4.1.1",
"chartjs-plugin-datalabels": "^2.2.0",
"core-js": "^3.26.1",
"core-js": "^3.27.1",
"date-fns": "^2.29.3",
"date-fns-tz": "^1.3.7",
"leaflet": "^1.9.3",
"linkify-html": "^4.0.2",
"linkifyjs": "^4.0.2",
"register-service-worker": "^1.7.1",
"sanitize-html": "^2.8.0",
"sanitize-html": "^2.8.1",
"vue": "^3.2.45",
"vue-chart-3": "3.1.1",
"vue-fullscreen": "^3.1.1",
@ -42,8 +42,8 @@
"@types/chai": "^4.3.4",
"@types/mocha": "^10.0.1",
"@types/sanitize-html": "^2.8.0",
"@typescript-eslint/eslint-plugin": "^5.47.0",
"@typescript-eslint/parser": "^5.47.0",
"@typescript-eslint/eslint-plugin": "^5.47.1",
"@typescript-eslint/parser": "^5.47.1",
"@vue/cli-plugin-babel": "~5.0.8",
"@vue/cli-plugin-eslint": "~5.0.8",
"@vue/cli-plugin-pwa": "~5.0.8",
@ -55,7 +55,7 @@
"@vue/eslint-config-typescript": "^11.0.2",
"@vue/test-utils": "^2.2.6",
"chai": "^4.3.7",
"eslint": "8.30.0",
"eslint": "8.31.0",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-import": "^2.26.0",

View File

@ -40,6 +40,12 @@
{{ $t('about.CONTACT_ADMIN') }}
</a>
</div>
<div v-if="weather_provider && weather_provider.name">
{{ $t('about.WEATHER_DATA_FROM') }}
<a :href="weather_provider.url" target="_blank" rel="nofollow noopener">
{{ weather_provider.name }}
</a>
</div>
</div>
</div>
</template>
@ -55,6 +61,22 @@
const appConfig: ComputedRef<TAppConfig> = computed(
() => store.getters[ROOT_STORE.GETTERS.APP_CONFIG]
)
const weather_provider: ComputedRef<Record<string, string>> = computed(() =>
get_weather_provider()
)
function get_weather_provider() {
const weather_provider: Record<string, string> = {}
if (appConfig.value.weather_provider === 'darksky') {
weather_provider['name'] = 'Dark Sky'
weather_provider['url'] = 'https://darksky.net'
}
if (appConfig.value.weather_provider === 'visualcrossing') {
weather_provider['name'] = 'Visual Crossing'
weather_provider['url'] = 'https://www.visualcrossing.com'
}
return weather_provider
}
</script>
<style lang="scss" scoped>

View File

@ -1,13 +1,6 @@
<template>
<div class="wind">
<Distance
:distance="weather.wind"
unitFrom="m"
:digits="1"
:displayUnit="false"
:useImperialUnits="useImperialUnits"
/>
{{ useImperialUnits ? 'ft' : 'm' }}/s
{{ getWindSpeed(weather.wind, useImperialUnits) }}
<div class="wind-bearing">
<i
v-if="weather.windBearing"
@ -27,6 +20,7 @@
import { useI18n } from 'vue-i18n'
import { IWeather } from '@/types/workouts'
import { getWindSpeed } from '@/utils/units'
import { convertDegreeToDirection } from '@/utils/weather'
interface Props {

View File

@ -58,10 +58,20 @@
/>
</td>
<td>
{{ Number(workoutObject.weatherStart.temperature).toFixed(1) }}°C
{{
getTemperature(
workoutObject.weatherStart.temperature,
useImperialUnits
)
}}
</td>
<td>
{{ Number(workoutObject.weatherEnd.temperature).toFixed(1) }}°C
{{
getTemperature(
workoutObject.weatherEnd.temperature,
useImperialUnits
)
}}
</td>
</tr>
<tr>
@ -112,6 +122,7 @@
import WeatherWind from '@/components/Workout/WorkoutDetail/WeatherWind.vue'
import { IWorkoutObject } from '@/types/workouts'
import { getTemperature } from '@/utils/units'
interface Props {
workoutObject: IWorkoutObject

View File

@ -94,9 +94,9 @@
},
"TIMEZONE": "Zeitzone",
"UNITS": {
"IMPERIAL": "Imperiales System (ft, mi)",
"IMPERIAL": "Imperiales System (ft, mi, mph, °F)",
"LABEL": "Einheiten für die Distanz",
"METRIC": "Metrisches System (m, km)"
"METRIC": "Metrisches System (m, km, m/s, °C)"
}
},
"REGISTER": "Registrieren",

View File

@ -16,6 +16,7 @@
"FROM": "Von",
"GPX_FILE": ".gpx Datei",
"HIDE_FILTERS": "verberge Filter",
"INVALID_ASCENT_OR_DESCENT": "Beide Höhenwerte müssen angegeben werden und größer oder gleich 0 sein.",
"INVALID_DISTANCE": "Die Distanz muss größer als 0 sein",
"INVALID_DURATION": "Die Dauer muss größer als 0 Sekunden sein",
"LATEST_WORKOUTS": "Letzte Trainings",

View File

@ -2,5 +2,6 @@
"CONTACT_ADMIN": "Contact the administrator",
"FITTRACKEE_DESCRIPTION": "<strong>FitTrackee</strong> is a self-hosted outdoor activity tracker.",
"FITTRACKEE_LICENSE": "under {0} license ",
"SOURCE_CODE": "Source code"
"SOURCE_CODE": "Source code",
"WEATHER_DATA_FROM": "Weather data from:"
}

View File

@ -94,9 +94,9 @@
},
"TIMEZONE": "Timezone",
"UNITS": {
"IMPERIAL": "Imperial system (ft, mi)",
"IMPERIAL": "Imperial system (ft, mi, mph, °F)",
"LABEL": "Units for distance",
"METRIC": "Metric system (m, km)"
"METRIC": "Metric system (m, km, m/s, °C)"
}
},
"REGISTER": "Register",

View File

@ -2,5 +2,6 @@
"CONTACT_ADMIN": "Contacter l'administrateur",
"FITTRACKEE_DESCRIPTION": "<strong>FitTrackee</strong> est un <em>tracker</em> d'activités sportives (en extérieur).",
"FITTRACKEE_LICENSE": "sous licence {0} (en) ",
"SOURCE_CODE": "Code source (en)"
"SOURCE_CODE": "Code source (en)",
"WEATHER_DATA_FROM": "Source des données météo :"
}

View File

@ -94,9 +94,9 @@
},
"TIMEZONE": "Fuseau horaire",
"UNITS": {
"IMPERIAL": "Système impérial (ft, mi)",
"IMPERIAL": "Système impérial (ft, mi, mph, °F)",
"LABEL": "Unités pour les distances",
"METRIC": "Système métrique (m, km)"
"METRIC": "Système métrique (m, km, m/s, °C)"
}
},
"REGISTER": "S'inscrire",

View File

@ -94,9 +94,9 @@
},
"TIMEZONE": "Timezone",
"UNITS": {
"IMPERIAL": "Sistema imperiale (ft, mi)",
"IMPERIAL": "Sistema imperiale (ft, mi, mph, °F)",
"LABEL": "Unità per la distanza",
"METRIC": "Sistema metrico (m, km)"
"METRIC": "Sistema metrico (m, km, m/s, °C)"
}
},
"REGISTER": "Registra",

View File

@ -13,6 +13,6 @@
"NO": "Nee",
"REGISTER": "Registreer",
"RESET": "Reset",
"SUBMIT": "Aanpassen",
"SUBMIT": "Uploaden",
"YES": "Ja"
}

View File

@ -94,9 +94,9 @@
},
"TIMEZONE": "Tijdzone",
"UNITS": {
"IMPERIAL": "Imperialistisch systeem (ft, mi)",
"IMPERIAL": "Imperialistisch systeem (ft, mi, mph, °F)",
"LABEL": "Eenheid voor afstand",
"METRIC": "Metrisch systeem (m, km)"
"METRIC": "Metrisch systeem (m, km, m/s, °C)"
}
},
"REGISTER": "Registreren",

View File

@ -16,6 +16,7 @@
"FROM": "van",
"GPX_FILE": ".gpx bestand",
"HIDE_FILTERS": "verberg filters",
"INVALID_ASCENT_OR_DESCENT": "Beide waarden moeten opgegeven worden en dienen groter dan of gelijk aan 0 te zijn.",
"INVALID_DISTANCE": "De afstand moet groter zijn dan 0",
"INVALID_DURATION": "De duur moet langer zijn dan 0 seconden",
"LATEST_WORKOUTS": "Laatste trainingen",

View File

@ -6,7 +6,7 @@ export interface IAppStatistics {
}
export type TAppConfig = {
[key: string]: number | boolean | string
[key: string]: number | boolean | string | null
admin_contact: string
gpx_limit_import: number
is_email_sending_enabled: boolean
@ -16,6 +16,7 @@ export type TAppConfig = {
max_users: number
max_zip_file_size: number
version: string
weather_provider: string | null
}
export interface IApplication {

View File

@ -63,3 +63,23 @@ export const convertStatsDistance = (
const unitTo = useImperialUnits ? units[unitFrom].defaultTarget : unitFrom
return useImperialUnits ? convertDistance(value, unitFrom, unitTo, 2) : value
}
export const getTemperature = (
temperatureInCelsius: number,
useImperialUnits: boolean
): string => {
const temperature = useImperialUnits
? temperatureInCelsius * 1.8 + 32
: temperatureInCelsius
const unit = useImperialUnits ? ' °F' : '°C'
return `${temperature === 0 ? 0 : Number(temperature).toFixed(1)}${unit}`
}
export const getWindSpeed = (
windSpeedInMS: number,
useImperialUnits: boolean
): string => {
const windSpeed = useImperialUnits ? windSpeedInMS * 2.2369363 : windSpeedInMS
const unit = useImperialUnits ? ' mph' : 'm/s'
return `${windSpeed === 0 ? 0 : Number(windSpeed).toFixed(1)}${unit}`
}

View File

@ -1,7 +1,7 @@
import { assert } from 'chai'
import { TUnit } from '@/types/units'
import { convertDistance } from '@/utils/units'
import { convertDistance, getTemperature, getWindSpeed } from '@/utils/units'
describe('convertDistance', () => {
const testsParams: [number, TUnit, TUnit, number][] = [
@ -56,3 +56,35 @@ describe('convertDistance w/ digits', () => {
})
})
})
describe('getTemperature', () => {
const testsParams: [number, boolean, string][] = [
[0, false, '0°C'],
[10.0, false, '10.0°C'],
[10.3, false, '10.3°C'],
[0, true, '32.0 °F'],
[13.0, true, '55.4 °F'],
]
testsParams.map((testParams) => {
it(`get temperature for input: ${testParams[0]} and imperialUnits: ${testParams[1]}`, () => {
assert.equal(getTemperature(testParams[0], testParams[1]), testParams[2])
})
})
})
describe('getWindSpeed', () => {
const testsParams: [number, boolean, string][] = [
[0, false, '0m/s'],
[6.0, false, '6.0m/s'],
[6.3, false, '6.3m/s'],
[0, true, '0 mph'],
[13.2, true, '29.5 mph'],
]
testsParams.map((testParams) => {
it(`get wind speed for input: ${testParams[0]} and imperialUnits: ${testParams[1]}`, () => {
assert.equal(getWindSpeed(testParams[0], testParams[1]), testParams[2])
})
})
})

View File

@ -963,10 +963,10 @@
"@babel/helper-validator-identifier" "^7.19.1"
to-fast-properties "^2.0.0"
"@eslint/eslintrc@^1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.0.tgz#8ec64e0df3e7a1971ee1ff5158da87389f167a63"
integrity sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A==
"@eslint/eslintrc@^1.4.1":
version "1.4.1"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.1.tgz#af58772019a2d271b7e2d4c23ff4ddcba3ccfb3e"
integrity sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==
dependencies:
ajv "^6.12.4"
debug "^4.3.2"
@ -1251,10 +1251,10 @@
magic-string "^0.25.0"
string.prototype.matchall "^4.0.6"
"@tmcw/togeojson@^5.5.0":
version "5.5.0"
resolved "https://registry.yarnpkg.com/@tmcw/togeojson/-/togeojson-5.5.0.tgz#874081d1b3df1e51a6c5e878dd8f4cdf06a3ac48"
integrity sha512-K/neYsnwGkC1sjI0UInSyuxybPsBPXZF+g18l7XtzW37Jfzzcw4YjMd/rnqP5QRONtjuvGN6nGeCemKfeWkPGg==
"@tmcw/togeojson@^5.6.0":
version "5.6.0"
resolved "https://registry.yarnpkg.com/@tmcw/togeojson/-/togeojson-5.6.0.tgz#a6c96971acdf36eef73a1c398cf19a565eb02831"
integrity sha512-p+c0kbSY/tIwd3C0rpmTf3iFAOUYV6bkJbDiq0oQDGROOc7c6uh/X8r/mijY6SIFYIkVuKc8MS2REKhur2e6Jw==
"@tootallnate/once@2":
version "2.0.0"
@ -1489,7 +1489,7 @@
dependencies:
"@types/node" "*"
"@typescript-eslint/eslint-plugin@^5.0.0", "@typescript-eslint/eslint-plugin@^5.47.0":
"@typescript-eslint/eslint-plugin@^5.0.0":
version "5.47.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.47.0.tgz#dadb79df3b0499699b155839fd6792f16897d910"
integrity sha512-AHZtlXAMGkDmyLuLZsRpH3p4G/1iARIwc/T0vIem2YB+xW6pZaXYXzCBnZSF/5fdM97R9QqZWZ+h3iW10XgevQ==
@ -1504,7 +1504,22 @@
semver "^7.3.7"
tsutils "^3.21.0"
"@typescript-eslint/parser@^5.0.0", "@typescript-eslint/parser@^5.47.0":
"@typescript-eslint/eslint-plugin@^5.47.1":
version "5.47.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.47.1.tgz#50cc5085578a7fa22cd46a0806c2e5eae858af02"
integrity sha512-r4RZ2Jl9kcQN7K/dcOT+J7NAimbiis4sSM9spvWimsBvDegMhKLA5vri2jG19PmIPbDjPeWzfUPQ2hjEzA4Nmg==
dependencies:
"@typescript-eslint/scope-manager" "5.47.1"
"@typescript-eslint/type-utils" "5.47.1"
"@typescript-eslint/utils" "5.47.1"
debug "^4.3.4"
ignore "^5.2.0"
natural-compare-lite "^1.4.0"
regexpp "^3.2.0"
semver "^7.3.7"
tsutils "^3.21.0"
"@typescript-eslint/parser@^5.0.0":
version "5.47.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.47.0.tgz#62e83de93499bf4b500528f74bf2e0554e3a6c8d"
integrity sha512-udPU4ckK+R1JWCGdQC4Qa27NtBg7w020ffHqGyAK8pAgOVuNw7YaKXGChk+udh+iiGIJf6/E/0xhVXyPAbsczw==
@ -1514,6 +1529,16 @@
"@typescript-eslint/typescript-estree" "5.47.0"
debug "^4.3.4"
"@typescript-eslint/parser@^5.47.1":
version "5.47.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.47.1.tgz#c4bf16f8c3c7608ce4bf8ff804b677fc899f173f"
integrity sha512-9Vb+KIv29r6GPu4EboWOnQM7T+UjpjXvjCPhNORlgm40a9Ia9bvaPJswvtae1gip2QEeVeGh6YquqAzEgoRAlw==
dependencies:
"@typescript-eslint/scope-manager" "5.47.1"
"@typescript-eslint/types" "5.47.1"
"@typescript-eslint/typescript-estree" "5.47.1"
debug "^4.3.4"
"@typescript-eslint/scope-manager@5.47.0":
version "5.47.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.47.0.tgz#f58144a6b0ff58b996f92172c488813aee9b09df"
@ -1522,6 +1547,14 @@
"@typescript-eslint/types" "5.47.0"
"@typescript-eslint/visitor-keys" "5.47.0"
"@typescript-eslint/scope-manager@5.47.1":
version "5.47.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.47.1.tgz#0d302b3c2f20ab24e4787bf3f5a0d8c449b823bd"
integrity sha512-9hsFDsgUwrdOoW1D97Ewog7DYSHaq4WKuNs0LHF9RiCmqB0Z+XRR4Pf7u7u9z/8CciHuJ6yxNws1XznI3ddjEw==
dependencies:
"@typescript-eslint/types" "5.47.1"
"@typescript-eslint/visitor-keys" "5.47.1"
"@typescript-eslint/type-utils@5.47.0":
version "5.47.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.47.0.tgz#2b440979c574e317d3473225ae781f292c99e55d"
@ -1532,11 +1565,26 @@
debug "^4.3.4"
tsutils "^3.21.0"
"@typescript-eslint/type-utils@5.47.1":
version "5.47.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.47.1.tgz#aee13314f840ab336c1adb49a300856fd16d04ce"
integrity sha512-/UKOeo8ee80A7/GJA427oIrBi/Gd4osk/3auBUg4Rn9EahFpevVV1mUK8hjyQD5lHPqX397x6CwOk5WGh1E/1w==
dependencies:
"@typescript-eslint/typescript-estree" "5.47.1"
"@typescript-eslint/utils" "5.47.1"
debug "^4.3.4"
tsutils "^3.21.0"
"@typescript-eslint/types@5.47.0":
version "5.47.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.47.0.tgz#67490def406eaa023dbbd8da42ee0d0c9b5229d3"
integrity sha512-eslFG0Qy8wpGzDdYKu58CEr3WLkjwC5Usa6XbuV89ce/yN5RITLe1O8e+WFEuxnfftHiJImkkOBADj58ahRxSg==
"@typescript-eslint/types@5.47.1":
version "5.47.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.47.1.tgz#459f07428aec5a8c4113706293c2ae876741ac8e"
integrity sha512-CmALY9YWXEpwuu6377ybJBZdtSAnzXLSQcxLSqSQSbC7VfpMu/HLVdrnVJj7ycI138EHqocW02LPJErE35cE9A==
"@typescript-eslint/typescript-estree@5.47.0":
version "5.47.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.47.0.tgz#ed971a11c5c928646d6ba7fc9dfdd6e997649aca"
@ -1550,6 +1598,19 @@
semver "^7.3.7"
tsutils "^3.21.0"
"@typescript-eslint/typescript-estree@5.47.1":
version "5.47.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.47.1.tgz#b9d8441308aca53df7f69b2c67a887b82c9ed418"
integrity sha512-4+ZhFSuISAvRi2xUszEj0xXbNTHceV9GbH9S8oAD2a/F9SW57aJNQVOCxG8GPfSWH/X4eOPdMEU2jYVuWKEpWA==
dependencies:
"@typescript-eslint/types" "5.47.1"
"@typescript-eslint/visitor-keys" "5.47.1"
debug "^4.3.4"
globby "^11.1.0"
is-glob "^4.0.3"
semver "^7.3.7"
tsutils "^3.21.0"
"@typescript-eslint/utils@5.47.0":
version "5.47.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.47.0.tgz#b5005f7d2696769a1fdc1e00897005a25b3a0ec7"
@ -1564,6 +1625,20 @@
eslint-utils "^3.0.0"
semver "^7.3.7"
"@typescript-eslint/utils@5.47.1":
version "5.47.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.47.1.tgz#595f25ac06e9ee28c339fd43c709402820b13d7b"
integrity sha512-l90SdwqfmkuIVaREZ2ykEfCezepCLxzWMo5gVfcJsJCaT4jHT+QjgSkYhs5BMQmWqE9k3AtIfk4g211z/sTMVw==
dependencies:
"@types/json-schema" "^7.0.9"
"@types/semver" "^7.3.12"
"@typescript-eslint/scope-manager" "5.47.1"
"@typescript-eslint/types" "5.47.1"
"@typescript-eslint/typescript-estree" "5.47.1"
eslint-scope "^5.1.1"
eslint-utils "^3.0.0"
semver "^7.3.7"
"@typescript-eslint/visitor-keys@5.47.0":
version "5.47.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.0.tgz#4aca4efbdf6209c154df1f7599852d571b80bb45"
@ -1572,6 +1647,14 @@
"@typescript-eslint/types" "5.47.0"
eslint-visitor-keys "^3.3.0"
"@typescript-eslint/visitor-keys@5.47.1":
version "5.47.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.1.tgz#d35c2da544dbb685db9c5b5b85adac0a1d74d1f2"
integrity sha512-rF3pmut2JCCjh6BLRhNKdYjULMb1brvoaiWDlHfLNVgmnZ0sBVJrs3SyaKE1XoDDnJuAx/hDQryHYmPUuNq0ig==
dependencies:
"@typescript-eslint/types" "5.47.1"
eslint-visitor-keys "^3.3.0"
"@ungap/promise-all-settled@1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
@ -3000,7 +3083,12 @@ core-js@^2.6.12:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
core-js@^3.26.1, core-js@^3.8.3:
core-js@^3.27.1:
version "3.27.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.27.1.tgz#23cc909b315a6bb4e418bf40a52758af2103ba46"
integrity sha512-GutwJLBChfGCpwwhbYoqfv03LAfmiz7e7D/BNxzeMxwQf10GRSzqiOjx7AmtEk+heiD/JWmBuyBPgFtx0Sg1ww==
core-js@^3.8.3:
version "3.26.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.26.1.tgz#7a9816dabd9ee846c1c0fe0e8fcad68f3709134e"
integrity sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA==
@ -3788,12 +3876,12 @@ eslint-webpack-plugin@^3.1.0:
normalize-path "^3.0.0"
schema-utils "^4.0.0"
eslint@8.30.0:
version "8.30.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.30.0.tgz#83a506125d089eef7c5b5910eeea824273a33f50"
integrity sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ==
eslint@8.31.0:
version "8.31.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.31.0.tgz#75028e77cbcff102a9feae1d718135931532d524"
integrity sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==
dependencies:
"@eslint/eslintrc" "^1.4.0"
"@eslint/eslintrc" "^1.4.1"
"@humanwhocodes/config-array" "^0.11.8"
"@humanwhocodes/module-importer" "^1.0.1"
"@nodelib/fs.walk" "^1.2.8"
@ -6723,10 +6811,10 @@ safe-regex-test@^1.0.0:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sanitize-html@^2.8.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.8.0.tgz#651d1d0e5b2d61b4ec6147cc46f6d6680eef98ce"
integrity sha512-ZsGyc6avnqgvEm3eMKrcy8xa7WM1MrGrfkGsUgQee2CU+vg3PCfNCexXwBDF/6dEPvaQ4k/QqRjnYKHL8xgNjg==
sanitize-html@^2.8.1:
version "2.8.1"
resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.8.1.tgz#319c4fdba67e1edf35b1fd6d9362210044826d47"
integrity sha512-qK5neD0SaMxGwVv5txOYv05huC3o6ZAA4h5+7nJJgWMNFUNRjcjLO6FpwAtKzfKCZ0jrG6xTk6eVFskbvOGblg==
dependencies:
deepmerge "^4.2.2"
escape-string-regexp "^4.0.0"

210
poetry.lock generated
View File

@ -14,14 +14,14 @@ files = [
[[package]]
name = "alembic"
version = "1.9.0"
version = "1.9.1"
description = "A database migration tool for SQLAlchemy."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "alembic-1.9.0-py3-none-any.whl", hash = "sha256:e8ce855f731d92cfdbf9a71d148401fcc420399927817c6e2c0b6a5f31db745d"},
{file = "alembic-1.9.0.tar.gz", hash = "sha256:6af6792fe699730b27480382701b16028ebbaac6bc5cd4f06daf5fa3e4690364"},
{file = "alembic-1.9.1-py3-none-any.whl", hash = "sha256:a9781ed0979a20341c2cbb56bd22bd8db4fc1913f955e705444bd3a97c59fa32"},
{file = "alembic-1.9.1.tar.gz", hash = "sha256:f9f76e41061f5ebe27d4fe92600df9dd612521a7683f904dab328ba02cffa5a2"},
]
[package.dependencies]
@ -62,21 +62,22 @@ typing-extensions = {version = ">=3.6.5", markers = "python_version < \"3.8\""}
[[package]]
name = "attrs"
version = "22.1.0"
version = "22.2.0"
description = "Classes Without Boilerplate"
category = "dev"
optional = false
python-versions = ">=3.5"
python-versions = ">=3.6"
files = [
{file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
{file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
{file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"},
{file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"},
]
[package.extras]
dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"]
docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"]
tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"]
dev = ["attrs[docs,tests]"]
docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"]
tests = ["attrs[tests-no-zope]", "zope.interface"]
tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"]
[[package]]
name = "authlib"
@ -352,63 +353,63 @@ test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"]
[[package]]
name = "coverage"
version = "7.0.0"
version = "7.0.1"
description = "Code coverage measurement for Python"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "coverage-7.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f2569682d6ea9628da8d6ba38579a48b1e53081226ec7a6c82b5024b3ce5009f"},
{file = "coverage-7.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ec256a592b497f26054195f7d7148892aca8c4cdcc064a7cc66ef7a0455b811"},
{file = "coverage-7.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5885a4ceb6dde34271bb0adafa4a248a7f589c89821e9da3110c39f92f41e21b"},
{file = "coverage-7.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d43d406a4d73aa7f855fa44fa77ff47e739b565b2af3844600cdc016d01e46b9"},
{file = "coverage-7.0.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18df11efa615b79b9ecc13035a712957ff6283f7b244e57684e1c092869f541"},
{file = "coverage-7.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f6a4bf5bdee93f6817797beba7086292c2ebde6df0d5822e0c33f8b05415c339"},
{file = "coverage-7.0.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:33efe89cd0efef016db19d8d05aa46631f76793de90a61b6717acb202b36fe60"},
{file = "coverage-7.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:96b5b1f1079e48f56bfccf103bcf44d48b9eb5163f1ea523fad580f15d3fe5e0"},
{file = "coverage-7.0.0-cp310-cp310-win32.whl", hash = "sha256:fb85b7a7a4b204bd59d6d0b0c8d87d9ffa820da225e691dfaffc3137dc05b5f6"},
{file = "coverage-7.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:793dcd9d42035746fc7637df4336f7581df19d33c5c5253cf988c99d8e93a8ba"},
{file = "coverage-7.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d564142a03d3bc8913499a458e931b52ddfe952f69b6cd4b24d810fd2959044a"},
{file = "coverage-7.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0a8b0e86bede874bf5da566b02194fbb12dd14ce3585cabd58452007f272ba81"},
{file = "coverage-7.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e645c73cbfc4577d93747d3f793115acf6f907a7eb9208fa807fdcf2da1964a4"},
{file = "coverage-7.0.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de06e7585abe88c6d38c1b73ce4c3cb4c1a79fbb0da0d0f8e8689ef5729ec60d"},
{file = "coverage-7.0.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a30b646fbdd5bc52f506e149fa4fbdef82432baf6b81774e61ec4e3b43b9cbde"},
{file = "coverage-7.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:db8141856dc9be0917413df7200f53accf1d84c8b156868e6af058a1ea8e903a"},
{file = "coverage-7.0.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:59e71912c7fc78d08a567ee65656123878f49ca1b5672e660ea70bf8dfbebf8f"},
{file = "coverage-7.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b8f7cd942dda3795fc9eadf303cc53a422ac057e3b70c2ad6d4276ec6a83a541"},
{file = "coverage-7.0.0-cp311-cp311-win32.whl", hash = "sha256:bf437a04b9790d3c9cd5b48e9ce9aa84229040e3ae7d6c670a55118906113c5a"},
{file = "coverage-7.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:a7e1bb36b4e57a2d304322021b35d4e4a25fa0d501ba56e8e51efaebf4480556"},
{file = "coverage-7.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:215f40ef86f1958a1151fa7fad2b4f2f99534c4e10a34a1e065eba3f19ef8868"},
{file = "coverage-7.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae088eb1cbdad8206931b1bf3f11dee644e038a9300be84d3e705e29356e5b1d"},
{file = "coverage-7.0.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9071e197faa24837b967bc9aa0b9ef961f805a75f1ee3ea1f3367f55cd46c3c"},
{file = "coverage-7.0.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f1e6d9c70d45a960d3f3d781ea62b167fdf2e0e1f6bb282b96feea653adb923"},
{file = "coverage-7.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9fadd15f9fcfd7b16d9cccce9f5e6ec6f9b8df860633ad9aa62c2b14c259560f"},
{file = "coverage-7.0.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:10b6246cae61896ab4c7568e498e492cbb73a2dfa4c3af79141c43cf806f929a"},
{file = "coverage-7.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a8785791c2120af114ea7a06137f7778632e568a5aa2bbfc3b46c573b702af74"},
{file = "coverage-7.0.0-cp37-cp37m-win32.whl", hash = "sha256:30220518dd89c4878908d73f5f3d1269f86e9e045354436534587a18c7b9da85"},
{file = "coverage-7.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bc904aa96105d73357de03de76336b1e3db28e2b12067d36625fd9646ab043fd"},
{file = "coverage-7.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2331b7bd84a1be79bd17ca8e103ce38db8cbf7cb354dc56e651ba489cf849212"},
{file = "coverage-7.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e907db8bdd0ad1253a33c20fdc5f0f6209d271114a9c6f1fcdf96617343f7ca0"},
{file = "coverage-7.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0deee68e0dae1d6e3fe6943c76d7e66fbeb6519bd08e4e5366bcc28a8a9aca"},
{file = "coverage-7.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fff0f08bc5ffd0d78db821971472b4adc2ee876b86f743e46d634fb8e3c22f"},
{file = "coverage-7.0.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a290b7921c1c05787b953e5854d394e887df40696f21381cc33c4e2179bf50ac"},
{file = "coverage-7.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:100546219af59d2ad82d4575de03a303eb27b75ea36ffbd1677371924d50bcbc"},
{file = "coverage-7.0.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c1ba6e63b831112b9484ff5905370d89e43d4316bac76d403031f60d61597466"},
{file = "coverage-7.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c685fc17d6f4f1a3833e9dac27d0b931f7ccb52be6c30d269374203c7d0204a2"},
{file = "coverage-7.0.0-cp38-cp38-win32.whl", hash = "sha256:8938f3a10f45019b502020ba9567b97b6ecc8c76b664b421705c5406d4f92fe8"},
{file = "coverage-7.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:c4b63888bef2928d0eca12cbce0760cfb696acb4fe226eb55178b6a2a039328a"},
{file = "coverage-7.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cda63459eb20652b22e038729a8f5063862c189a3963cb042a764b753172f75e"},
{file = "coverage-7.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e06abac1a4aec1ff989131e43ca917fc7bd296f34bf0cfe86cbf74343b21566d"},
{file = "coverage-7.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32b94ad926e933976627f040f96dd1d9b0ac91f8d27e868c30a28253b9b6ac2d"},
{file = "coverage-7.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6b4af31fb49a2ae8de1cd505fa66c403bfcc5066e845ac19d8904dcfc9d40da"},
{file = "coverage-7.0.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36b62f0220459e528ad5806cc7dede71aa716e067d2cb10cb4a09686b8791fba"},
{file = "coverage-7.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:43ec1935c6d6caab4f3bc126d20bd709c0002a175d62208ebe745be37a826a41"},
{file = "coverage-7.0.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8593c9baf1f0f273afa22f5b45508b76adc7b8e94e17e7d98fbe1e3cd5812af2"},
{file = "coverage-7.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fee283cd36c3f14422d9c1b51da24ddbb5e1eed89ad2480f6a9f115df38b5df8"},
{file = "coverage-7.0.0-cp39-cp39-win32.whl", hash = "sha256:97c0b001ff15b8e8882995fc07ac0a08c8baf8b13c1145f3f12e0587bbb0e335"},
{file = "coverage-7.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:8dbf83a4611c591b5de65069b6fd4dd3889200ed270cd2f7f5ac765d3842889f"},
{file = "coverage-7.0.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:bcaf18e46668057051a312c714a4548b81f7e8fb3454116ad97be7562d2a99e4"},
{file = "coverage-7.0.0.tar.gz", hash = "sha256:9a175da2a7320e18fc3ee1d147639a2b3a8f037e508c96aa2da160294eb50e17"},
{file = "coverage-7.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b3695c4f4750bca943b3e1f74ad4be8d29e4aeab927d50772c41359107bd5d5c"},
{file = "coverage-7.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fa6a5a224b7f4cfb226f4fc55a57e8537fcc096f42219128c2c74c0e7d0953e1"},
{file = "coverage-7.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74f70cd92669394eaf8d7756d1b195c8032cf7bbbdfce3bc489d4e15b3b8cf73"},
{file = "coverage-7.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b66bb21a23680dee0be66557dc6b02a3152ddb55edf9f6723fa4a93368f7158d"},
{file = "coverage-7.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d87717959d4d0ee9db08a0f1d80d21eb585aafe30f9b0a54ecf779a69cb015f6"},
{file = "coverage-7.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:854f22fa361d1ff914c7efa347398374cc7d567bdafa48ac3aa22334650dfba2"},
{file = "coverage-7.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1e414dc32ee5c3f36544ea466b6f52f28a7af788653744b8570d0bf12ff34bc0"},
{file = "coverage-7.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6c5ad996c6fa4d8ed669cfa1e8551348729d008a2caf81489ab9ea67cfbc7498"},
{file = "coverage-7.0.1-cp310-cp310-win32.whl", hash = "sha256:691571f31ace1837838b7e421d3a09a8c00b4aac32efacb4fc9bd0a5c647d25a"},
{file = "coverage-7.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:89caf4425fe88889e2973a8e9a3f6f5f9bbe5dd411d7d521e86428c08a873a4a"},
{file = "coverage-7.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:63d56165a7c76265468d7e0c5548215a5ba515fc2cba5232d17df97bffa10f6c"},
{file = "coverage-7.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4f943a3b2bc520102dd3e0bb465e1286e12c9a54f58accd71b9e65324d9c7c01"},
{file = "coverage-7.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:830525361249dc4cd013652b0efad645a385707a5ae49350c894b67d23fbb07c"},
{file = "coverage-7.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd1b9c5adc066db699ccf7fa839189a649afcdd9e02cb5dc9d24e67e7922737d"},
{file = "coverage-7.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00c14720b8b3b6c23b487e70bd406abafc976ddc50490f645166f111c419c39"},
{file = "coverage-7.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6d55d840e1b8c0002fce66443e124e8581f30f9ead2e54fbf6709fb593181f2c"},
{file = "coverage-7.0.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:66b18c3cf8bbab0cce0d7b9e4262dc830e93588986865a8c78ab2ae324b3ed56"},
{file = "coverage-7.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:12a5aa77783d49e05439fbe6e6b427484f8a0f9f456b46a51d8aac022cfd024d"},
{file = "coverage-7.0.1-cp311-cp311-win32.whl", hash = "sha256:b77015d1cb8fe941be1222a5a8b4e3fbca88180cfa7e2d4a4e58aeabadef0ab7"},
{file = "coverage-7.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb992c47cb1e5bd6a01e97182400bcc2ba2077080a17fcd7be23aaa6e572e390"},
{file = "coverage-7.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e78e9dcbf4f3853d3ae18a8f9272111242531535ec9e1009fa8ec4a2b74557dc"},
{file = "coverage-7.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e60bef2e2416f15fdc05772bf87db06c6a6f9870d1db08fdd019fbec98ae24a9"},
{file = "coverage-7.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9823e4789ab70f3ec88724bba1a203f2856331986cd893dedbe3e23a6cfc1e4e"},
{file = "coverage-7.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9158f8fb06747ac17bd237930c4372336edc85b6e13bdc778e60f9d685c3ca37"},
{file = "coverage-7.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:486ee81fa694b4b796fc5617e376326a088f7b9729c74d9defa211813f3861e4"},
{file = "coverage-7.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1285648428a6101b5f41a18991c84f1c3959cee359e51b8375c5882fc364a13f"},
{file = "coverage-7.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2c44fcfb3781b41409d0f060a4ed748537557de9362a8a9282182fafb7a76ab4"},
{file = "coverage-7.0.1-cp37-cp37m-win32.whl", hash = "sha256:d6814854c02cbcd9c873c0f3286a02e3ac1250625cca822ca6bc1018c5b19f1c"},
{file = "coverage-7.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f66460f17c9319ea4f91c165d46840314f0a7c004720b20be58594d162a441d8"},
{file = "coverage-7.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9b373c9345c584bb4b5f5b8840df7f4ab48c4cbb7934b58d52c57020d911b856"},
{file = "coverage-7.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d3022c3007d3267a880b5adcf18c2a9bf1fc64469b394a804886b401959b8742"},
{file = "coverage-7.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92651580bd46519067e36493acb394ea0607b55b45bd81dd4e26379ed1871f55"},
{file = "coverage-7.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cfc595d2af13856505631be072835c59f1acf30028d1c860b435c5fc9c15b69"},
{file = "coverage-7.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b4b3a4d9915b2be879aff6299c0a6129f3d08a775d5a061f503cf79571f73e4"},
{file = "coverage-7.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b6f22bb64cc39bcb883e5910f99a27b200fdc14cdd79df8696fa96b0005c9444"},
{file = "coverage-7.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72d1507f152abacea81f65fee38e4ef3ac3c02ff8bc16f21d935fd3a8a4ad910"},
{file = "coverage-7.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0a79137fc99815fff6a852c233628e735ec15903cfd16da0f229d9c4d45926ab"},
{file = "coverage-7.0.1-cp38-cp38-win32.whl", hash = "sha256:b3763e7fcade2ff6c8e62340af9277f54336920489ceb6a8cd6cc96da52fcc62"},
{file = "coverage-7.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:09f6b5a8415b6b3e136d5fec62b552972187265cb705097bf030eb9d4ffb9b60"},
{file = "coverage-7.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:978258fec36c154b5e250d356c59af7d4c3ba02bef4b99cda90b6029441d797d"},
{file = "coverage-7.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:19ec666533f0f70a0993f88b8273057b96c07b9d26457b41863ccd021a043b9a"},
{file = "coverage-7.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfded268092a84605f1cc19e5c737f9ce630a8900a3589e9289622db161967e9"},
{file = "coverage-7.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07bcfb1d8ac94af886b54e18a88b393f6a73d5959bb31e46644a02453c36e475"},
{file = "coverage-7.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:397b4a923cc7566bbc7ae2dfd0ba5a039b61d19c740f1373791f2ebd11caea59"},
{file = "coverage-7.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:aec2d1515d9d39ff270059fd3afbb3b44e6ec5758af73caf18991807138c7118"},
{file = "coverage-7.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c20cfebcc149a4c212f6491a5f9ff56f41829cd4f607b5be71bb2d530ef243b1"},
{file = "coverage-7.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fd556ff16a57a070ce4f31c635953cc44e25244f91a0378c6e9bdfd40fdb249f"},
{file = "coverage-7.0.1-cp39-cp39-win32.whl", hash = "sha256:b9ea158775c7c2d3e54530a92da79496fb3fb577c876eec761c23e028f1e216c"},
{file = "coverage-7.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:d1991f1dd95eba69d2cd7708ff6c2bbd2426160ffc73c2b81f617a053ebcb1a8"},
{file = "coverage-7.0.1-pp37.pp38.pp39-none-any.whl", hash = "sha256:3dd4ee135e08037f458425b8842d24a95a0961831a33f89685ff86b77d378f89"},
{file = "coverage-7.0.1.tar.gz", hash = "sha256:a4a574a19eeb67575a5328a5760bbbb737faa685616586a9f9da4281f940109c"},
]
[package.dependencies]
@ -521,14 +522,14 @@ watch = ["watchdog", "watchdog-gevent"]
[[package]]
name = "exceptiongroup"
version = "1.0.4"
version = "1.1.0"
description = "Backport of PEP 654 (exception groups)"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"},
{file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"},
{file = "exceptiongroup-1.1.0-py3-none-any.whl", hash = "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e"},
{file = "exceptiongroup-1.1.0.tar.gz", hash = "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"},
]
[package.extras]
@ -623,22 +624,23 @@ dramatiq = ">=1.5,<2.0"
[[package]]
name = "flask-limiter"
version = "2.8.1"
version = "2.9.2"
description = "Rate limiting for flask applications"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "Flask-Limiter-2.8.1.tar.gz", hash = "sha256:d37d0a39ce1fe36f661a765132557d3213040c51d9355379fe2f336bfea7e308"},
{file = "Flask_Limiter-2.8.1-py3-none-any.whl", hash = "sha256:621f1f26fbde4768c60df510e31569e72ae91eee659633e35fd78d90073a0d56"},
{file = "Flask-Limiter-2.9.2.tar.gz", hash = "sha256:041bf0d72c8c62d2cb54c772de1ad842c82bdefeddfadc1c9171739f296484e2"},
{file = "Flask_Limiter-2.9.2-py3-none-any.whl", hash = "sha256:64c6456204d88006324127071598a04cdd77be1576e00e8f5b74fad80925ea37"},
]
[package.dependencies]
Flask = ">=2"
limits = [
{version = ">=2.3"},
{version = ">=2.8"},
{version = "*", extras = ["redis"], optional = true, markers = "extra == \"redis\""},
]
ordered-set = ">4,<5"
rich = ">=12,<13"
typing-extensions = ">=4"
@ -712,14 +714,14 @@ smmap = ">=3.0.1,<6"
[[package]]
name = "gitpython"
version = "3.1.29"
version = "3.1.30"
description = "GitPython is a python library used to interact with Git repositories"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "GitPython-3.1.29-py3-none-any.whl", hash = "sha256:41eea0deec2deea139b459ac03656f0dd28fc4a3387240ec1d3c259a2c47850f"},
{file = "GitPython-3.1.29.tar.gz", hash = "sha256:cc36bfc4a3f913e66805a28e84703e419d9c264c1077e537b54f0e1af85dbefd"},
{file = "GitPython-3.1.30-py3-none-any.whl", hash = "sha256:cd455b0000615c60e286208ba540271af9fe531fa6a87cc590a7298785ab2882"},
{file = "GitPython-3.1.30.tar.gz", hash = "sha256:769c2d83e13f5d938b7688479da374c4e3d49f71549aaf462b646db9602ea6f8"},
]
[package.dependencies]
@ -909,21 +911,21 @@ testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packag
[[package]]
name = "importlib-resources"
version = "5.10.1"
version = "5.10.2"
description = "Read resources from Python packages"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "importlib_resources-5.10.1-py3-none-any.whl", hash = "sha256:c09b067d82e72c66f4f8eb12332f5efbebc9b007c0b6c40818108c9870adc363"},
{file = "importlib_resources-5.10.1.tar.gz", hash = "sha256:32bb095bda29741f6ef0e5278c42df98d135391bee5f932841efc0041f748dc3"},
{file = "importlib_resources-5.10.2-py3-none-any.whl", hash = "sha256:7d543798b0beca10b6a01ac7cafda9f822c54db9e8376a6bf57e0cbd74d486b6"},
{file = "importlib_resources-5.10.2.tar.gz", hash = "sha256:e4a96c8cc0339647ff9a5e0550d9f276fc5a01ffa276012b58ec108cfd7b8484"},
]
[package.dependencies]
zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
[package.extras]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
[[package]]
@ -940,14 +942,14 @@ files = [
[[package]]
name = "isort"
version = "5.11.3"
version = "5.11.4"
description = "A Python utility / library to sort Python imports."
category = "dev"
optional = false
python-versions = ">=3.7.0"
files = [
{file = "isort-5.11.3-py3-none-any.whl", hash = "sha256:83155ffa936239d986b0f190347a3f2285f42a9b9e1725c89d865b27dd0627e5"},
{file = "isort-5.11.3.tar.gz", hash = "sha256:a8ca25fbfad0f7d5d8447a4314837298d9f6b23aed8618584c894574f626b64b"},
{file = "isort-5.11.4-py3-none-any.whl", hash = "sha256:c033fd0edb91000a7f09527fe5c75321878f98322a77ddcc81adbd83724afb7b"},
{file = "isort-5.11.4.tar.gz", hash = "sha256:6db30c5ded9815d813932c04c2f85a360bcdd35fed496f4d8f35495ef0a261b6"},
]
[package.extras]
@ -988,14 +990,14 @@ i18n = ["Babel (>=2.7)"]
[[package]]
name = "limits"
version = "2.7.2"
version = "2.8.0"
description = "Rate limiting utilities"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "limits-2.7.2-py3-none-any.whl", hash = "sha256:503a22d3b292fccf1a25f26654fefb44f97d83cb6af37b8996c0e2499df5363b"},
{file = "limits-2.7.2.tar.gz", hash = "sha256:159b3c1514af0529854b640f43299aa4a9961b3298e352f4197d9736b8309106"},
{file = "limits-2.8.0-py3-none-any.whl", hash = "sha256:ee19e9a98dd392d52dd62cfc28d93710eb29a22cf9834c21c16ee19367c4d596"},
{file = "limits-2.8.0.tar.gz", hash = "sha256:b47e01c48b3b677c7230cb51775ebe7c30a574e9711dc68499dab810ed883bb9"},
]
[package.dependencies]
@ -1162,6 +1164,21 @@ files = [
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
]
[[package]]
name = "ordered-set"
version = "4.1.0"
description = "An OrderedSet is a custom MutableSet that remembers its order, so that every"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "ordered-set-4.1.0.tar.gz", hash = "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8"},
{file = "ordered_set-4.1.0-py3-none-any.whl", hash = "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562"},
]
[package.extras]
dev = ["black", "mypy", "pytest"]
[[package]]
name = "outcome"
version = "1.2.0"
@ -1288,19 +1305,22 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa
[[package]]
name = "platformdirs"
version = "2.6.0"
version = "2.6.2"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "platformdirs-2.6.0-py3-none-any.whl", hash = "sha256:1a89a12377800c81983db6be069ec068eee989748799b946cce2a6e80dcc54ca"},
{file = "platformdirs-2.6.0.tar.gz", hash = "sha256:b46ffafa316e6b83b47489d240ce17173f123a9b9c83282141c3daf26ad9ac2e"},
{file = "platformdirs-2.6.2-py3-none-any.whl", hash = "sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490"},
{file = "platformdirs-2.6.2.tar.gz", hash = "sha256:e1fea1fe471b9ff8332e229df3cb7de4f53eeea4998d3b6bfff542115e998bd2"},
]
[package.dependencies]
typing-extensions = {version = ">=4.4", markers = "python_version < \"3.8\""}
[package.extras]
docs = ["furo (>=2022.9.29)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.4)"]
test = ["appdirs (==1.4.4)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"]
test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
[[package]]
name = "pluggy"
@ -2484,26 +2504,26 @@ files = [
[[package]]
name = "types-redis"
version = "4.3.21.6"
version = "4.3.21.7"
description = "Typing stubs for redis"
category = "dev"
optional = false
python-versions = "*"
files = [
{file = "types-redis-4.3.21.6.tar.gz", hash = "sha256:f7969f73a0f79e9e7895f053a06d8b429fb7b5d4fe1269b8ee40463388f653ad"},
{file = "types_redis-4.3.21.6-py3-none-any.whl", hash = "sha256:615e5a9142993789ffc22ee54435769b600da3e528bb51cf38430e5cd82af306"},
{file = "types-redis-4.3.21.7.tar.gz", hash = "sha256:f9baac41fcafb0b8ec655893feebc5d8bd189da05062d03b4566a16c092fc08c"},
{file = "types_redis-4.3.21.7-py3-none-any.whl", hash = "sha256:834e816a84fc570aa96551a9252b0df164ac671bc09e4708e2f8e2b129449ef1"},
]
[[package]]
name = "types-requests"
version = "2.28.11.6"
version = "2.28.11.7"
description = "Typing stubs for requests"
category = "dev"
optional = false
python-versions = "*"
files = [
{file = "types-requests-2.28.11.6.tar.gz", hash = "sha256:8c1b1e6a0b19522b4738063e772dcee82cee1c3646536ccc4eb96f655af2b6c6"},
{file = "types_requests-2.28.11.6-py3-none-any.whl", hash = "sha256:48b7c06e3dffc1b6359e1888084a2b97f41b6b63f208c571ddb02ddbc6a892e4"},
{file = "types-requests-2.28.11.7.tar.gz", hash = "sha256:0ae38633734990d019b80f5463dfa164ebd3581998ac8435f526da6fe4d598c3"},
{file = "types_requests-2.28.11.7-py3-none-any.whl", hash = "sha256:b6a2fca8109f4fdba33052f11ed86102bddb2338519e1827387137fefc66a98b"},
]
[package.dependencies]
@ -2703,4 +2723,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
[metadata]
lock-version = "2.0"
python-versions = "^3.7"
content-hash = "44659aa26f07c74af8d04ca7a5d426cf439e65ad5301386d0f6713d676ab9cd9"
content-hash = "a8eb55d8966ea55b1f38a96b410e85bab447cb436efcb208b514bf6cc20a9ef4"

Some files were not shown because too many files have changed in this diff Show More