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.
- geo_activity_playground/__main__.py +1 -1
- geo_activity_playground/core/activities.py +16 -9
- geo_activity_playground/core/activity_parsers.py +17 -32
- geo_activity_playground/core/cache_migrations.py +24 -0
- geo_activity_playground/core/heatmap.py +21 -21
- geo_activity_playground/core/privacy_zones.py +16 -0
- geo_activity_playground/core/similarity.py +1 -1
- geo_activity_playground/core/test_time_conversion.py +37 -0
- geo_activity_playground/core/time_conversion.py +14 -0
- geo_activity_playground/explorer/tile_visits.py +44 -27
- geo_activity_playground/importers/__init__.py +0 -0
- geo_activity_playground/importers/directory.py +7 -2
- geo_activity_playground/importers/strava_api.py +6 -0
- geo_activity_playground/importers/strava_checkout.py +12 -3
- geo_activity_playground/webui/__init__.py +0 -0
- geo_activity_playground/webui/activity/__init__.py +0 -0
- geo_activity_playground/webui/activity/blueprint.py +58 -0
- geo_activity_playground/webui/{activity_controller.py → activity/controller.py} +128 -18
- geo_activity_playground/webui/{templates/activity-day.html.j2 → activity/templates/activity/day.html.j2} +14 -2
- geo_activity_playground/webui/{templates/activity-name.html.j2 → activity/templates/activity/name.html.j2} +1 -1
- geo_activity_playground/webui/{templates/activity.html.j2 → activity/templates/activity/show.html.j2} +9 -4
- geo_activity_playground/webui/app.py +68 -281
- geo_activity_playground/webui/calendar/__init__.py +0 -0
- geo_activity_playground/webui/calendar/blueprint.py +26 -0
- geo_activity_playground/webui/{calendar_controller.py → calendar/controller.py} +5 -5
- geo_activity_playground/webui/{templates/calendar.html.j2 → calendar/templates/calendar/index.html.j2} +3 -2
- geo_activity_playground/webui/{templates/calendar-month.html.j2 → calendar/templates/calendar/month.html.j2} +2 -2
- geo_activity_playground/webui/eddington/__init__.py +0 -0
- geo_activity_playground/webui/eddington/blueprint.py +19 -0
- geo_activity_playground/webui/{eddington_controller.py → eddington/controller.py} +14 -6
- geo_activity_playground/webui/eddington/templates/eddington/index.html.j2 +56 -0
- geo_activity_playground/webui/entry_controller.py +4 -2
- geo_activity_playground/webui/equipment/__init__.py +0 -0
- geo_activity_playground/webui/equipment/blueprint.py +19 -0
- geo_activity_playground/webui/{equipment_controller.py → equipment/controller.py} +5 -3
- geo_activity_playground/webui/explorer/__init__.py +0 -0
- geo_activity_playground/webui/explorer/blueprint.py +54 -0
- geo_activity_playground/webui/{explorer_controller.py → explorer/controller.py} +6 -2
- geo_activity_playground/webui/{templates/explorer.html.j2 → explorer/templates/explorer/index.html.j2} +2 -2
- geo_activity_playground/webui/heatmap/__init__.py +0 -0
- geo_activity_playground/webui/heatmap/blueprint.py +41 -0
- geo_activity_playground/webui/{heatmap_controller.py → heatmap/heatmap_controller.py} +36 -13
- geo_activity_playground/webui/{templates/heatmap.html.j2 → heatmap/templates/heatmap/index.html.j2} +17 -2
- geo_activity_playground/webui/search_controller.py +1 -9
- geo_activity_playground/webui/square_planner/__init__.py +0 -0
- geo_activity_playground/webui/square_planner/blueprint.py +38 -0
- geo_activity_playground/webui/summary/__init__.py +0 -0
- geo_activity_playground/webui/summary/blueprint.py +16 -0
- geo_activity_playground/webui/summary/controller.py +268 -0
- geo_activity_playground/webui/summary/templates/summary/index.html.j2 +135 -0
- geo_activity_playground/webui/templates/{index.html.j2 → home.html.j2} +1 -1
- geo_activity_playground/webui/templates/page.html.j2 +32 -19
- geo_activity_playground/webui/templates/search.html.j2 +1 -1
- geo_activity_playground/webui/tile/__init__.py +0 -0
- geo_activity_playground/webui/tile/blueprint.py +31 -0
- geo_activity_playground/webui/upload/__init__.py +0 -0
- geo_activity_playground/webui/upload/blueprint.py +28 -0
- geo_activity_playground/webui/{upload_controller.py → upload/controller.py} +15 -6
- geo_activity_playground/webui/{templates/upload.html.j2 → upload/templates/upload/index.html.j2} +12 -11
- {geo_activity_playground-0.22.0.dist-info → geo_activity_playground-0.24.0.dist-info}/METADATA +2 -1
- geo_activity_playground-0.24.0.dist-info/RECORD +95 -0
- geo_activity_playground/webui/config_controller.py +0 -12
- geo_activity_playground/webui/locations_controller.py +0 -28
- geo_activity_playground/webui/summary_controller.py +0 -60
- geo_activity_playground/webui/templates/config.html.j2 +0 -24
- geo_activity_playground/webui/templates/eddington.html.j2 +0 -18
- geo_activity_playground/webui/templates/locations.html.j2 +0 -38
- geo_activity_playground/webui/templates/summary.html.j2 +0 -21
- geo_activity_playground-0.22.0.dist-info/RECORD +0 -74
- /geo_activity_playground/webui/{templates/activity-lines.html.j2 → activity/templates/activity/lines.html.j2} +0 -0
- /geo_activity_playground/webui/{templates/equipment.html.j2 → equipment/templates/equipment/index.html.j2} +0 -0
- /geo_activity_playground/webui/{square_planner_controller.py → square_planner/controller.py} +0 -0
- /geo_activity_playground/webui/{templates/square-planner.html.j2 → square_planner/templates/square_planner/index.html.j2} +0 -0
- /geo_activity_playground/webui/{tile_controller.py → tile/controller.py} +0 -0
- {geo_activity_playground-0.22.0.dist-info → geo_activity_playground-0.24.0.dist-info}/LICENSE +0 -0
- {geo_activity_playground-0.22.0.dist-info → geo_activity_playground-0.24.0.dist-info}/WHEEL +0 -0
- {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: '© <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="
|
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="
|
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"
|
62
|
-
|
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"
|
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"
|
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"
|
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"
|
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"
|
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"
|
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"
|
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
|
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 {
|
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
|
51
|
-
return redirect(
|
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(
|
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():
|
geo_activity_playground/webui/{templates/upload.html.j2 → upload/templates/upload/index.html.j2}
RENAMED
@@ -5,17 +5,12 @@
|
|
5
5
|
<div class="col">
|
6
6
|
<h1>Upload Activity</h1>
|
7
7
|
|
8
|
-
|
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 %}
|
{geo_activity_playground-0.22.0.dist-info → geo_activity_playground-0.24.0.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: geo-activity-playground
|
3
|
-
Version: 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
|
-
)
|