geo-activity-playground 0.38.1__py3-none-any.whl → 0.39.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 +5 -47
- geo_activity_playground/alembic/README +1 -0
- geo_activity_playground/alembic/env.py +76 -0
- geo_activity_playground/alembic/script.py.mako +26 -0
- geo_activity_playground/alembic/versions/451e7836b53d_add_square_planner_bookmark.py +33 -0
- geo_activity_playground/alembic/versions/63d3b7f6f93c_initial_version.py +73 -0
- geo_activity_playground/alembic/versions/ab83b9d23127_add_upstream_id.py +28 -0
- geo_activity_playground/alembic/versions/b03491c593f6_add_crop_indices.py +30 -0
- geo_activity_playground/alembic/versions/e02e27876deb_add_square_planner_bookmark_name.py +28 -0
- geo_activity_playground/alembic/versions/script.py.mako +28 -0
- geo_activity_playground/core/activities.py +50 -136
- geo_activity_playground/core/config.py +3 -3
- geo_activity_playground/core/datamodel.py +257 -0
- geo_activity_playground/core/enrichment.py +90 -92
- geo_activity_playground/core/heart_rate.py +1 -2
- geo_activity_playground/core/paths.py +6 -7
- geo_activity_playground/core/raster_map.py +43 -4
- geo_activity_playground/core/similarity.py +1 -2
- geo_activity_playground/core/tasks.py +2 -2
- geo_activity_playground/core/test_meta_search.py +3 -3
- geo_activity_playground/core/test_summary_stats.py +1 -1
- geo_activity_playground/explorer/grid_file.py +2 -2
- geo_activity_playground/explorer/tile_visits.py +8 -10
- geo_activity_playground/heatmap_video.py +7 -8
- geo_activity_playground/importers/activity_parsers.py +2 -2
- geo_activity_playground/importers/directory.py +9 -10
- geo_activity_playground/importers/strava_api.py +9 -9
- geo_activity_playground/importers/strava_checkout.py +12 -13
- geo_activity_playground/importers/test_csv_parser.py +3 -3
- geo_activity_playground/importers/test_directory.py +1 -1
- geo_activity_playground/importers/test_strava_api.py +1 -1
- geo_activity_playground/webui/app.py +94 -86
- geo_activity_playground/webui/authenticator.py +1 -1
- geo_activity_playground/webui/{activity/controller.py → blueprints/activity_blueprint.py} +246 -108
- geo_activity_playground/webui/{auth_blueprint.py → blueprints/auth_blueprint.py} +1 -1
- geo_activity_playground/webui/blueprints/bubble_chart_blueprint.py +61 -0
- geo_activity_playground/webui/{calendar/controller.py → blueprints/calendar_blueprint.py} +19 -19
- geo_activity_playground/webui/{eddington_blueprint.py → blueprints/eddington_blueprint.py} +9 -5
- geo_activity_playground/webui/blueprints/entry_views.py +68 -0
- geo_activity_playground/webui/{equipment_blueprint.py → blueprints/equipment_blueprint.py} +58 -4
- geo_activity_playground/webui/{explorer/controller.py → blueprints/explorer_blueprint.py} +88 -54
- geo_activity_playground/webui/blueprints/heatmap_blueprint.py +233 -0
- geo_activity_playground/webui/{search_blueprint.py → blueprints/search_blueprint.py} +7 -11
- geo_activity_playground/webui/blueprints/settings_blueprint.py +446 -0
- geo_activity_playground/webui/{square_planner_blueprint.py → blueprints/square_planner_blueprint.py} +31 -6
- geo_activity_playground/webui/{summary_blueprint.py → blueprints/summary_blueprint.py} +21 -26
- geo_activity_playground/webui/blueprints/tile_blueprint.py +27 -0
- geo_activity_playground/webui/{upload_blueprint.py → blueprints/upload_blueprint.py} +13 -18
- geo_activity_playground/webui/flasher.py +26 -0
- geo_activity_playground/webui/plot_util.py +1 -1
- geo_activity_playground/webui/search_util.py +4 -6
- geo_activity_playground/webui/static/images/layers-2x.png +0 -0
- geo_activity_playground/webui/static/images/layers.png +0 -0
- geo_activity_playground/webui/static/images/marker-icon-2x.png +0 -0
- geo_activity_playground/webui/static/images/marker-icon.png +0 -0
- geo_activity_playground/webui/static/images/marker-shadow.png +0 -0
- geo_activity_playground/webui/templates/activity/day.html.j2 +81 -0
- geo_activity_playground/webui/templates/activity/edit.html.j2 +38 -0
- geo_activity_playground/webui/{activity/templates → templates}/activity/name.html.j2 +29 -27
- geo_activity_playground/webui/{activity/templates → templates}/activity/show.html.j2 +60 -36
- geo_activity_playground/webui/templates/activity/trim.html.j2 +68 -0
- geo_activity_playground/webui/templates/bubble_chart/index.html.j2 +26 -0
- geo_activity_playground/webui/templates/calendar/index.html.j2 +48 -0
- geo_activity_playground/webui/templates/calendar/month.html.j2 +57 -0
- geo_activity_playground/webui/templates/equipment/index.html.j2 +7 -0
- geo_activity_playground/webui/templates/home.html.j2 +6 -6
- geo_activity_playground/webui/templates/page.html.j2 +2 -1
- geo_activity_playground/webui/{settings/templates → templates}/settings/index.html.j2 +9 -20
- geo_activity_playground/webui/templates/settings/manage-equipments.html.j2 +49 -0
- geo_activity_playground/webui/templates/settings/manage-kinds.html.j2 +48 -0
- geo_activity_playground/webui/{settings/templates → templates}/settings/metadata-extraction.html.j2 +1 -1
- geo_activity_playground/webui/{settings/templates → templates}/settings/privacy-zones.html.j2 +2 -0
- geo_activity_playground/webui/{settings/templates → templates}/settings/strava.html.j2 +2 -0
- geo_activity_playground/webui/templates/square_planner/index.html.j2 +63 -13
- {geo_activity_playground-0.38.1.dist-info → geo_activity_playground-0.39.0.dist-info}/METADATA +5 -1
- geo_activity_playground-0.39.0.dist-info/RECORD +133 -0
- geo_activity_playground/__init__.py +0 -0
- geo_activity_playground/core/__init__.py +0 -0
- geo_activity_playground/explorer/__init__.py +0 -0
- geo_activity_playground/importers/__init__.py +0 -0
- geo_activity_playground/webui/__init__.py +0 -0
- geo_activity_playground/webui/activity/__init__.py +0 -0
- geo_activity_playground/webui/activity/blueprint.py +0 -109
- geo_activity_playground/webui/activity/templates/activity/day.html.j2 +0 -80
- geo_activity_playground/webui/activity/templates/activity/edit.html.j2 +0 -42
- geo_activity_playground/webui/calendar/__init__.py +0 -0
- geo_activity_playground/webui/calendar/blueprint.py +0 -23
- geo_activity_playground/webui/calendar/templates/calendar/index.html.j2 +0 -46
- geo_activity_playground/webui/calendar/templates/calendar/month.html.j2 +0 -55
- geo_activity_playground/webui/entry_controller.py +0 -63
- geo_activity_playground/webui/explorer/__init__.py +0 -0
- geo_activity_playground/webui/explorer/blueprint.py +0 -62
- geo_activity_playground/webui/heatmap/__init__.py +0 -0
- geo_activity_playground/webui/heatmap/blueprint.py +0 -51
- geo_activity_playground/webui/heatmap/heatmap_controller.py +0 -216
- geo_activity_playground/webui/settings/blueprint.py +0 -262
- geo_activity_playground/webui/settings/controller.py +0 -272
- geo_activity_playground/webui/settings/templates/settings/equipment-offsets.html.j2 +0 -44
- geo_activity_playground/webui/settings/templates/settings/kind-renames.html.j2 +0 -25
- geo_activity_playground/webui/settings/templates/settings/kinds-without-achievements.html.j2 +0 -30
- geo_activity_playground/webui/tile_blueprint.py +0 -42
- geo_activity_playground-0.38.1.dist-info/RECORD +0 -129
- /geo_activity_playground/webui/{activity/templates → templates}/activity/lines.html.j2 +0 -0
- /geo_activity_playground/webui/{explorer/templates → templates}/explorer/index.html.j2 +0 -0
- /geo_activity_playground/webui/{heatmap/templates → templates}/heatmap/index.html.j2 +0 -0
- /geo_activity_playground/webui/{settings/templates → templates}/settings/admin-password.html.j2 +0 -0
- /geo_activity_playground/webui/{settings/templates → templates}/settings/color-schemes.html.j2 +0 -0
- /geo_activity_playground/webui/{settings/templates → templates}/settings/heart-rate.html.j2 +0 -0
- /geo_activity_playground/webui/{settings/templates → templates}/settings/segmentation.html.j2 +0 -0
- /geo_activity_playground/webui/{settings/templates → templates}/settings/sharepic.html.j2 +0 -0
- {geo_activity_playground-0.38.1.dist-info → geo_activity_playground-0.39.0.dist-info}/LICENSE +0 -0
- {geo_activity_playground-0.38.1.dist-info → geo_activity_playground-0.39.0.dist-info}/WHEEL +0 -0
- {geo_activity_playground-0.38.1.dist-info → geo_activity_playground-0.39.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
import abc
|
2
|
+
from enum import Enum
|
3
|
+
|
4
|
+
import flask
|
5
|
+
|
6
|
+
|
7
|
+
class FlashTypes(Enum):
|
8
|
+
PRIMARY = "primary"
|
9
|
+
SECONDARY = "secondary"
|
10
|
+
SUCCESS = "success"
|
11
|
+
DANGER = "danger"
|
12
|
+
WARNING = "warning"
|
13
|
+
INFO = "info"
|
14
|
+
LIGHT = "light"
|
15
|
+
DARK = "dark"
|
16
|
+
|
17
|
+
|
18
|
+
class Flasher(abc.ABC):
|
19
|
+
@abc.abstractmethod
|
20
|
+
def flash_message(self, message: str, type: FlashTypes):
|
21
|
+
pass
|
22
|
+
|
23
|
+
|
24
|
+
class FlaskFlasher(Flasher):
|
25
|
+
def flash_message(self, message, type):
|
26
|
+
flask.flash(message, category=type.value)
|
@@ -1,11 +1,9 @@
|
|
1
|
-
from typing import Optional
|
2
|
-
|
3
1
|
from werkzeug.datastructures import MultiDict
|
4
2
|
|
5
|
-
from
|
6
|
-
from
|
7
|
-
from
|
8
|
-
from
|
3
|
+
from ..core.config import ConfigAccessor
|
4
|
+
from ..core.meta_search import _parse_date_or_none
|
5
|
+
from ..core.meta_search import SearchQuery
|
6
|
+
from .authenticator import Authenticator
|
9
7
|
|
10
8
|
|
11
9
|
def search_query_from_form(args: MultiDict) -> SearchQuery:
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,81 @@
|
|
1
|
+
{% extends "page.html.j2" %}
|
2
|
+
|
3
|
+
{% block container %}
|
4
|
+
<div class="row mb-3">
|
5
|
+
<div class="col">
|
6
|
+
<h1>{{ date }}</h1>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
|
11
|
+
<div class="row mb-3">
|
12
|
+
<div class="col-12">
|
13
|
+
<div id="activity-map" style="height: 500px;"></div>
|
14
|
+
<script>
|
15
|
+
var map = L.map('activity-map', {
|
16
|
+
fullscreenControl: true
|
17
|
+
});
|
18
|
+
L.tileLayer('/tile/grayscale/{z}/{x}/{y}.png', {
|
19
|
+
maxZoom: 19,
|
20
|
+
attribution: '{{ map_tile_attribution|safe }}'
|
21
|
+
}).addTo(map);
|
22
|
+
|
23
|
+
let geojson = L.geoJSON({{ geojson| safe }}, {
|
24
|
+
style: function (feature) { return { color: feature.properties.color } }
|
25
|
+
}).addTo(map)
|
26
|
+
map.fitBounds(geojson.getBounds());
|
27
|
+
</script>
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
|
31
|
+
<div class="row mb-3">
|
32
|
+
<div class="col">
|
33
|
+
<h2>Activities</h2>
|
34
|
+
<div class="table-responsive">
|
35
|
+
<table class="table table-sort table-arrows">
|
36
|
+
<thead>
|
37
|
+
<tr>
|
38
|
+
<th>Name</th>
|
39
|
+
<th>Date</th>
|
40
|
+
<th>Distance / km</th>
|
41
|
+
<th>Elapsed time</th>
|
42
|
+
<th>Speed / km/h</th>
|
43
|
+
<th>Equipment</th>
|
44
|
+
<th>Kind</th>
|
45
|
+
</tr>
|
46
|
+
</thead>
|
47
|
+
<tbody>
|
48
|
+
{% for activity in activities %}
|
49
|
+
<tr>
|
50
|
+
<td><span style="color: {{ activity['color'] }};">█</span> <a
|
51
|
+
href="{{ url_for('activity.show', id=activity.id) }}">{{
|
52
|
+
activity.name }}</a></td>
|
53
|
+
<td>{{ activity.start|dt }}</td>
|
54
|
+
<td>{{ activity.distance_km | round(1) }}</td>
|
55
|
+
<td>{{ activity.elapsed_time|td }}</td>
|
56
|
+
<td>{{ activity.average_speed_moving_kmh|round(1) }}</td>
|
57
|
+
<td>{{ activity["equipment"] }}</td>
|
58
|
+
<td>{{ activity["kind"] }}</td>
|
59
|
+
</tr>
|
60
|
+
{% endfor %}
|
61
|
+
{% if activities|length > 1 %}
|
62
|
+
<tr>
|
63
|
+
<td><b>Total</b></td>
|
64
|
+
<td></td>
|
65
|
+
<td><b>{{ total_distance | round(1) }}</b></td>
|
66
|
+
<td><b>{{ total_elapsed_time|td }}</b></td>
|
67
|
+
<td></td>
|
68
|
+
<td></td>
|
69
|
+
</tr>
|
70
|
+
{% endif %}
|
71
|
+
</tbody>
|
72
|
+
</table>
|
73
|
+
</div>
|
74
|
+
</div>
|
75
|
+
</div>
|
76
|
+
|
77
|
+
<h2>Share picture</h2>
|
78
|
+
|
79
|
+
<p><img class="img-fluid" src="{{ url_for('.day_sharepic', year=year, month=month, day=day) }}" /></p>
|
80
|
+
|
81
|
+
{% endblock %}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
{% extends "page.html.j2" %}
|
2
|
+
|
3
|
+
{% block container %}
|
4
|
+
|
5
|
+
<h1 class="mb-3">Edit Activity</h1>
|
6
|
+
|
7
|
+
<form method="POST">
|
8
|
+
<div class="mb-3">
|
9
|
+
<label for="name" class="form-label">Name</label>
|
10
|
+
<input type="text" class="form-control" id="name" name="name" value="{{ activity.name }}" />
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<div class="mb-3">
|
14
|
+
<label for="kind" class="form-label">Kind</label>
|
15
|
+
<select class="form-control" id="kind" name="kind">
|
16
|
+
<option value="null" {% if activity.kind==None %} selected {% endif %}>None</option>
|
17
|
+
{% for kind in kinds %}
|
18
|
+
<option value="{{ kind.id }}" {% if activity.kind==kind %} selected {% endif %}>{{ kind.name }}</option>
|
19
|
+
{% endfor %}
|
20
|
+
</select>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<div class="mb-3">
|
24
|
+
<label for="equipment" class="form-label">Equipment</label>
|
25
|
+
<select class="form-control" id="equipment" name="equipment">
|
26
|
+
<option value="null" {% if activity.equipment==None %} selected {% endif %}>None</option>
|
27
|
+
{% for equipment in equipments %}
|
28
|
+
<option value="{{ equipment.id }}" {% if activity.equipment==equipment %} selected {% endif %}>
|
29
|
+
{{ equipment.name }}
|
30
|
+
</option>
|
31
|
+
{% endfor %}
|
32
|
+
</select>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<button type="submit" class="btn btn-primary">Save</button>
|
36
|
+
</form>
|
37
|
+
|
38
|
+
{% endblock %}
|
@@ -50,33 +50,35 @@
|
|
50
50
|
<div class="col">
|
51
51
|
<h2>Activities</h2>
|
52
52
|
|
53
|
-
<
|
54
|
-
<
|
55
|
-
<
|
56
|
-
<
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
<
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
53
|
+
<div class="table-responsive">
|
54
|
+
<table class="table table-sort table-arrows">
|
55
|
+
<thead>
|
56
|
+
<tr>
|
57
|
+
<th>Name</th>
|
58
|
+
<th>Date</th>
|
59
|
+
<th class="numeric-sort">Distance / km</th>
|
60
|
+
<th>Elapsed time</th>
|
61
|
+
<th>Speed / km/h</th>
|
62
|
+
<th>Equipment</th>
|
63
|
+
<th>Kind</th>
|
64
|
+
</tr>
|
65
|
+
</thead>
|
66
|
+
<tbody>
|
67
|
+
{% for activity in activities %}
|
68
|
+
<tr>
|
69
|
+
<td><span style="color: {{ activity['color'] }};">█</span> <a
|
70
|
+
href="{{ url_for('activity.show', id=activity.id) }}">{{
|
71
|
+
activity.name }}</a></td>
|
72
|
+
<td>{{ activity.start|dt }}</td>
|
73
|
+
<td>{{ activity.distance_km | round(1) }}</td>
|
74
|
+
<td>{{ activity.elapsed_time|td }}</td>
|
75
|
+
<td>{{ activity.average_speed_moving_kmh|round(1) }}</td>
|
76
|
+
<td>{{ activity["equipment"] }}</td>
|
77
|
+
<td>{{ activity["kind"] }}</td>
|
78
|
+
</tr>
|
79
|
+
{% endfor %}
|
80
|
+
</tbody>
|
81
|
+
</table>
|
80
82
|
</table>
|
81
83
|
</div>
|
82
84
|
</div>
|
@@ -8,20 +8,23 @@
|
|
8
8
|
</div>
|
9
9
|
|
10
10
|
<div class="row mb-3">
|
11
|
-
<div class="col-4">
|
11
|
+
<div class="col-sm-12 col-md-4">
|
12
12
|
<dl>
|
13
13
|
<dt>Name</dt>
|
14
14
|
<dd>{{ activity["name"] }}</dd>
|
15
|
+
|
16
|
+
{% if activity.kind %}
|
15
17
|
<dt>Kind</dt>
|
16
|
-
<dd>{{ activity.kind }}</dd>
|
17
|
-
|
18
|
-
|
18
|
+
<dd>{{ activity.kind.name }}</dd>
|
19
|
+
{% endif %}
|
20
|
+
|
19
21
|
<dt>Distance</dt>
|
20
22
|
<dd>{{ activity.distance_km|round(1) }} km</dd>
|
21
|
-
|
22
|
-
|
23
|
-
<dt>
|
24
|
-
<dd>{{ activity.moving_time|td }}</dd>
|
23
|
+
|
24
|
+
{% if activity.elapsed_time %}
|
25
|
+
<dt>Duration</dt>
|
26
|
+
<dd>{{ activity.elapsed_time|td }} elapsed, {{ activity.moving_time|td }} moving</dd>
|
27
|
+
|
25
28
|
<dt>Average moving speed</dt>
|
26
29
|
<dd>{{ activity.average_speed_moving_kmh|round(1) }} km/h = {{
|
27
30
|
(60/activity.average_speed_moving_kmh)|round(1) }} min/km</dd>
|
@@ -32,29 +35,48 @@
|
|
32
35
|
<dd><a href="{{ url_for('activity.day', year=date.year, month=date.month, day=date.day) }}">{{ date }}</a>
|
33
36
|
{{ time }}
|
34
37
|
</dd>
|
38
|
+
{% endif %}
|
39
|
+
|
40
|
+
{% if activity.calories %}
|
35
41
|
<dt>Calories</dt>
|
36
42
|
<dd>{{ activity.calories }}</dd>
|
43
|
+
{% endif %}
|
44
|
+
|
45
|
+
{% if activity.steps %}
|
37
46
|
<dt>Steps</dt>
|
38
47
|
<dd>{{ activity.steps }}</dd>
|
39
|
-
{%
|
48
|
+
{% endif %}
|
49
|
+
|
50
|
+
{% if activity.elevation_gain %}
|
40
51
|
<dt>Elevation gain</dt>
|
41
52
|
<dd>{{ activity.elevation_gain|round(0) }} m</dd>
|
42
53
|
{% endif %}
|
54
|
+
|
55
|
+
{% if activity.equipment %}
|
43
56
|
<dt>Equipment</dt>
|
44
|
-
<dd>{{ activity
|
57
|
+
<dd>{{ activity.equipment.name }}</dd>
|
58
|
+
{% endif %}
|
59
|
+
|
60
|
+
{% if new_tiles[14] %}
|
45
61
|
<dt>New Explorer Tiles</dt>
|
46
62
|
<dd>{{ new_tiles[14] }}</dd>
|
63
|
+
{% endif %}
|
64
|
+
|
65
|
+
{% if new_tiles[17] %}
|
47
66
|
<dt>New Squadratinhos</dt>
|
48
67
|
<dd>{{ new_tiles[17] }}</dd>
|
68
|
+
{% endif %}
|
69
|
+
|
49
70
|
<dt>ID</dt>
|
50
71
|
<dd>{{ activity.id }}</dd>
|
51
72
|
<dt>Source path</dt>
|
52
73
|
<dd>{{ activity.path }}</dd>
|
53
74
|
</dl>
|
54
75
|
|
55
|
-
<a href="{{ url_for('.edit', id=activity['id']) }}" class="btn btn-secondary btn-small">Edit
|
76
|
+
<a href="{{ url_for('.edit', id=activity['id']) }}" class="btn btn-secondary btn-small">Edit</a>
|
77
|
+
<a href="{{ url_for('.trim', id=activity['id']) }}" class="btn btn-secondary btn-small">Trim</a>
|
56
78
|
</div>
|
57
|
-
<div class="col-8">
|
79
|
+
<div class="col-sm-12 col-md-8">
|
58
80
|
<div id="activity-map" style="height: 500px;" class="mb-3"></div>
|
59
81
|
<script>
|
60
82
|
var map = L.map('activity-map', {
|
@@ -161,7 +183,7 @@
|
|
161
183
|
|
162
184
|
<h2>Share picture</h2>
|
163
185
|
|
164
|
-
<p><img src="{{ url_for('.sharepic', id=activity.id) }}" /></p>
|
186
|
+
<p><img class="img-fluid" src="{{ url_for('.sharepic', id=activity.id) }}" /></p>
|
165
187
|
|
166
188
|
<p>Not happy with the displayed data? <a href="{{ url_for('settings.sharepic') }}">Change share picture
|
167
189
|
settings</a>.</p>
|
@@ -207,29 +229,31 @@
|
|
207
229
|
|
208
230
|
<p><a href="{{ url_for('.name', name=activity['name']) }}">Overview over these activities</a></p>
|
209
231
|
|
210
|
-
<
|
211
|
-
<
|
212
|
-
<
|
213
|
-
<
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
<
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
232
|
+
<div class="table-responsive">
|
233
|
+
<table class="table table-sort table-arrows">
|
234
|
+
<thead>
|
235
|
+
<tr>
|
236
|
+
<th>Date</th>
|
237
|
+
<th class="numeric-sort">Distance / km</th>
|
238
|
+
<th>Elapsed time</th>
|
239
|
+
<th>Equipment</th>
|
240
|
+
<th>Kind</th>
|
241
|
+
</tr>
|
242
|
+
</thead>
|
243
|
+
<tbody>
|
244
|
+
{% for other_activity in similar_activites %}
|
245
|
+
<tr>
|
246
|
+
<td><a href="{{ url_for('.show', id=other_activity.id) }}">{{ other_activity.start|dt
|
247
|
+
}}</a></td>
|
248
|
+
<td>{{ other_activity.distance_km | round(1) }}</td>
|
249
|
+
<td>{{ other_activity.elapsed_time|td }}</td>
|
250
|
+
<td>{{ other_activity["equipment"] }}</td>
|
251
|
+
<td>{{ other_activity["kind"] }}</td>
|
252
|
+
</tr>
|
253
|
+
{% endfor %}
|
254
|
+
</tbody>
|
255
|
+
</table>
|
256
|
+
</div>
|
233
257
|
</div>
|
234
258
|
</div>
|
235
259
|
{% endif %}
|
@@ -0,0 +1,68 @@
|
|
1
|
+
{% extends "page.html.j2" %}
|
2
|
+
|
3
|
+
{% block container %}
|
4
|
+
<div class="row mb-3">
|
5
|
+
<div class="col">
|
6
|
+
<h1>{{ activity["name"] }}</h1>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<a href="{{ url_for('.show', id=activity['id']) }}" class="btn btn-primary btn-small">Back</a>
|
11
|
+
|
12
|
+
|
13
|
+
<div id="activity-map" style="height: 600px;" class="mb-3"></div>
|
14
|
+
<script>
|
15
|
+
var map = L.map('activity-map', {
|
16
|
+
fullscreenControl: true
|
17
|
+
});
|
18
|
+
L.tileLayer('/tile/pastel/{z}/{x}/{y}.png', {
|
19
|
+
maxZoom: 19,
|
20
|
+
attribution: '{{ map_tile_attribution|safe }}'
|
21
|
+
}).addTo(map);
|
22
|
+
|
23
|
+
|
24
|
+
let layer = L.geoJSON({{ color_line_geojson| safe }}, {
|
25
|
+
pointToLayer: function (feature, lat_lon) {
|
26
|
+
let p = feature.properties
|
27
|
+
|
28
|
+
let marker = null
|
29
|
+
if (p.markerType == "circle") {
|
30
|
+
marker = L.circleMarker(lat_lon, p.markerStyle)
|
31
|
+
} else {
|
32
|
+
marker = L.marker(lat_lon)
|
33
|
+
}
|
34
|
+
|
35
|
+
let text = ''
|
36
|
+
if (p.name) {
|
37
|
+
text += `<p><b>${p.name}</b></p>`
|
38
|
+
}
|
39
|
+
if (p.description) {
|
40
|
+
text += p.description
|
41
|
+
}
|
42
|
+
if (text) {
|
43
|
+
marker = marker.bindPopup(text)
|
44
|
+
}
|
45
|
+
|
46
|
+
return marker
|
47
|
+
}
|
48
|
+
})
|
49
|
+
|
50
|
+
layer.addTo(map)
|
51
|
+
map.fitBounds(layer.getBounds());
|
52
|
+
</script>
|
53
|
+
|
54
|
+
<form method="POST">
|
55
|
+
<div class="mb-3">
|
56
|
+
<label for="begin" class="form-label">Begin</label>
|
57
|
+
<input type="number" class="form-control" id="begin" name="begin" value="{{ activity.index_begin }}" />
|
58
|
+
</div>
|
59
|
+
|
60
|
+
<div class="mb-3">
|
61
|
+
<label for="end" class="form-label">End</label>
|
62
|
+
<input type="number" class="form-control" id="end" name="end" value="{{ activity.index_end }}" />
|
63
|
+
</div>
|
64
|
+
|
65
|
+
<button type="submit" class="btn btn-primary">Save</button>
|
66
|
+
</form>
|
67
|
+
|
68
|
+
{% endblock %}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<!-- filepath: /home/michael/Nextcloud/Python/Geoactivity-Fork/geo-activity-playground/geo_activity_playground/webui/templates/bubble_chart/index.html.j2 -->
|
2
|
+
{% extends "page.html.j2" %}
|
3
|
+
|
4
|
+
{% block container %}
|
5
|
+
<div class="row mb-3">
|
6
|
+
<div class="col">
|
7
|
+
<h1>Bubble Chart</h1>
|
8
|
+
<div id="bubble-chart"></div>
|
9
|
+
<script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
|
10
|
+
<script src="https://cdn.jsdelivr.net/npm/vega-lite@5"></script>
|
11
|
+
<script src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
|
12
|
+
<script>
|
13
|
+
const chartSpec = {{ bubble_chart | safe }};
|
14
|
+
vegaEmbed('#bubble-chart', chartSpec).then((result) => {
|
15
|
+
// Add a click event listener to the chart
|
16
|
+
result.view.addEventListener('click', (event, item) => {
|
17
|
+
if (item && item.datum && item.datum.activity_url) {
|
18
|
+
// Redirect to the activity URL
|
19
|
+
window.location.href = item.datum.activity_url;
|
20
|
+
}
|
21
|
+
});
|
22
|
+
}).catch(console.error);
|
23
|
+
</script>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
{% endblock %}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
{% extends "page.html.j2" %}
|
2
|
+
|
3
|
+
{% block container %}
|
4
|
+
<div class="row mb-1">
|
5
|
+
<div class="col">
|
6
|
+
<h1>Calendar</h1>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<div class="row mb-1">
|
11
|
+
<div class="col">
|
12
|
+
<div class="table-responsive">
|
13
|
+
<table class="table">
|
14
|
+
<thead>
|
15
|
+
<tr>
|
16
|
+
<th>Year</th>
|
17
|
+
{% for i in range(1, 13) %}
|
18
|
+
<th style="text-align: right;">{{ i }}</th>
|
19
|
+
{% endfor %}
|
20
|
+
<th style="text-align: right;">Total</th>
|
21
|
+
</tr>
|
22
|
+
</thead>
|
23
|
+
<tbody>
|
24
|
+
{% for year, month_data in monthly_distances.items() %}
|
25
|
+
{% set year_int = year|round(0)|int %}
|
26
|
+
<tr>
|
27
|
+
<td>{{ year_int }}</td>
|
28
|
+
{% for month in range(1, 13) %}
|
29
|
+
<td align="right">
|
30
|
+
{% set distance = month_data[month] %}
|
31
|
+
{% if distance %}
|
32
|
+
<a href="{{ url_for(".month", year=year_int, month=month) }}">{{ distance|int() }} km</a>
|
33
|
+
{% else %}
|
34
|
+
0 km
|
35
|
+
{% endif %}
|
36
|
+
</td>
|
37
|
+
{% endfor %}
|
38
|
+
<td align="right">{{ yearly_distances[year]|int() }} km</td>
|
39
|
+
</tr>
|
40
|
+
{% endfor %}
|
41
|
+
</tbody>
|
42
|
+
</table>
|
43
|
+
</div>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
|
47
|
+
|
48
|
+
{% endblock %}
|
@@ -0,0 +1,57 @@
|
|
1
|
+
{% extends "page.html.j2" %}
|
2
|
+
|
3
|
+
{% block container %}
|
4
|
+
<div class="row mb-1">
|
5
|
+
<div class="col">
|
6
|
+
<h1>Calendar {{ year }}-{{ "{0:02d}".format(month) }}</h1>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<div class="row mb-1">
|
11
|
+
<div class="col">
|
12
|
+
<div class="table-responsive">
|
13
|
+
<table class="table">
|
14
|
+
<thead>
|
15
|
+
<tr>
|
16
|
+
<th>Week</th>
|
17
|
+
<th>Monday</th>
|
18
|
+
<th>Tuesday</th>
|
19
|
+
<th>Wednesday</th>
|
20
|
+
<th>Thursday</th>
|
21
|
+
<th>Friday</th>
|
22
|
+
<th>Saturday</th>
|
23
|
+
<th>Sunday</th>
|
24
|
+
</tr>
|
25
|
+
</thead>
|
26
|
+
<tbody>
|
27
|
+
{% for week, week_data in weeks.items() %}
|
28
|
+
<tr>
|
29
|
+
<td>{{ week }}</td>
|
30
|
+
{% for day in range(1, 8) %}
|
31
|
+
<td>
|
32
|
+
{% if weeks[week][day] %}
|
33
|
+
<a href="{{ url_for('activity.day', year=year, month=month, day=day_of_month[week][day]) }}"><b>{{
|
34
|
+
day_of_month[week][day] }}.</b></a>
|
35
|
+
{% elif day_of_month[week][day] %}
|
36
|
+
<b>{{ day_of_month[week][day] }}.</b>
|
37
|
+
{% endif %}
|
38
|
+
|
39
|
+
{% if weeks[week][day] %}
|
40
|
+
<ul>
|
41
|
+
{% for activity in weeks[week][day] %}
|
42
|
+
<li><a href="{{ url_for('activity.show', id=activity.id) }}">{{ activity.name }}</a></li>
|
43
|
+
{% endfor %}
|
44
|
+
</ul>
|
45
|
+
{% endif %}
|
46
|
+
</td>
|
47
|
+
{% endfor %}
|
48
|
+
</tr>
|
49
|
+
{% endfor %}
|
50
|
+
</tbody>
|
51
|
+
</table>
|
52
|
+
</div>
|
53
|
+
</div>
|
54
|
+
</div>
|
55
|
+
|
56
|
+
|
57
|
+
{% endblock %}
|
@@ -32,6 +32,13 @@
|
|
32
32
|
</div>
|
33
33
|
</div>
|
34
34
|
|
35
|
+
<div class="row mb-3">
|
36
|
+
<div class="col">
|
37
|
+
<h2>Monthly Equipment Usage</h2>
|
38
|
+
{{ vega_direct("stacked_area_chart", stacked_area_chart) }}
|
39
|
+
</div>
|
40
|
+
</div>
|
41
|
+
|
35
42
|
<div class="row mb-3">
|
36
43
|
<div class="col">
|
37
44
|
<h2>Details for each equipment</h2>
|
@@ -13,11 +13,6 @@
|
|
13
13
|
</div>
|
14
14
|
|
15
15
|
|
16
|
-
<div class="row mb-3">
|
17
|
-
<div class="col">
|
18
|
-
<h2>Latest activities</h2>
|
19
|
-
</div>
|
20
|
-
</div>
|
21
16
|
|
22
17
|
<script>
|
23
18
|
function add_map(id, geojson) {
|
@@ -35,7 +30,11 @@
|
|
35
30
|
}
|
36
31
|
</script>
|
37
32
|
|
38
|
-
|
33
|
+
<h2>Latest activities</h2>
|
34
|
+
|
35
|
+
{% for date, date_data in latest_activities.items() %}
|
36
|
+
<h3>{{ date }}</h3>
|
37
|
+
{% for activity_batch in date_data|batch(3) %}
|
39
38
|
<div class="row row-cols-1 row-cols-md-3 g-4 mb-3">
|
40
39
|
{% for elem in activity_batch %}
|
41
40
|
<div class="col">
|
@@ -57,6 +56,7 @@
|
|
57
56
|
{% endfor %}
|
58
57
|
</div>
|
59
58
|
{% endfor %}
|
59
|
+
{% endfor %}
|
60
60
|
{% else %}
|
61
61
|
<p>You don't have activities yet. Either put some files into a directory <tt>Activities</tt>, <a
|
62
62
|
href="{{ url_for('upload.index') }}">upload activities</a> or <a href="{{ url_for('settings.strava') }}">set up
|