geo-activity-playground 0.22.0__py3-none-any.whl → 0.24.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 (77) hide show
  1. geo_activity_playground/__main__.py +1 -1
  2. geo_activity_playground/core/activities.py +16 -9
  3. geo_activity_playground/core/activity_parsers.py +17 -32
  4. geo_activity_playground/core/cache_migrations.py +24 -0
  5. geo_activity_playground/core/heatmap.py +21 -21
  6. geo_activity_playground/core/privacy_zones.py +16 -0
  7. geo_activity_playground/core/similarity.py +1 -1
  8. geo_activity_playground/core/test_time_conversion.py +37 -0
  9. geo_activity_playground/core/time_conversion.py +14 -0
  10. geo_activity_playground/explorer/tile_visits.py +44 -27
  11. geo_activity_playground/importers/__init__.py +0 -0
  12. geo_activity_playground/importers/directory.py +7 -2
  13. geo_activity_playground/importers/strava_api.py +6 -0
  14. geo_activity_playground/importers/strava_checkout.py +12 -3
  15. geo_activity_playground/webui/__init__.py +0 -0
  16. geo_activity_playground/webui/activity/__init__.py +0 -0
  17. geo_activity_playground/webui/activity/blueprint.py +58 -0
  18. geo_activity_playground/webui/{activity_controller.py → activity/controller.py} +128 -18
  19. geo_activity_playground/webui/{templates/activity-day.html.j2 → activity/templates/activity/day.html.j2} +14 -2
  20. geo_activity_playground/webui/{templates/activity-name.html.j2 → activity/templates/activity/name.html.j2} +1 -1
  21. geo_activity_playground/webui/{templates/activity.html.j2 → activity/templates/activity/show.html.j2} +9 -4
  22. geo_activity_playground/webui/app.py +68 -281
  23. geo_activity_playground/webui/calendar/__init__.py +0 -0
  24. geo_activity_playground/webui/calendar/blueprint.py +26 -0
  25. geo_activity_playground/webui/{calendar_controller.py → calendar/controller.py} +5 -5
  26. geo_activity_playground/webui/{templates/calendar.html.j2 → calendar/templates/calendar/index.html.j2} +3 -2
  27. geo_activity_playground/webui/{templates/calendar-month.html.j2 → calendar/templates/calendar/month.html.j2} +2 -2
  28. geo_activity_playground/webui/eddington/__init__.py +0 -0
  29. geo_activity_playground/webui/eddington/blueprint.py +19 -0
  30. geo_activity_playground/webui/{eddington_controller.py → eddington/controller.py} +14 -6
  31. geo_activity_playground/webui/eddington/templates/eddington/index.html.j2 +56 -0
  32. geo_activity_playground/webui/entry_controller.py +4 -2
  33. geo_activity_playground/webui/equipment/__init__.py +0 -0
  34. geo_activity_playground/webui/equipment/blueprint.py +19 -0
  35. geo_activity_playground/webui/{equipment_controller.py → equipment/controller.py} +5 -3
  36. geo_activity_playground/webui/explorer/__init__.py +0 -0
  37. geo_activity_playground/webui/explorer/blueprint.py +54 -0
  38. geo_activity_playground/webui/{explorer_controller.py → explorer/controller.py} +6 -2
  39. geo_activity_playground/webui/{templates/explorer.html.j2 → explorer/templates/explorer/index.html.j2} +2 -2
  40. geo_activity_playground/webui/heatmap/__init__.py +0 -0
  41. geo_activity_playground/webui/heatmap/blueprint.py +41 -0
  42. geo_activity_playground/webui/{heatmap_controller.py → heatmap/heatmap_controller.py} +36 -13
  43. geo_activity_playground/webui/{templates/heatmap.html.j2 → heatmap/templates/heatmap/index.html.j2} +17 -2
  44. geo_activity_playground/webui/search_controller.py +1 -9
  45. geo_activity_playground/webui/square_planner/__init__.py +0 -0
  46. geo_activity_playground/webui/square_planner/blueprint.py +38 -0
  47. geo_activity_playground/webui/summary/__init__.py +0 -0
  48. geo_activity_playground/webui/summary/blueprint.py +16 -0
  49. geo_activity_playground/webui/summary/controller.py +268 -0
  50. geo_activity_playground/webui/summary/templates/summary/index.html.j2 +135 -0
  51. geo_activity_playground/webui/templates/{index.html.j2 → home.html.j2} +1 -1
  52. geo_activity_playground/webui/templates/page.html.j2 +32 -19
  53. geo_activity_playground/webui/templates/search.html.j2 +1 -1
  54. geo_activity_playground/webui/tile/__init__.py +0 -0
  55. geo_activity_playground/webui/tile/blueprint.py +31 -0
  56. geo_activity_playground/webui/upload/__init__.py +0 -0
  57. geo_activity_playground/webui/upload/blueprint.py +28 -0
  58. geo_activity_playground/webui/{upload_controller.py → upload/controller.py} +15 -6
  59. geo_activity_playground/webui/{templates/upload.html.j2 → upload/templates/upload/index.html.j2} +12 -11
  60. {geo_activity_playground-0.22.0.dist-info → geo_activity_playground-0.24.0.dist-info}/METADATA +2 -1
  61. geo_activity_playground-0.24.0.dist-info/RECORD +95 -0
  62. geo_activity_playground/webui/config_controller.py +0 -12
  63. geo_activity_playground/webui/locations_controller.py +0 -28
  64. geo_activity_playground/webui/summary_controller.py +0 -60
  65. geo_activity_playground/webui/templates/config.html.j2 +0 -24
  66. geo_activity_playground/webui/templates/eddington.html.j2 +0 -18
  67. geo_activity_playground/webui/templates/locations.html.j2 +0 -38
  68. geo_activity_playground/webui/templates/summary.html.j2 +0 -21
  69. geo_activity_playground-0.22.0.dist-info/RECORD +0 -74
  70. /geo_activity_playground/webui/{templates/activity-lines.html.j2 → activity/templates/activity/lines.html.j2} +0 -0
  71. /geo_activity_playground/webui/{templates/equipment.html.j2 → equipment/templates/equipment/index.html.j2} +0 -0
  72. /geo_activity_playground/webui/{square_planner_controller.py → square_planner/controller.py} +0 -0
  73. /geo_activity_playground/webui/{templates/square-planner.html.j2 → square_planner/templates/square_planner/index.html.j2} +0 -0
  74. /geo_activity_playground/webui/{tile_controller.py → tile/controller.py} +0 -0
  75. {geo_activity_playground-0.22.0.dist-info → geo_activity_playground-0.24.0.dist-info}/LICENSE +0 -0
  76. {geo_activity_playground-0.22.0.dist-info → geo_activity_playground-0.24.0.dist-info}/WHEEL +0 -0
  77. {geo_activity_playground-0.22.0.dist-info → geo_activity_playground-0.24.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,135 @@
1
+ {% extends "page.html.j2" %}
2
+
3
+ {% block container %}
4
+
5
+
6
+ <h1>Summary Statistics</h1>
7
+
8
+ <h2>Distances</h2>
9
+
10
+ <p>This is your weekly distance for the past rolling year, split by activity kind.</p>
11
+
12
+ <div class="row mb-3">
13
+ <div class="col">
14
+ {{ vega_direct("plot_weekly_distance", plot_weekly_distance) }}
15
+ </div>
16
+ </div>
17
+
18
+ <p>Similarly, you find the same data but aggregated by whole months for the past two years. Also split by activity kind.
19
+ </p>
20
+
21
+ <div class="row mb-3">
22
+ <div class="col">
23
+ {{ vega_direct("plot_monthly_distance", plot_monthly_distance) }}
24
+ </div>
25
+ </div>
26
+
27
+
28
+ <p>How much distance have you covered in each year up to that time of the year? The first plot gives you each year as
29
+ one line. The second plot shows you how distance you have covered in each year and with which activity kinds.</p>
30
+
31
+ <div class="row mb-3">
32
+ <div class="col-md-6">
33
+ {{ vega_direct("plot_year_cumulative", plot_year_cumulative) }}
34
+ </div>
35
+ <div class="col-md-6">
36
+ {{ vega_direct("plot_yearly_distance", plot_yearly_distance) }}
37
+ </div>
38
+ </div>
39
+
40
+ <div class="row mb-3">
41
+ <div class="col-md-4">
42
+ <p>What's the average distance per activity? This naturally differs per activity and perhaps also varies with
43
+ the years. This table gives you all the mean values.</p>
44
+ </div>
45
+
46
+ <div class="col-md-8">
47
+ <table class="table">
48
+ <thead>
49
+ <tr>
50
+ <th style="text-align: right;">Year</th>
51
+ {% for col in tabulate_year_kind_mean['columns'][1:] %}
52
+ <th style="text-align: right;">{{ col }}</th>
53
+ {% endfor %}
54
+ </tr>
55
+ </thead>
56
+ <tbody>
57
+ {% for row in tabulate_year_kind_mean['data'] %}
58
+ <tr>
59
+ {% for value in row %}
60
+ <td align="right">
61
+ {% if value == value %}
62
+ {{ value | round(2) }}
63
+ {% if not loop.first %}
64
+ km
65
+ {% endif %}
66
+ {% endif %}
67
+ </td>
68
+ {% endfor %}
69
+ </tr>
70
+ {% endfor %}
71
+ </tbody>
72
+ </table>
73
+ </div>
74
+ </div>
75
+
76
+ <p>Next we take one row per month, each column is a day-of-month. The brighter a box, the more distance you have covered
77
+ on that day. This makes it easy to spot those days where you really covered a lot of distance!</p>
78
+
79
+ <div class="row mb-3">
80
+ <div class="col">
81
+ {{ vega_direct("plot_distance_heatmap", plot_distance_heatmap) }}
82
+ </div>
83
+ </div>
84
+
85
+
86
+ <h2>Hall of Fame</h2>
87
+
88
+ <script>
89
+ function add_map(id, geojson) {
90
+ let map = L.map(`map-${id}`, {
91
+ fullscreenControl: true
92
+ })
93
+ L.tileLayer('/tile/color/{z}/{x}/{y}.png', {
94
+ maxZoom: 19,
95
+ attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
96
+ }).addTo(map)
97
+
98
+ let geojson_layer = L.geoJSON(geojson).addTo(map)
99
+ map.fitBounds(geojson_layer.getBounds())
100
+ return map
101
+ }
102
+ </script>
103
+
104
+ {% for activity_batch in nominations|batch(3) %}
105
+ <div class="row row-cols-1 row-cols-md-3 g-4 mb-3">
106
+ {% for activity, reasons, line_geojson in activity_batch %}
107
+ <div class="col">
108
+ <div class="card">
109
+ <div class="card-img-top" id="map-{{ activity.id }}" style="height: 200px; width: 100%;"></div>
110
+ <script>
111
+ let map{{ activity.id }} = add_map("{{ activity.id }}", {{ line_geojson | safe }})
112
+ </script>
113
+ <div class="card-body">
114
+ <a href="{{ url_for('activity.show', id=activity.id) }}">
115
+ <h5 class="card-title">{{ activity["name"] }}</h5>
116
+ </a>
117
+ <p class="card-text">
118
+ <ul style='list-style-type: "🏆 "'>
119
+ {% for reason in reasons %}
120
+ <li>{{ reason }}</li>
121
+ {% endfor %}
122
+ </ul>
123
+ </p>
124
+ <p class="card-text"><small class="text-body-secondary"></small>{{ activity.kind }} with {{
125
+ (activity.distance_km)|round(1) }} km in {{
126
+ activity.elapsed_time }} on {{ activity.start }}</small></p>
127
+ </div>
128
+ </div>
129
+ </div>
130
+ {% endfor %}
131
+ </div>
132
+ {% endfor %}
133
+
134
+
135
+ {% endblock %}
@@ -51,7 +51,7 @@
51
51
  let map{{ elem.activity.id }} = add_map("{{ elem.activity.id }}", {{ elem.line_geojson | safe }})
52
52
  </script>
53
53
  <div class="card-body">
54
- <a href="/activity/{{ elem.activity.id }}">
54
+ <a href="{{ url_for('activity.show', id=elem.activity.id) }}">
55
55
  <h5 class="card-title">{{ elem.activity["name"] }}</h5>
56
56
  </a>
57
57
  <p class="card-text">{{ elem.activity.kind }} with {{ (elem.activity.distance_km)|round(1) }} km in {{
@@ -49,7 +49,7 @@
49
49
  <div class="container">
50
50
  <nav class="navbar navbar-expand-lg bg-body-secondary mb-3">
51
51
  <div class="container-fluid">
52
- <a class="navbar-brand" href="/">Geo Actvitity Playground</a>
52
+ <a class="navbar-brand" href="{{ url_for('index') }}">Geo Actvitity Playground</a>
53
53
  <button class="navbar-toggler" type="button" data-bs-toggle="collapse"
54
54
  data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
55
55
  aria-expanded="false" aria-label="Toggle navigation">
@@ -57,35 +57,38 @@
57
57
  </button>
58
58
  <div class="collapse navbar-collapse" id="navbarSupportedContent">
59
59
  <ul class="navbar-nav me-auto mb-2 mb-lg-0">
60
+
60
61
  <li class="nav-item">
61
- <a class="nav-link active" aria-current="page" href="/">Home</a>
62
- </li>
63
- <li class="nav-item">
64
- <a class="nav-link active" aria-current="page" href="/summary">Summary</a>
65
- </li>
66
- <li class="nav-item">
67
- <a class="nav-link active" aria-current="page" href="/calendar">Calendar</a>
62
+ <a class="nav-link active" aria-current="page"
63
+ href="{{ url_for('summary.index') }}">Summary</a>
68
64
  </li>
69
65
  <li class="nav-item">
70
- <a class="nav-link active" aria-current="page" href="/explorer/14">Explorer</a>
66
+ <a class="nav-link active" aria-current="page"
67
+ href="{{ url_for('calendar.index') }}">Calendar</a>
71
68
  </li>
72
69
  <li class="nav-item">
73
- <a class="nav-link active" aria-current="page" href="/explorer/17">Squadratinhos</a>
70
+ <a class="nav-link active" aria-current="page"
71
+ href="{{ url_for('explorer.map', zoom=14) }}">Explorer</a>
74
72
  </li>
75
73
  <li class="nav-item">
76
- <a class="nav-link active" aria-current="page" href="/heatmap">Heatmap</a>
74
+ <a class="nav-link active" aria-current="page"
75
+ href="{{ url_for('explorer.map', zoom=17) }}">Squadratinhos</a>
77
76
  </li>
78
77
  <li class="nav-item">
79
- <a class="nav-link active" aria-current="page" href="/eddington">Eddington</a>
78
+ <a class="nav-link active" aria-current="page"
79
+ 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" href="/locations">Locations</a>
82
+ <a class="nav-link active" aria-current="page"
83
+ href="{{ url_for('eddington.index') }}">Eddington</a>
83
84
  </li>
84
85
  <li class="nav-item">
85
- <a class="nav-link active" aria-current="page" href="/equipment">Equipment</a>
86
+ <a class="nav-link active" aria-current="page"
87
+ href="{{ url_for('equipment.index') }}">Equipment</a>
86
88
  </li>
87
89
  <li class="nav-item">
88
- <a class="nav-link active" aria-current="page" href="/upload">Upload</a>
90
+ <a class="nav-link active" aria-current="page"
91
+ href="{{ url_for('upload.index') }}">Upload</a>
89
92
  </li>
90
93
  </ul>
91
94
  </div>
@@ -106,19 +109,29 @@
106
109
  </script>
107
110
  {% endmacro %}
108
111
 
112
+ {% with messages = get_flashed_messages(with_categories=true) %}
113
+ {% if messages %}
114
+ {% for category, message in messages %}
115
+ <div class="alert alert-{{ category }}" role="alert">
116
+ {{ message }}
117
+ </div>
118
+ {% endfor %}
119
+ {% endif %}
120
+ {% endwith %}
121
+
109
122
  {% block container %}
110
123
  {% endblock %}
111
124
 
112
125
  <div class="row border-top py-3 my-4">
113
126
  <ul class="nav col-12 justify-content-end">
114
127
  <li class="nav-item"><a href="https://github.com/martin-ueding/geo-activity-playground"
115
- class="nav-link px-2 text-muted">GitHub</a></li>
128
+ class="nav-link px-2 text-muted" target="_blank">GitHub</a></li>
116
129
  <li class="nav-item"><a href="https://martin-ueding.github.io/geo-activity-playground/"
117
- class="nav-link px-2 text-muted">Documentation</a></li>
130
+ class="nav-link px-2 text-muted" target="_blank">Documentation</a></li>
118
131
  <li class="nav-item"><a href="https://bonn.social/tags/GeoActivityPlayground"
119
- class="nav-link px-2 text-muted">Mastodon</a></li>
132
+ class="nav-link px-2 text-muted" target="_blank">Mastodon</a></li>
120
133
  <li class="nav-item"><a href="https://pypi.org/project/geo-activity-playground/"
121
- class="nav-link px-2 text-muted">PyPI</a></li>
134
+ class="nav-link px-2 text-muted" target="_blank">PyPI</a></li>
122
135
  <li class="nav-item"><a href="https://martin-ueding.de/" class="nav-link px-2 text-muted">Martin
123
136
  Ueding</a></li>
124
137
  </ul>
@@ -23,7 +23,7 @@
23
23
  <tbody>
24
24
  {% for activity in activities %}
25
25
  <tr>
26
- <td>{{ activity.name }}</td>
26
+ <td><a href="{{ url_for('activity.show', id=activity.id) }}">{{ activity['name'] }}</a></td>
27
27
  <td>{{ activity.start }}</td>
28
28
  <td>{{ activity.kind }}</td>
29
29
  <td>{{ '%.1f' % activity["distance_km"] }} km</td>
File without changes
@@ -0,0 +1,31 @@
1
+ from flask import Blueprint
2
+ from flask import Response
3
+
4
+ from .controller import TileController
5
+
6
+
7
+ def make_tile_blueprint() -> Blueprint:
8
+ blueprint = Blueprint("tiles", __name__, template_folder="templates")
9
+
10
+ tile_controller = TileController()
11
+
12
+ @blueprint.route("/color/<z>/<x>/<y>.png")
13
+ def tile_color(x: str, y: str, z: str):
14
+ return Response(
15
+ tile_controller.render_color(int(x), int(y), int(z)), mimetype="image/png"
16
+ )
17
+
18
+ @blueprint.route("/grayscale/<z>/<x>/<y>.png")
19
+ def tile_grayscale(x: str, y: str, z: str):
20
+ return Response(
21
+ tile_controller.render_grayscale(int(x), int(y), int(z)),
22
+ mimetype="image/png",
23
+ )
24
+
25
+ @blueprint.route("/pastel/<z>/<x>/<y>.png")
26
+ def tile_pastel(x: str, y: str, z: str):
27
+ return Response(
28
+ tile_controller.render_pastel(int(x), int(y), int(z)), mimetype="image/png"
29
+ )
30
+
31
+ return blueprint
File without changes
@@ -0,0 +1,28 @@
1
+ from flask import Blueprint
2
+ from flask import render_template
3
+
4
+ from ...core.activities import ActivityRepository
5
+ from ...explorer.tile_visits import TileVisitAccessor
6
+ from .controller import UploadController
7
+
8
+
9
+ def make_upload_blueprint(
10
+ repository: ActivityRepository,
11
+ tile_visit_accessor: TileVisitAccessor,
12
+ config: dict,
13
+ ) -> Blueprint:
14
+ blueprint = Blueprint("upload", __name__, template_folder="templates")
15
+
16
+ upload_controller = UploadController(repository, tile_visit_accessor, config)
17
+
18
+ @blueprint.route("/")
19
+ def index():
20
+ return render_template(
21
+ "upload/index.html.j2", **upload_controller.render_form()
22
+ )
23
+
24
+ @blueprint.route("/receive", methods=["POST"])
25
+ def receive():
26
+ return upload_controller.receive()
27
+
28
+ return blueprint
@@ -7,7 +7,6 @@ from flask import flash
7
7
  from flask import redirect
8
8
  from flask import request
9
9
  from flask import Response
10
- from flask import url_for
11
10
  from werkzeug.utils import secure_filename
12
11
 
13
12
  from geo_activity_playground.core.activities import ActivityRepository
@@ -42,19 +41,27 @@ class UploadController:
42
41
  for root, dirs, files in os.walk("Activities"):
43
42
  directories.append(root)
44
43
  directories.sort()
45
- return {"directories": directories}
44
+ return {
45
+ "directories": directories,
46
+ "has_upload": "password" in self._config.get("upload", {}),
47
+ }
46
48
 
47
49
  def receive(self) -> Response:
48
50
  # check if the post request has the file part
49
51
  if "file" not in request.files:
50
- flash("No file part")
51
- return redirect(request.url)
52
+ flash("No file could be found. Did you select a file?", "warning")
53
+ return redirect("/upload")
54
+
55
+ if request.form["password"] != self._config["upload"]["password"]:
56
+ flash("Incorrect upload password!", "danger")
57
+ return redirect("/upload")
58
+
52
59
  file = request.files["file"]
53
60
  # If the user does not select a file, the browser submits an
54
61
  # empty file without a filename.
55
62
  if file.filename == "":
56
- flash("No selected file")
57
- return redirect(request.url)
63
+ flash("No selected file", "warning")
64
+ return redirect("/upload")
58
65
  if file:
59
66
  filename = secure_filename(file.filename)
60
67
  target_path = pathlib.Path(request.form["directory"]) / filename
@@ -76,6 +83,7 @@ class UploadController:
76
83
  skip_strava=True,
77
84
  )
78
85
  activity_id = get_file_hash(target_path)
86
+ flash(f"Activity was saved with ID {activity_id}.", "success")
79
87
  return redirect(f"/activity/{activity_id}")
80
88
 
81
89
 
@@ -88,6 +96,7 @@ def scan_for_activities(
88
96
  if pathlib.Path("Activities").exists():
89
97
  import_from_directory(
90
98
  repository,
99
+ config.get("kind", {}),
91
100
  config.get("metadata_extraction_regexes", []),
92
101
  )
93
102
  if pathlib.Path("Strava Export").exists():
@@ -5,17 +5,12 @@
5
5
  <div class="col">
6
6
  <h1>Upload Activity</h1>
7
7
 
8
- <form method="post" enctype="multipart/form-data" action="/upload/receive">
9
-
10
-
11
-
12
-
8
+ {% if has_upload %}
9
+ <form method="post" enctype="multipart/form-data" action="{{ url_for(".receive") }}">
13
10
  <div class="mb-3">
14
11
  <label for="file1" class="form-label">Activity file</label>
15
12
  <input type="file" name="file" id="file1" class="form-control">
16
13
  </div>
17
-
18
-
19
14
  <div class="mb-3">
20
15
  <label for="directory" class="form-label">Target directory</label>
21
16
  <select name="directory" id="directory" class="form-select" aria-label="Default select example">
@@ -24,13 +19,19 @@
24
19
  {% endfor %}
25
20
  </select>
26
21
  </div>
27
-
28
-
22
+ <div class="mb-3">
23
+ <label for="password" class="form-label">Password</label>
24
+ <input type="password" name="password" id="password" class="form-control">
25
+ </div>
29
26
  <button type="submit" class="btn btn-primary">Upload</button>
30
-
31
27
  </form>
28
+ {% else %}
29
+ <p>You don't have an upload password set. In order to use this feature, add the following to your configuration
30
+ file:</p>
31
+ <code><pre>[upload]
32
+ password = "your unique password here"</pre></code>
33
+ {% endif %}
32
34
  </div>
33
35
  </div>
34
36
 
35
-
36
37
  {% endblock %}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: geo-activity-playground
3
- Version: 0.22.0
3
+ Version: 0.24.0
4
4
  Summary: Analysis of geo data activities like rides, runs or hikes.
5
5
  License: MIT
6
6
  Author: Martin Ueding
@@ -29,6 +29,7 @@ Requires-Dist: pyarrow (>=16.1.0,<17.0.0)
29
29
  Requires-Dist: python-dateutil (>=2.8.2,<3.0.0)
30
30
  Requires-Dist: requests (>=2.28.1,<3.0.0)
31
31
  Requires-Dist: scipy (>=1.8.1,<2.0.0)
32
+ Requires-Dist: shapely (>=2.0.5,<3.0.0)
32
33
  Requires-Dist: stravalib (>=1.3.3,<2.0.0)
33
34
  Requires-Dist: tcxreader (>=0.4.5,<0.5.0)
34
35
  Requires-Dist: tomli (>=2.0.1,<3.0.0) ; python_version < "3.11"
@@ -0,0 +1,95 @@
1
+ geo_activity_playground/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ geo_activity_playground/__main__.py,sha256=pAl5_cWjuNyGmE_zMoPtG7Gc7r7lNk3psn9CAxzP2fU,4131
3
+ geo_activity_playground/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ geo_activity_playground/core/activities.py,sha256=Cee7Md1izbINyiU-mOiQC2Bo6fHPeH3moSoCR4Yjm2A,11403
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
7
+ geo_activity_playground/core/config.py,sha256=YjqCiEmIAa-GM1-JfBctMEsl8-I56pZyyDdTyPduOzw,477
8
+ geo_activity_playground/core/coordinates.py,sha256=tDfr9mlXhK6E_MMIJ0vYWVCoH0Lq8uyuaqUgaa8i0jg,966
9
+ geo_activity_playground/core/heatmap.py,sha256=bRLQHzmTEsQbX8XWeg85x_lRGk272UoYRiCnoxZ5da0,4189
10
+ geo_activity_playground/core/paths.py,sha256=EX4yQQyAmWRxTYS0QvFCbGz9v1J0z_1QYfd5hjTmaRw,808
11
+ geo_activity_playground/core/privacy_zones.py,sha256=4TumHsVUN1uW6RG3ArqTXDykPVipF98DCxVBe7YNdO8,512
12
+ geo_activity_playground/core/similarity.py,sha256=Jo8jRViuORCxdIGvyaflgsQhwu9S_jn10a450FRL18A,3159
13
+ geo_activity_playground/core/tasks.py,sha256=lcfLeYpla819v2BTf53zvT0xEB4I6OpSfqfF_xlsLwM,1817
14
+ geo_activity_playground/core/test_tiles.py,sha256=zce1FxNfsSpOQt66jMehdQRVoNdl-oiFydx6iVBHZXM,764
15
+ geo_activity_playground/core/test_time_conversion.py,sha256=Sh6nZA3uCTOdZTZa3yOijtR0m74QtZu2mcWXsDNnyQI,984
16
+ geo_activity_playground/core/tiles.py,sha256=VxPu9vdfKnxDxaYo5JSYmka9Dt3TDxg0zo3cYVJXVHc,3359
17
+ geo_activity_playground/core/time_conversion.py,sha256=9J6aTlqJhWvsknQkoECNL-CIG-8BKs6ZatJJ9XJnTsg,367
18
+ geo_activity_playground/explorer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ 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
21
+ geo_activity_playground/explorer/video.py,sha256=ROAmV9shfJyqTgnXVD41KFORiwnRgVpEWenIq4hMCRM,4389
22
+ 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=o3GsRRqpt0RWSJ-H0upLWcXiFJ81YpvgZCrHI8Qlqng,7765
25
+ geo_activity_playground/importers/strava_checkout.py,sha256=nvZX7skevqMWdW9Gxzt4VhwCaI3wv2z5g-ODv6ZWxng,8078
26
+ geo_activity_playground/importers/test_directory.py,sha256=ljXokx7q0OgtHvEdHftcQYEmZJUDVv3OOF5opklxdT4,724
27
+ geo_activity_playground/importers/test_strava_api.py,sha256=4vX7wDr1a9aRh8myxNrIq6RwDBbP8ZeoXXPc10CAbW4,431
28
+ geo_activity_playground/webui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
+ geo_activity_playground/webui/activity/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ geo_activity_playground/webui/activity/blueprint.py,sha256=wtP3E_iJpOmUhqaq_SyZPkB92eM1giYsyJmqKUwIW0A,1758
31
+ geo_activity_playground/webui/activity/controller.py,sha256=8RkVnD_uW09XBCKn_5m2sD81jG7ezshYLAbGvtDVB3M,16627
32
+ geo_activity_playground/webui/activity/templates/activity/day.html.j2,sha256=r3qKl9uTzOko4R-ZzyYAZt1j61JSevYP4g0Yi06HHPg,2702
33
+ geo_activity_playground/webui/activity/templates/activity/lines.html.j2,sha256=5gB1aDjRgi_RventenRfC10_FtMT4ch_VuWvA9AMlBY,1121
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
37
+ geo_activity_playground/webui/calendar/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
+ geo_activity_playground/webui/calendar/blueprint.py,sha256=rlnhgU2DWAcdLMRq7m77NzrM_aDyp4s3kuuQHuzjHhg,782
39
+ geo_activity_playground/webui/calendar/controller.py,sha256=QpSAkR2s1sbLSu6P_fNNTccgGglOzEH2PIv1XwKxeVY,2778
40
+ geo_activity_playground/webui/calendar/templates/calendar/index.html.j2,sha256=xoR6R4cUgTQNRHQv3m3f4Bc-yCjJEsJj5d4_CWlJsRo,1427
41
+ geo_activity_playground/webui/calendar/templates/calendar/month.html.j2,sha256=sRIiNo_Rp9CHary6e-lnpKJKOuAonoDEBvKMxzbTLQE,1802
42
+ geo_activity_playground/webui/eddington/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
+ geo_activity_playground/webui/eddington/blueprint.py,sha256=evIvueLfDWVTxJ9pRguqmZ9-Pybd2WmBRst_-7vX2QA,551
44
+ geo_activity_playground/webui/eddington/controller.py,sha256=L4YssL088UMamp4qgzEtilzhaKGZsyfVUScuw-fmTjE,2888
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
47
+ geo_activity_playground/webui/equipment/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
+ geo_activity_playground/webui/equipment/blueprint.py,sha256=YbOAsjk05BMeUlD6wGfxE1amvlLsFjG4HMJn5SBrVRU,551
49
+ geo_activity_playground/webui/equipment/controller.py,sha256=ST8MEgk5UxTiIM0sQUpKr1TJ2uPRm55V6DI3brPokUo,2390
50
+ geo_activity_playground/webui/equipment/templates/equipment/index.html.j2,sha256=dNuezVKJNJZbQ0y2-AYlXNHpncTbtOSDNLqNswRTxaI,1320
51
+ geo_activity_playground/webui/explorer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
+ geo_activity_playground/webui/explorer/blueprint.py,sha256=jHE6bb4H2GmYaix9P452Cyrn_30z_Nm705AZ0WNm4HQ,1950
53
+ geo_activity_playground/webui/explorer/controller.py,sha256=8d0FFrG55ZlPQ5seQC2DegxIkPGIW7pvw8Jm4eJt3fg,10634
54
+ geo_activity_playground/webui/explorer/templates/explorer/index.html.j2,sha256=OxNt2_VwH-cmVXJrDNfeD-dNB9IrwBjdhhQEqkXxHj0,6529
55
+ geo_activity_playground/webui/heatmap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
+ geo_activity_playground/webui/heatmap/blueprint.py,sha256=bjQu-HL3QN5UpJ6tHOifhcLGlPr_hIKvaRu78md4JqM,1470
57
+ geo_activity_playground/webui/heatmap/heatmap_controller.py,sha256=f83Yyns2guS7Ck90OJZT41Ftw8SjAodAOEMlEML5Pa0,6350
58
+ geo_activity_playground/webui/heatmap/templates/heatmap/index.html.j2,sha256=YLeu6P4djl8G4qAXR6DhetseqrbOodN7aN4coocknc4,1875
59
+ geo_activity_playground/webui/search_controller.py,sha256=PzMf7b8tiJKZIZoPvQ9A2hOrzoKV9cS3jq05w2fK94c,532
60
+ geo_activity_playground/webui/square_planner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
61
+ geo_activity_playground/webui/square_planner/blueprint.py,sha256=r2VkSM547chX85g6c1BQ8NC-tkdqGdYp-2ZALBiiDTc,1320
62
+ geo_activity_playground/webui/square_planner/controller.py,sha256=wYcNEviDgqyYxSrnwMD_5LnYXIazVH9plGX8RxG6oco,3464
63
+ geo_activity_playground/webui/square_planner/templates/square_planner/index.html.j2,sha256=aIB0ql5qW4HXfp0ENksYYOk9vTgBitwyHJX5W7bqkeY,6512
64
+ geo_activity_playground/webui/static/android-chrome-192x192.png,sha256=yxZgo8Jw4hrgOgrn3tvi9G0AXWGFD29kjCuxC07WoT4,17610
65
+ geo_activity_playground/webui/static/android-chrome-384x384.png,sha256=bgeqAdyvDZBMch7rVi3qSawf0Zr4Go0EG8Ws_B8NApY,49297
66
+ geo_activity_playground/webui/static/android-chrome-512x512.png,sha256=Uiv62gQpUjMOdp9d6exzd6IyOi5zgQdgjIVVWYw5m98,38891
67
+ geo_activity_playground/webui/static/apple-touch-icon.png,sha256=NsVMubJtVzslhql4GZlwTWm4cNRowgcEOT53RuZV190,16549
68
+ geo_activity_playground/webui/static/browserconfig.xml,sha256=u5EvU6U1jKSDiXHW0i4rXrs0lT_tmS82otbJnvmccrk,253
69
+ geo_activity_playground/webui/static/favicon-16x16.png,sha256=Yy8lRjGB7itDaeUI_l_Toq3OO0gzgPGnqIfM3CqcQy0,1419
70
+ geo_activity_playground/webui/static/favicon-32x32.png,sha256=K52R99pLGgWHnjFPPkVieBm051Er-sTCiMvgLr9Alrg,2587
71
+ geo_activity_playground/webui/static/favicon.ico,sha256=uVNZlrF22zn1lcCS_9wAoWLhBt1mpGSGMmHZwwskIVc,12014
72
+ geo_activity_playground/webui/static/mstile-150x150.png,sha256=j1ANUQJ1Xi1DR2sGqYZztob2ypfGw04eNtGpN9SxExA,11964
73
+ geo_activity_playground/webui/static/safari-pinned-tab.svg,sha256=OzoEVGY0igWRXM1NiM3SRKugdICBN7aB_XuxaC3Mu9Q,8371
74
+ 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/summary/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
+ geo_activity_playground/webui/summary/blueprint.py,sha256=kzQ6MDOycQKfDcVoEUmL7HYHJA_gu8DlzVHwO37-_jA,514
78
+ geo_activity_playground/webui/summary/controller.py,sha256=zwJq9HIppk_3kBeRvl2ZF2kJfqvCaSSSWrqFHOrSPWc,8673
79
+ 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
82
+ 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
84
+ geo_activity_playground/webui/tile/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
+ geo_activity_playground/webui/tile/blueprint.py,sha256=cK0o2Z3BrLycgF9zw0F8s9qF-JaYDbF5Gog-GXDtUZ8,943
86
+ geo_activity_playground/webui/tile/controller.py,sha256=PISh4vKs27b-LxFfTARtr5RAwHFresA1Kw1MDcERSRU,1221
87
+ geo_activity_playground/webui/upload/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
+ 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
90
+ geo_activity_playground/webui/upload/templates/upload/index.html.j2,sha256=hfXkEXaz_MkM46rU56423D6V6WtK-EAXPszSY-_-Tx8,1471
91
+ geo_activity_playground-0.24.0.dist-info/LICENSE,sha256=4RpAwKO8bPkfXH2lnpeUW0eLkNWglyG4lbrLDU_MOwY,1070
92
+ geo_activity_playground-0.24.0.dist-info/METADATA,sha256=UrduUtiMuNdNYDDjMnA-KMXzetNnr71JTAjs4O4PZjM,1665
93
+ geo_activity_playground-0.24.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
94
+ geo_activity_playground-0.24.0.dist-info/entry_points.txt,sha256=pbNlLI6IIZIp7nPYCfAtiSiz2oxJSCl7DODD6SPkLKk,81
95
+ geo_activity_playground-0.24.0.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- from geo_activity_playground.core.activities import ActivityRepository
2
-
3
-
4
- class ConfigController:
5
- def __init__(self, repository: ActivityRepository) -> None:
6
- self._repository = repository
7
-
8
- def action_index(self) -> dict:
9
- return {}
10
-
11
- def action_save(self, form_input) -> dict:
12
- return {}
@@ -1,28 +0,0 @@
1
- import geojson
2
-
3
- from ..core.activities import ActivityRepository
4
-
5
-
6
- class LocationsController:
7
- def __init__(self, repository: ActivityRepository) -> None:
8
- self._repository = repository
9
-
10
- def render_index(self) -> dict:
11
- activity_endpoints = []
12
- for activity in self._repository.iter_activities():
13
- activity_endpoints.append(
14
- (activity["start_latitude"], activity["start_longitude"])
15
- )
16
- activity_endpoints.append(
17
- (activity["end_latitude"], activity["end_longitude"])
18
- )
19
-
20
- activity_endpoints_geojson = geojson.dumps(
21
- geojson.FeatureCollection(
22
- [
23
- geojson.Feature(geometry=geojson.Point((longitude, latitude)))
24
- for latitude, longitude in activity_endpoints
25
- ]
26
- )
27
- )
28
- return {"activity_endpoints_geojson": activity_endpoints_geojson}
@@ -1,60 +0,0 @@
1
- import functools
2
-
3
- import altair as alt
4
- import pandas as pd
5
-
6
- from geo_activity_playground.core.activities import ActivityRepository
7
-
8
-
9
- class SummaryController:
10
- def __init__(self, repository: ActivityRepository) -> None:
11
- self._repository = repository
12
-
13
- @functools.cache
14
- def render(self) -> dict:
15
- return {
16
- "distance_heatmap_meta_plot": distance_heatmap_meta_plot(
17
- self._repository.meta
18
- ),
19
- "year_on_year_distance_meta_plot": year_on_year_distance_meta_plot(
20
- self._repository.meta
21
- ),
22
- }
23
-
24
-
25
- def distance_heatmap_meta_plot(meta: pd.DataFrame) -> str:
26
- return (
27
- alt.Chart(meta)
28
- .mark_rect()
29
- .encode(
30
- alt.X("date(start):O", title="Day of month"),
31
- alt.Y(
32
- "yearmonth(start):O",
33
- scale=alt.Scale(reverse=True),
34
- title="Year and month",
35
- ),
36
- alt.Color("sum(distance_km)", scale=alt.Scale(scheme="viridis")),
37
- [
38
- alt.Tooltip("yearmonthdate(start)", title="Date"),
39
- alt.Tooltip(
40
- "sum(distance_km)", format=".1f", title="Total distance / km"
41
- ),
42
- alt.Tooltip("count(distance_km)", title="Number of activities"),
43
- ],
44
- )
45
- .to_json(format="vega")
46
- )
47
-
48
-
49
- def year_on_year_distance_meta_plot(meta: pd.DataFrame) -> str:
50
- return (
51
- alt.Chart(meta, title="Year on Year Distance")
52
- .mark_bar()
53
- .encode(
54
- alt.X("month(start)"),
55
- alt.Y("sum(distance_km)"),
56
- alt.Color("kind", scale=alt.Scale(scheme="category10"), title="Kind"),
57
- )
58
- .facet(facet="year(start):O", columns=4)
59
- .to_json(format="vega")
60
- )