geo-activity-playground 0.24.2__py3-none-any.whl → 0.26.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. geo_activity_playground/__main__.py +0 -2
  2. geo_activity_playground/core/activities.py +71 -149
  3. geo_activity_playground/core/enrichment.py +164 -0
  4. geo_activity_playground/core/paths.py +34 -15
  5. geo_activity_playground/core/tasks.py +27 -4
  6. geo_activity_playground/explorer/tile_visits.py +78 -42
  7. geo_activity_playground/{core → importers}/activity_parsers.py +7 -14
  8. geo_activity_playground/importers/directory.py +36 -27
  9. geo_activity_playground/importers/strava_api.py +45 -38
  10. geo_activity_playground/importers/strava_checkout.py +24 -16
  11. geo_activity_playground/webui/activity/controller.py +2 -2
  12. geo_activity_playground/webui/activity/templates/activity/show.html.j2 +2 -0
  13. geo_activity_playground/webui/app.py +11 -31
  14. geo_activity_playground/webui/entry_controller.py +5 -5
  15. geo_activity_playground/webui/heatmap/heatmap_controller.py +6 -0
  16. geo_activity_playground/webui/static/bootstrap-dark-mode.js +78 -0
  17. geo_activity_playground/webui/strava/__init__.py +0 -0
  18. geo_activity_playground/webui/strava/blueprint.py +33 -0
  19. geo_activity_playground/webui/strava/controller.py +49 -0
  20. geo_activity_playground/webui/strava/templates/strava/client-id.html.j2 +36 -0
  21. geo_activity_playground/webui/strava/templates/strava/connected.html.j2 +14 -0
  22. geo_activity_playground/webui/templates/home.html.j2 +5 -0
  23. geo_activity_playground/webui/templates/page.html.j2 +44 -12
  24. geo_activity_playground/webui/templates/settings.html.j2 +24 -0
  25. geo_activity_playground/webui/upload/controller.py +13 -17
  26. {geo_activity_playground-0.24.2.dist-info → geo_activity_playground-0.26.0.dist-info}/METADATA +1 -1
  27. {geo_activity_playground-0.24.2.dist-info → geo_activity_playground-0.26.0.dist-info}/RECORD +30 -25
  28. geo_activity_playground/core/cache_migrations.py +0 -133
  29. geo_activity_playground/webui/strava_controller.py +0 -27
  30. geo_activity_playground/webui/templates/strava-connect.html.j2 +0 -30
  31. {geo_activity_playground-0.24.2.dist-info → geo_activity_playground-0.26.0.dist-info}/LICENSE +0 -0
  32. {geo_activity_playground-0.24.2.dist-info → geo_activity_playground-0.26.0.dist-info}/WHEEL +0 -0
  33. {geo_activity_playground-0.24.2.dist-info → geo_activity_playground-0.26.0.dist-info}/entry_points.txt +0 -0
@@ -18,11 +18,12 @@ from .explorer.blueprint import make_explorer_blueprint
18
18
  from .heatmap.blueprint import make_heatmap_blueprint
19
19
  from .search_controller import SearchController
20
20
  from .square_planner.blueprint import make_square_planner_blueprint
21
- from .strava_controller import StravaController
21
+ from .strava.blueprint import make_strava_blueprint
22
22
  from .summary.blueprint import make_summary_blueprint
23
23
  from .tile.blueprint import make_tile_blueprint
24
24
  from .upload.blueprint import make_upload_blueprint
25
25
  from geo_activity_playground.core.privacy_zones import PrivacyZone
26
+ from geo_activity_playground.webui.strava.controller import StravaController
26
27
 
27
28
 
28
29
  def route_search(app: Flask, repository: ActivityRepository) -> None:
@@ -45,35 +46,10 @@ def route_start(app: Flask, repository: ActivityRepository) -> None:
45
46
  return render_template("home.html.j2", **entry_controller.render())
46
47
 
47
48
 
48
- def route_strava(app: Flask, host: str, port: int) -> None:
49
- strava_controller = StravaController()
50
-
51
- @app.route("/strava/connect")
52
- def strava_connect():
53
- return render_template(
54
- "strava-connect.html.j2",
55
- host=host,
56
- port=port,
57
- **strava_controller.action_connect()
58
- )
59
-
60
- @app.route("/strava/authorize")
61
- def strava_authorize():
62
- client_id = request.form["client_id"]
63
- client_secret = request.form["client_secret"]
64
- return redirect(
65
- strava_controller.action_authorize(host, port, client_id, client_secret)
66
- )
67
-
68
- @app.route("/strava/callback")
69
- def strava_callback():
70
- code = request.args.get("code", type=str)
71
- return render_template(
72
- "strava-connect.html.j2",
73
- host=host,
74
- port=port,
75
- **strava_controller.action_connect()
76
- )
49
+ def route_settings(app: Flask) -> None:
50
+ @app.route("/settings")
51
+ def settings():
52
+ return render_template("settings.html.j2")
77
53
 
78
54
 
79
55
  def get_secret_key():
@@ -99,7 +75,7 @@ def webui_main(
99
75
 
100
76
  route_search(app, repository)
101
77
  route_start(app, repository)
102
- route_strava(app, host, port)
78
+ route_settings(app)
103
79
 
104
80
  app.config["UPLOAD_FOLDER"] = "Activities"
105
81
  app.secret_key = get_secret_key()
@@ -136,6 +112,10 @@ def webui_main(
136
112
  make_summary_blueprint(repository),
137
113
  url_prefix="/summary",
138
114
  )
115
+ app.register_blueprint(
116
+ make_strava_blueprint(host, port),
117
+ url_prefix="/strava",
118
+ )
139
119
  app.register_blueprint(make_tile_blueprint(), url_prefix="/tile")
140
120
  app.register_blueprint(
141
121
  make_upload_blueprint(repository, tile_visit_accessor, config),
@@ -13,12 +13,12 @@ class EntryController:
13
13
  self._repository = repository
14
14
 
15
15
  def render(self) -> dict:
16
- result = {
17
- "distance_last_30_days_plot": distance_last_30_days_meta_plot(
16
+ result = {"latest_activities": []}
17
+
18
+ if len(self._repository):
19
+ result["distance_last_30_days_plot"] = distance_last_30_days_meta_plot(
18
20
  self._repository.meta
19
- ),
20
- "latest_activities": [],
21
- }
21
+ )
22
22
 
23
23
  for activity in itertools.islice(
24
24
  self._repository.iter_activities(dropna=True), 15
@@ -83,6 +83,12 @@ class HeatmapController:
83
83
  with work_tracker(
84
84
  tile_count_cache_path.with_suffix(".json")
85
85
  ) as parsed_activities:
86
+ if parsed_activities - activity_ids:
87
+ logger.warning(
88
+ f"Resetting heatmap cache for {kind=}/{x=}/{y=}/{z=} because activities have been removed."
89
+ )
90
+ tile_counts = np.zeros(tile_pixels, dtype=np.int32)
91
+ parsed_activities.clear()
86
92
  for activity_id in activity_ids:
87
93
  if activity_id in parsed_activities:
88
94
  continue
@@ -0,0 +1,78 @@
1
+ /*!
2
+ * Color mode toggler for Bootstrap's docs (https://getbootstrap.com/)
3
+ * Copyright 2011-2024 The Bootstrap Authors
4
+ * Licensed under the Creative Commons Attribution 3.0 Unported License.
5
+ */
6
+
7
+ (() => {
8
+ 'use strict'
9
+
10
+ const getStoredTheme = () => localStorage.getItem('theme')
11
+ const setStoredTheme = theme => localStorage.setItem('theme', theme)
12
+
13
+ const getPreferredTheme = () => {
14
+ const storedTheme = getStoredTheme()
15
+ if (storedTheme) {
16
+ return storedTheme
17
+ }
18
+
19
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
20
+ }
21
+
22
+ const setTheme = theme => {
23
+ if (theme === 'auto') {
24
+ document.documentElement.setAttribute('data-bs-theme', (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'))
25
+ } else {
26
+ document.documentElement.setAttribute('data-bs-theme', theme)
27
+ }
28
+ }
29
+
30
+ setTheme(getPreferredTheme())
31
+
32
+ const showActiveTheme = (theme, focus = false) => {
33
+ const themeSwitcher = document.querySelector('#bd-theme')
34
+
35
+ if (!themeSwitcher) {
36
+ return
37
+ }
38
+
39
+ const themeSwitcherText = document.querySelector('#bd-theme-text')
40
+ const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`)
41
+
42
+ document.querySelectorAll('[data-bs-theme-value]').forEach(element => {
43
+ element.classList.remove('active')
44
+ element.setAttribute('aria-pressed', 'false')
45
+ })
46
+
47
+ btnToActive.classList.add('active')
48
+ btnToActive.setAttribute('aria-pressed', 'true')
49
+ const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})`
50
+ themeSwitcher.setAttribute('aria-label', themeSwitcherLabel)
51
+
52
+ if (focus) {
53
+ themeSwitcher.focus()
54
+ }
55
+ }
56
+
57
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
58
+ const storedTheme = getStoredTheme()
59
+ if (storedTheme !== 'light' && storedTheme !== 'dark') {
60
+ setTheme(getPreferredTheme())
61
+ }
62
+ })
63
+
64
+ window.addEventListener('DOMContentLoaded', () => {
65
+ showActiveTheme(getPreferredTheme())
66
+
67
+ document.querySelectorAll('[data-bs-theme-value]')
68
+ .forEach(toggle => {
69
+ toggle.addEventListener('click', () => {
70
+ const theme = toggle.getAttribute('data-bs-theme-value')
71
+ setStoredTheme(theme)
72
+ setTheme(theme)
73
+ showActiveTheme(theme, true)
74
+ })
75
+ })
76
+ })
77
+ })()
78
+
File without changes
@@ -0,0 +1,33 @@
1
+ from flask import Blueprint
2
+ from flask import redirect
3
+ from flask import render_template
4
+ from flask import request
5
+
6
+ from .controller import StravaController
7
+
8
+
9
+ def make_strava_blueprint(host: str, port: int) -> Blueprint:
10
+ strava_controller = StravaController(host, port)
11
+ blueprint = Blueprint("strava", __name__, template_folder="templates")
12
+
13
+ @blueprint.route("/setup")
14
+ def setup():
15
+ return render_template(
16
+ "strava/client-id.html.j2", **strava_controller.set_client_id()
17
+ )
18
+
19
+ @blueprint.route("/post-client-id", methods=["POST"])
20
+ def post_client_id():
21
+ client_id = request.form["client_id"]
22
+ client_secret = request.form["client_secret"]
23
+ url = strava_controller.save_client_id(client_id, client_secret)
24
+ return redirect(url)
25
+
26
+ @blueprint.route("/callback")
27
+ def strava_callback():
28
+ code = request.args.get("code", type=str)
29
+ return render_template(
30
+ "strava/connected.html.j2", **strava_controller.save_code(code)
31
+ )
32
+
33
+ return blueprint
@@ -0,0 +1,49 @@
1
+ import json
2
+ import urllib.parse
3
+ from typing import Optional
4
+
5
+ from geo_activity_playground.core.paths import strava_dynamic_config_path
6
+
7
+
8
+ class StravaController:
9
+ def __init__(self, host: str, port: int) -> None:
10
+ self._host = host
11
+ self._port = port
12
+
13
+ self._client_secret: Optional[str] = None
14
+
15
+ def set_client_id(self) -> dict:
16
+ return {"host": self._host, "port": self._port}
17
+
18
+ def save_client_id(self, client_id: str, client_secret: str) -> str:
19
+ self._client_id = client_id
20
+ self._client_secret = client_secret
21
+
22
+ payload = {
23
+ "client_id": client_id,
24
+ "redirect_uri": f"http://{self._host}:{self._port}/strava/callback",
25
+ "response_type": "code",
26
+ "scope": "activity:read_all",
27
+ }
28
+
29
+ arg_string = "&".join(
30
+ f"{key}={urllib.parse.quote(value)}" for key, value in payload.items()
31
+ )
32
+ return f"https://www.strava.com/oauth/authorize?{arg_string}"
33
+
34
+ def save_code(self, code: str) -> dict:
35
+ self._code = code
36
+
37
+ with open(strava_dynamic_config_path(), "w") as f:
38
+ json.dump(
39
+ {
40
+ "client_id": self._client_id,
41
+ "client_secret": self._client_secret,
42
+ "code": self._code,
43
+ },
44
+ f,
45
+ indent=2,
46
+ sort_keys=True,
47
+ )
48
+
49
+ return {}
@@ -0,0 +1,36 @@
1
+ {% extends "page.html.j2" %}
2
+
3
+ {% block container %}
4
+
5
+ <h1 class="mb-3">Strava Connect</h1>
6
+
7
+ <div class="row mb-3">
8
+ <div class="col-md-6">
9
+ <p>You can connect to Strava such that it automatically downloads your activities.</p>
10
+
11
+ <p>The Strava API has rather strict rate limiting. If you don't mind waiting a bit, you can just use the "client
12
+ ID" and "client secret" that are provided to use a pre-defined Strava app. In that case, just click on the
13
+ button. If you don't want to share the API limits with other people, go the <a
14
+ href="https://www.strava.com/settings/api">Strava API page</a> and create your own app. Then fill in
15
+ "client ID" and "client secret" in the form.</p>
16
+ </div>
17
+
18
+ <div class="col-md-6">
19
+ <form action="/strava/post-client-id" method="POST">
20
+ <div class="mb-3">
21
+ <label for="client_id" class="form-label">Client ID</label>
22
+ <input type="text" class="form-control" id="client_id" name="client_id" value="131693" />
23
+ </div>
24
+ <div class="mb-3">
25
+ <label for="client_secret" class="form-label">Client Secret</label>
26
+ <input type="text" class="form-control" id="client_secret" name="client_secret"
27
+ value="0ccc0100a2c218512a7ef0cea3b0e322fb4b4365" />
28
+ </div>
29
+
30
+ <input type="submit" value="Connect to Strava" />
31
+ </form>
32
+ </div>
33
+ </div>
34
+
35
+
36
+ {% endblock %}
@@ -0,0 +1,14 @@
1
+ {% extends "page.html.j2" %}
2
+
3
+ {% block container %}
4
+
5
+ <h1 class="mb-3">Strava Connect</h1>
6
+
7
+ <div class="row mb-3">
8
+ <div class="col">
9
+ <p>You're now connected to Strava. Please restart the webserver to start loading actvities.</p>
10
+ </div>
11
+ </div>
12
+
13
+
14
+ {% endblock %}
@@ -4,6 +4,7 @@
4
4
  <div class="row mb-3">
5
5
  </div>
6
6
 
7
+ {% if latest_activities %}
7
8
  <div class="row mb-3">
8
9
  <div class="col-md-9">
9
10
  <h2>Last 30 days</h2>
@@ -63,5 +64,9 @@
63
64
  {% endfor %}
64
65
  </div>
65
66
  {% endfor %}
67
+ {% else %}
68
+ <p>You don't have activities yet. Either put some files into a directory <tt>Activities</tt> or <a
69
+ href="{{ url_for('strava.setup') }}">set up Strava API</a>.</p>
70
+ {% endif %}
66
71
 
67
72
  {% endblock %}
@@ -33,6 +33,9 @@
33
33
  <script src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
34
34
 
35
35
 
36
+ <script src="/static/bootstrap-dark-mode.js"></script>
37
+
38
+
36
39
  <!-- Favicon. -->
37
40
  <link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png">
38
41
  <link rel="icon" type="image/png" sizes="32x32" href="/static/favicon-32x32.png">
@@ -59,36 +62,65 @@
59
62
  <ul class="navbar-nav me-auto mb-2 mb-lg-0">
60
63
 
61
64
  <li class="nav-item">
62
- <a class="nav-link active" aria-current="page"
63
- href="{{ url_for('summary.index') }}">Summary</a>
65
+ <a class="nav-link" aria-current="page" href="{{ url_for('summary.index') }}">Summary</a>
64
66
  </li>
65
67
  <li class="nav-item">
66
- <a class="nav-link active" aria-current="page"
67
- href="{{ url_for('calendar.index') }}">Calendar</a>
68
+ <a class="nav-link" aria-current="page" href="{{ url_for('calendar.index') }}">Calendar</a>
68
69
  </li>
69
70
  <li class="nav-item">
70
- <a class="nav-link active" aria-current="page"
71
+ <a class="nav-link" aria-current="page"
71
72
  href="{{ url_for('explorer.map', zoom=14) }}">Explorer</a>
72
73
  </li>
73
74
  <li class="nav-item">
74
- <a class="nav-link active" aria-current="page"
75
+ <a class="nav-link" aria-current="page"
75
76
  href="{{ url_for('explorer.map', zoom=17) }}">Squadratinhos</a>
76
77
  </li>
77
78
  <li class="nav-item">
78
- <a class="nav-link active" aria-current="page"
79
- href="{{ url_for('heatmap.index') }}">Heatmap</a>
79
+ <a class="nav-link" aria-current="page" href="{{ url_for('heatmap.index') }}">Heatmap</a>
80
80
  </li>
81
81
  <li class="nav-item">
82
- <a class="nav-link active" aria-current="page"
82
+ <a class="nav-link" aria-current="page"
83
83
  href="{{ url_for('eddington.index') }}">Eddington</a>
84
84
  </li>
85
85
  <li class="nav-item">
86
- <a class="nav-link active" aria-current="page"
86
+ <a class="nav-link" aria-current="page"
87
87
  href="{{ url_for('equipment.index') }}">Equipment</a>
88
88
  </li>
89
89
  <li class="nav-item">
90
- <a class="nav-link active" aria-current="page"
91
- href="{{ url_for('upload.index') }}">Upload</a>
90
+ <a class="nav-link" aria-current="page" href="{{ url_for('upload.index') }}">Upload</a>
91
+ </li>
92
+ <li class="nav-item">
93
+ <a class="nav-link" aria-current="page" href="{{ url_for('settings') }}">Settings</a>
94
+ </li>
95
+
96
+
97
+ <li class="nav-item dropdown">
98
+ <button
99
+ class="btn btn-link nav-link py-2 px-0 px-lg-2 dropdown-toggle d-flex align-items-center"
100
+ id="bd-theme" type="button" aria-expanded="false" data-bs-toggle="dropdown"
101
+ data-bs-display="static" aria-label="Toggle theme (dark)">
102
+ <span id="bd-theme-text">Theme</span>
103
+ </button>
104
+ <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="bd-theme-text">
105
+ <li>
106
+ <button type="button" class="dropdown-item d-flex align-items-center"
107
+ data-bs-theme-value="light" aria-pressed="false">
108
+ ☀️ Light
109
+ </button>
110
+ </li>
111
+ <li>
112
+ <button type="button" class="dropdown-item d-flex align-items-center active"
113
+ data-bs-theme-value="dark" aria-pressed="true">
114
+ 🌙 Dark
115
+ </button>
116
+ </li>
117
+ <li>
118
+ <button type="button" class="dropdown-item d-flex align-items-center"
119
+ data-bs-theme-value="auto" aria-pressed="false">
120
+ ⚙️ Auto
121
+ </button>
122
+ </li>
123
+ </ul>
92
124
  </li>
93
125
  </ul>
94
126
  </div>
@@ -0,0 +1,24 @@
1
+ {% extends "page.html.j2" %}
2
+
3
+ {% block container %}
4
+ <div class="row mb-3">
5
+ <div class="col">
6
+ <h1>Settings</h1>
7
+ </div>
8
+ </div>
9
+
10
+ <div class="row mb-3">
11
+ <div class="row row-cols-1 row-cols-md-3 g-2">
12
+ <div class="col">
13
+ <div class="card">
14
+ <div class="card-body">
15
+ <h5 class="card-title">Strava API</h5>
16
+ <p class="card-text">Connect to the Strava API to download activities from there.</p>
17
+ <a href="{{ url_for('strava.setup') }}" class="btn btn-primary">Set up Strava API</a>
18
+ </div>
19
+ </div>
20
+ </div>
21
+
22
+ </div>
23
+ </div>
24
+ {% endblock %}
@@ -10,7 +10,9 @@ from flask import Response
10
10
  from werkzeug.utils import secure_filename
11
11
 
12
12
  from geo_activity_playground.core.activities import ActivityRepository
13
- from geo_activity_playground.core.activities import embellish_time_series
13
+ from geo_activity_playground.core.activities import build_activity_meta
14
+ from geo_activity_playground.core.enrichment import enrich_activities
15
+ from geo_activity_playground.core.paths import _strava_dynamic_config_path
14
16
  from geo_activity_playground.explorer.tile_visits import compute_tile_evolution
15
17
  from geo_activity_playground.explorer.tile_visits import compute_tile_visits
16
18
  from geo_activity_playground.explorer.tile_visits import TileVisitAccessor
@@ -94,22 +96,16 @@ def scan_for_activities(
94
96
  skip_strava: bool = False,
95
97
  ) -> None:
96
98
  if pathlib.Path("Activities").exists():
97
- import_from_directory(
98
- repository,
99
- config.get("kind", {}),
100
- config.get("metadata_extraction_regexes", []),
101
- )
99
+ import_from_directory(config.get("metadata_extraction_regexes", []))
102
100
  if pathlib.Path("Strava Export").exists():
103
- import_from_strava_checkout(repository)
104
- if "strava" in config and not skip_strava:
105
- import_from_strava_api(repository)
101
+ import_from_strava_checkout()
102
+ if (_strava_dynamic_config_path.exists() or "strava" in config) and not skip_strava:
103
+ import_from_strava_api()
106
104
 
107
- if len(repository) == 0:
108
- logger.error(
109
- f"No activities found. You need to either add activity files (GPX, FIT, …) to {pathlib.Path('Activities')} or set up the Strava API. Starting without any activities is unfortunately not supported."
110
- )
111
- sys.exit(1)
105
+ enrich_activities(config.get("kind", {}))
106
+ build_activity_meta()
107
+ repository.reload()
112
108
 
113
- embellish_time_series(repository)
114
- compute_tile_visits(repository, tile_visit_accessor)
115
- compute_tile_evolution(tile_visit_accessor)
109
+ if len(repository) > 0:
110
+ compute_tile_visits(repository, tile_visit_accessor)
111
+ compute_tile_evolution(tile_visit_accessor)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: geo-activity-playground
3
- Version: 0.24.2
3
+ Version: 0.26.0
4
4
  Summary: Analysis of geo data activities like rides, runs or hikes.
5
5
  License: MIT
6
6
  Author: Martin Ueding
@@ -1,39 +1,39 @@
1
1
  geo_activity_playground/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- geo_activity_playground/__main__.py,sha256=pAl5_cWjuNyGmE_zMoPtG7Gc7r7lNk3psn9CAxzP2fU,4131
2
+ geo_activity_playground/__main__.py,sha256=NQDFaa1BSIdare1oIdYO0cESf0kPVnEIE3J24jlsX_E,4021
3
3
  geo_activity_playground/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- geo_activity_playground/core/activities.py,sha256=giZYepqGK3NujGpcCrYvF0rYDyVMopIMdXrvWBvbRm4,11606
5
- geo_activity_playground/core/activity_parsers.py,sha256=pjYjnaAXsWfUNOoomYWOmmFZO5Qs076RWM-pqNdo7BA,10905
6
- geo_activity_playground/core/cache_migrations.py,sha256=tkGA0h69-m-0Ek7JjvfVOX45YXLXkh_ZvC2TKB_fvbA,4281
4
+ geo_activity_playground/core/activities.py,sha256=I1lsn1gO_P9eHbZCZlkCO6Lvi5sqXq_-c-1aDv6uKMs,8081
7
5
  geo_activity_playground/core/config.py,sha256=YjqCiEmIAa-GM1-JfBctMEsl8-I56pZyyDdTyPduOzw,477
8
6
  geo_activity_playground/core/coordinates.py,sha256=tDfr9mlXhK6E_MMIJ0vYWVCoH0Lq8uyuaqUgaa8i0jg,966
7
+ geo_activity_playground/core/enrichment.py,sha256=wRVhrUCsvs2CcVhN-BQwxk_W_0E0AFp1_hncGzWNNvo,6435
9
8
  geo_activity_playground/core/heatmap.py,sha256=bRLQHzmTEsQbX8XWeg85x_lRGk272UoYRiCnoxZ5da0,4189
10
- geo_activity_playground/core/paths.py,sha256=EX4yQQyAmWRxTYS0QvFCbGz9v1J0z_1QYfd5hjTmaRw,808
9
+ geo_activity_playground/core/paths.py,sha256=ohJN_xNLh303uc0ATpL4iH01xANIE86pILRaQi4e-RQ,1893
11
10
  geo_activity_playground/core/privacy_zones.py,sha256=4TumHsVUN1uW6RG3ArqTXDykPVipF98DCxVBe7YNdO8,512
12
11
  geo_activity_playground/core/similarity.py,sha256=Jo8jRViuORCxdIGvyaflgsQhwu9S_jn10a450FRL18A,3159
13
- geo_activity_playground/core/tasks.py,sha256=lcfLeYpla819v2BTf53zvT0xEB4I6OpSfqfF_xlsLwM,1817
12
+ geo_activity_playground/core/tasks.py,sha256=22rIidyRR0eDqz9T7swJJSl7I96OeZ-T6xFQkh1-RsQ,2521
14
13
  geo_activity_playground/core/test_tiles.py,sha256=zce1FxNfsSpOQt66jMehdQRVoNdl-oiFydx6iVBHZXM,764
15
14
  geo_activity_playground/core/test_time_conversion.py,sha256=Sh6nZA3uCTOdZTZa3yOijtR0m74QtZu2mcWXsDNnyQI,984
16
15
  geo_activity_playground/core/tiles.py,sha256=VxPu9vdfKnxDxaYo5JSYmka9Dt3TDxg0zo3cYVJXVHc,3359
17
16
  geo_activity_playground/core/time_conversion.py,sha256=9J6aTlqJhWvsknQkoECNL-CIG-8BKs6ZatJJ9XJnTsg,367
18
17
  geo_activity_playground/explorer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
18
  geo_activity_playground/explorer/grid_file.py,sha256=k6j6KBEk2a2BY-onE8SV5TJsERGGyOrlY4as__meWpA,3304
20
- geo_activity_playground/explorer/tile_visits.py,sha256=fkn2Pgk-pBmC0ywDa0g2T9f4L2sdi5GaqGMbs4tow_g,12157
19
+ geo_activity_playground/explorer/tile_visits.py,sha256=CLZ8bCUKc5YoxvNa23hGF9JtlinIl9Wbk9u2xN4bJpw,13457
21
20
  geo_activity_playground/explorer/video.py,sha256=ROAmV9shfJyqTgnXVD41KFORiwnRgVpEWenIq4hMCRM,4389
22
21
  geo_activity_playground/importers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
- geo_activity_playground/importers/directory.py,sha256=6KUxMMJELq1WWQSbqAWt6ZVlZh7Np3LMJNkt6jPN5eI,4506
24
- geo_activity_playground/importers/strava_api.py,sha256=FJYamDBmHmB8uA9dNc0h0j7EgZPCofCspL2Wfq-PrTg,7869
25
- geo_activity_playground/importers/strava_checkout.py,sha256=nvZX7skevqMWdW9Gxzt4VhwCaI3wv2z5g-ODv6ZWxng,8078
22
+ geo_activity_playground/importers/activity_parsers.py,sha256=m2SpvGlTZ8F3gG6YB24_ZFrlOAbtqbfWi-GIYspeUco,10593
23
+ geo_activity_playground/importers/directory.py,sha256=YTYuroH-oYviL2a9B7No-6Fx4RrkhMqf60Yh-7dGats,5384
24
+ geo_activity_playground/importers/strava_api.py,sha256=ihy_ezq_gYUy1V4jbtg41N6K0ilYVOEiPZE07lSiUHU,8444
25
+ geo_activity_playground/importers/strava_checkout.py,sha256=mUKfCzBk0sMF6WEmAGelnwqGnonVO1UW_uZVAtxDJVQ,8576
26
26
  geo_activity_playground/importers/test_directory.py,sha256=ljXokx7q0OgtHvEdHftcQYEmZJUDVv3OOF5opklxdT4,724
27
27
  geo_activity_playground/importers/test_strava_api.py,sha256=4vX7wDr1a9aRh8myxNrIq6RwDBbP8ZeoXXPc10CAbW4,431
28
28
  geo_activity_playground/webui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
29
  geo_activity_playground/webui/activity/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
30
  geo_activity_playground/webui/activity/blueprint.py,sha256=gGMes5ycXi2rMixVYs4NQgzTU1Vttgu50prYAe3--Hs,1749
31
- geo_activity_playground/webui/activity/controller.py,sha256=CoNi9dJlGS-pZMEh14eZqWkbbLRHvQ01PEk8jRXpmgU,16665
31
+ geo_activity_playground/webui/activity/controller.py,sha256=BSmHwO2GLyMGiFRQ0BpTsHMKDWJbz5UHnwQ9HVULN4c,16679
32
32
  geo_activity_playground/webui/activity/templates/activity/day.html.j2,sha256=r3qKl9uTzOko4R-ZzyYAZt1j61JSevYP4g0Yi06HHPg,2702
33
33
  geo_activity_playground/webui/activity/templates/activity/lines.html.j2,sha256=5gB1aDjRgi_RventenRfC10_FtMT4ch_VuWvA9AMlBY,1121
34
34
  geo_activity_playground/webui/activity/templates/activity/name.html.j2,sha256=RDLEt6ip8_ngmdLgaC5jg92Dk-F2umGwKkd8cWmvVko,2400
35
- geo_activity_playground/webui/activity/templates/activity/show.html.j2,sha256=40eEbOypq0ZzebPgUXZy6fWjcGuMtcF_IFMNvc1_CXY,5148
36
- geo_activity_playground/webui/app.py,sha256=MCkWkeBTLXyqIE-GLhgGRsQYAtUPf9mUh6csbkoOvw8,4575
35
+ geo_activity_playground/webui/activity/templates/activity/show.html.j2,sha256=NOsMPUBy1UQM_YebtT6RQxLSgSGvCMetqi_mspJmUjQ,5229
36
+ geo_activity_playground/webui/app.py,sha256=a6RcsPH-n1Ru6RybkAPCRENL9KnPvxfPBJI9IzsEB5U,3980
37
37
  geo_activity_playground/webui/calendar/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
38
  geo_activity_playground/webui/calendar/blueprint.py,sha256=rlnhgU2DWAcdLMRq7m77NzrM_aDyp4s3kuuQHuzjHhg,782
39
39
  geo_activity_playground/webui/calendar/controller.py,sha256=QpSAkR2s1sbLSu6P_fNNTccgGglOzEH2PIv1XwKxeVY,2778
@@ -43,7 +43,7 @@ geo_activity_playground/webui/eddington/__init__.py,sha256=47DEQpj8HBSa-_TImW-5J
43
43
  geo_activity_playground/webui/eddington/blueprint.py,sha256=evIvueLfDWVTxJ9pRguqmZ9-Pybd2WmBRst_-7vX2QA,551
44
44
  geo_activity_playground/webui/eddington/controller.py,sha256=L4YssL088UMamp4qgzEtilzhaKGZsyfVUScuw-fmTjE,2888
45
45
  geo_activity_playground/webui/eddington/templates/eddington/index.html.j2,sha256=XHKeUymQMS5x00PLOVlg-nSRCz_jHB2pvD8QunULWJ4,1839
46
- geo_activity_playground/webui/entry_controller.py,sha256=v8twD3LJ0SoVtvzJoDKnU8dTtMZAKvhNU2AEH2Z87OA,1886
46
+ geo_activity_playground/webui/entry_controller.py,sha256=n9v4MriyL8kDR91LE9eeqc2tAvxyzFgoNMMXpr0qh4g,1906
47
47
  geo_activity_playground/webui/equipment/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
48
  geo_activity_playground/webui/equipment/blueprint.py,sha256=YbOAsjk05BMeUlD6wGfxE1amvlLsFjG4HMJn5SBrVRU,551
49
49
  geo_activity_playground/webui/equipment/controller.py,sha256=m1LNaGCNJnW97TlKT6x9NfQcMp6RKcZ8Q0I1FTUAvss,4037
@@ -54,7 +54,7 @@ geo_activity_playground/webui/explorer/controller.py,sha256=8d0FFrG55ZlPQ5seQC2D
54
54
  geo_activity_playground/webui/explorer/templates/explorer/index.html.j2,sha256=OxNt2_VwH-cmVXJrDNfeD-dNB9IrwBjdhhQEqkXxHj0,6529
55
55
  geo_activity_playground/webui/heatmap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
56
  geo_activity_playground/webui/heatmap/blueprint.py,sha256=bjQu-HL3QN5UpJ6tHOifhcLGlPr_hIKvaRu78md4JqM,1470
57
- geo_activity_playground/webui/heatmap/heatmap_controller.py,sha256=fM92NfeJ_qhXkuAT01Btkzeb_SbAgExxql_OH1G31os,6477
57
+ geo_activity_playground/webui/heatmap/heatmap_controller.py,sha256=i376qEqTJrhezGsdPyKR8VsRY2N0JdEra7_kY7NmwW8,6822
58
58
  geo_activity_playground/webui/heatmap/templates/heatmap/index.html.j2,sha256=YLeu6P4djl8G4qAXR6DhetseqrbOodN7aN4coocknc4,1875
59
59
  geo_activity_playground/webui/search_controller.py,sha256=PzMf7b8tiJKZIZoPvQ9A2hOrzoKV9cS3jq05w2fK94c,532
60
60
  geo_activity_playground/webui/square_planner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -65,6 +65,7 @@ geo_activity_playground/webui/static/android-chrome-192x192.png,sha256=yxZgo8Jw4
65
65
  geo_activity_playground/webui/static/android-chrome-384x384.png,sha256=bgeqAdyvDZBMch7rVi3qSawf0Zr4Go0EG8Ws_B8NApY,49297
66
66
  geo_activity_playground/webui/static/android-chrome-512x512.png,sha256=Uiv62gQpUjMOdp9d6exzd6IyOi5zgQdgjIVVWYw5m98,38891
67
67
  geo_activity_playground/webui/static/apple-touch-icon.png,sha256=NsVMubJtVzslhql4GZlwTWm4cNRowgcEOT53RuZV190,16549
68
+ geo_activity_playground/webui/static/bootstrap-dark-mode.js,sha256=XfyhxIFgjDc6aEj0kYgKpG5zjS5gvyhWCJmNfUG4HJY,2622
68
69
  geo_activity_playground/webui/static/browserconfig.xml,sha256=u5EvU6U1jKSDiXHW0i4rXrs0lT_tmS82otbJnvmccrk,253
69
70
  geo_activity_playground/webui/static/favicon-16x16.png,sha256=Yy8lRjGB7itDaeUI_l_Toq3OO0gzgPGnqIfM3CqcQy0,1419
70
71
  geo_activity_playground/webui/static/favicon-32x32.png,sha256=K52R99pLGgWHnjFPPkVieBm051Er-sTCiMvgLr9Alrg,2587
@@ -72,24 +73,28 @@ geo_activity_playground/webui/static/favicon.ico,sha256=uVNZlrF22zn1lcCS_9wAoWLh
72
73
  geo_activity_playground/webui/static/mstile-150x150.png,sha256=j1ANUQJ1Xi1DR2sGqYZztob2ypfGw04eNtGpN9SxExA,11964
73
74
  geo_activity_playground/webui/static/safari-pinned-tab.svg,sha256=OzoEVGY0igWRXM1NiM3SRKugdICBN7aB_XuxaC3Mu9Q,8371
74
75
  geo_activity_playground/webui/static/site.webmanifest,sha256=4vYxdPMpwTdB8EmOvHkkYcjZ8Yrci3pOwwY3o_VwACA,440
75
- geo_activity_playground/webui/strava_controller.py,sha256=-DZ1Ae-0cWx5tia2dJpGfsBBoIya0QO7IC2qa1-7Q_U,779
76
+ geo_activity_playground/webui/strava/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
+ geo_activity_playground/webui/strava/blueprint.py,sha256=Q2Wfxlg7bR2vjWZ3UpckPl9jWHwNB8hfP7SLBufgiUY,1055
78
+ geo_activity_playground/webui/strava/controller.py,sha256=Ye0R-nu5SwFdk2DAIEq7OE76XLgnRk9y8iJjLObbAUc,1437
79
+ geo_activity_playground/webui/strava/templates/strava/client-id.html.j2,sha256=m5gJk0VxcIn0VcROsOke5A5MbWXC5O1e1Lxc09MyN_g,1493
80
+ geo_activity_playground/webui/strava/templates/strava/connected.html.j2,sha256=TN6H8YPjG2zk8fodTaSaloTEJCXnpgTCIRX_uEVTiNI,274
76
81
  geo_activity_playground/webui/summary/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
82
  geo_activity_playground/webui/summary/blueprint.py,sha256=kzQ6MDOycQKfDcVoEUmL7HYHJA_gu8DlzVHwO37-_jA,514
78
83
  geo_activity_playground/webui/summary/controller.py,sha256=i6A9-S9j9Hm67FcZPzJpqMgxRVsXbDmJiVLGiRtZIqk,8805
79
84
  geo_activity_playground/webui/summary/templates/summary/index.html.j2,sha256=rsII1eMY-xNugh8A9SecnEcDZqkEOWYIfiHAGroQYuM,4442
80
- geo_activity_playground/webui/templates/home.html.j2,sha256=23MLQJ1k447NMLcgOGgTSNiPXFvdC2bQvACkDZbGo5k,2182
81
- geo_activity_playground/webui/templates/page.html.j2,sha256=aeTYem0CpOOPvWa5KzVKH1BiPJlF0LHqChL1IhpAUO0,7292
85
+ geo_activity_playground/webui/templates/home.html.j2,sha256=fp48MjBuO4QJfQz6YPOWH56IzStgaclx9XbwEKmUFHQ,2403
86
+ geo_activity_playground/webui/templates/page.html.j2,sha256=KwgUw7zOjWNVSZKM4BGWOaBbPhGZaaOXCvNV9pL-0R4,9160
82
87
  geo_activity_playground/webui/templates/search.html.j2,sha256=FvNRoDfUlSzXjM_tqZY_fDhuhUDgbPaY73q56gdvF1A,1130
83
- geo_activity_playground/webui/templates/strava-connect.html.j2,sha256=vLMqTnTV-DZJ1FHRjpm4OMgbABMwZQvbs8Ru9baKeBg,1111
88
+ geo_activity_playground/webui/templates/settings.html.j2,sha256=-q9GflrG2t6kSt-NerBN5NV1gRp12JT3KgGp_aPaKT8,674
84
89
  geo_activity_playground/webui/tile/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
90
  geo_activity_playground/webui/tile/blueprint.py,sha256=cK0o2Z3BrLycgF9zw0F8s9qF-JaYDbF5Gog-GXDtUZ8,943
86
91
  geo_activity_playground/webui/tile/controller.py,sha256=PISh4vKs27b-LxFfTARtr5RAwHFresA1Kw1MDcERSRU,1221
87
92
  geo_activity_playground/webui/upload/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
93
  geo_activity_playground/webui/upload/blueprint.py,sha256=2l5q1LYtjgLF_3CAyPvCLx-wMebp14OJvgZBzGILdmQ,801
89
- geo_activity_playground/webui/upload/controller.py,sha256=QrHfqRxroLuzBBNNSheUx-qJ9ww1sXT1oPdpS7Jx_ro,4092
94
+ geo_activity_playground/webui/upload/controller.py,sha256=BWcSlLy_fVAodDlnXkxE4eIotC_UHrjldACrToA-iAQ,3975
90
95
  geo_activity_playground/webui/upload/templates/upload/index.html.j2,sha256=hfXkEXaz_MkM46rU56423D6V6WtK-EAXPszSY-_-Tx8,1471
91
- geo_activity_playground-0.24.2.dist-info/LICENSE,sha256=4RpAwKO8bPkfXH2lnpeUW0eLkNWglyG4lbrLDU_MOwY,1070
92
- geo_activity_playground-0.24.2.dist-info/METADATA,sha256=Rele9lf2wXKFJkEXlZYiRY8AMzjTwwN9RBvYY9ieAkc,1665
93
- geo_activity_playground-0.24.2.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
94
- geo_activity_playground-0.24.2.dist-info/entry_points.txt,sha256=pbNlLI6IIZIp7nPYCfAtiSiz2oxJSCl7DODD6SPkLKk,81
95
- geo_activity_playground-0.24.2.dist-info/RECORD,,
96
+ geo_activity_playground-0.26.0.dist-info/LICENSE,sha256=4RpAwKO8bPkfXH2lnpeUW0eLkNWglyG4lbrLDU_MOwY,1070
97
+ geo_activity_playground-0.26.0.dist-info/METADATA,sha256=dFqZ5yCNUgsbZMxW55cZaI4Q_tbft6ifhGoaGgmImvc,1665
98
+ geo_activity_playground-0.26.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
99
+ geo_activity_playground-0.26.0.dist-info/entry_points.txt,sha256=pbNlLI6IIZIp7nPYCfAtiSiz2oxJSCl7DODD6SPkLKk,81
100
+ geo_activity_playground-0.26.0.dist-info/RECORD,,