geo-activity-playground 0.30.0__tar.gz → 0.31.0__tar.gz

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 (118) hide show
  1. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/PKG-INFO +1 -1
  2. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/core/activities.py +11 -2
  3. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/core/paths.py +3 -0
  4. geo_activity_playground-0.31.0/geo_activity_playground/webui/activity/blueprint.py +109 -0
  5. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/activity/templates/activity/day.html.j2 +3 -3
  6. geo_activity_playground-0.31.0/geo_activity_playground/webui/activity/templates/activity/edit.html.j2 +42 -0
  7. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/activity/templates/activity/name.html.j2 +2 -2
  8. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/activity/templates/activity/show.html.j2 +7 -5
  9. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/app.py +14 -3
  10. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/heatmap/heatmap_controller.py +8 -6
  11. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/search/templates/search/index.html.j2 +2 -2
  12. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/summary/templates/summary/index.html.j2 +1 -1
  13. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/templates/home.html.j2 +2 -2
  14. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/pyproject.toml +1 -1
  15. geo_activity_playground-0.30.0/geo_activity_playground/webui/activity/blueprint.py +0 -57
  16. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/LICENSE +0 -0
  17. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/__init__.py +0 -0
  18. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/__main__.py +0 -0
  19. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/core/__init__.py +0 -0
  20. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/core/config.py +0 -0
  21. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/core/coordinates.py +0 -0
  22. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/core/enrichment.py +0 -0
  23. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/core/heart_rate.py +0 -0
  24. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/core/heatmap.py +0 -0
  25. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/core/privacy_zones.py +0 -0
  26. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/core/similarity.py +0 -0
  27. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/core/tasks.py +0 -0
  28. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/core/test_tiles.py +0 -0
  29. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/core/test_time_conversion.py +0 -0
  30. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/core/tiles.py +0 -0
  31. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/core/time_conversion.py +0 -0
  32. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/explorer/__init__.py +0 -0
  33. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/explorer/grid_file.py +0 -0
  34. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/explorer/tile_visits.py +0 -0
  35. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/explorer/video.py +0 -0
  36. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/importers/__init__.py +0 -0
  37. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/importers/activity_parsers.py +0 -0
  38. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/importers/csv_parser.py +0 -0
  39. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/importers/directory.py +0 -0
  40. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/importers/strava_api.py +0 -0
  41. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/importers/strava_checkout.py +0 -0
  42. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/importers/test_csv_parser.py +0 -0
  43. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/importers/test_directory.py +0 -0
  44. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/importers/test_strava_api.py +0 -0
  45. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/__init__.py +0 -0
  46. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/activity/__init__.py +0 -0
  47. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/activity/controller.py +0 -0
  48. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/activity/templates/activity/lines.html.j2 +0 -0
  49. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/auth/blueprint.py +0 -0
  50. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/auth/templates/auth/index.html.j2 +0 -0
  51. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/authenticator.py +0 -0
  52. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/calendar/__init__.py +0 -0
  53. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/calendar/blueprint.py +0 -0
  54. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/calendar/controller.py +0 -0
  55. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/calendar/templates/calendar/index.html.j2 +0 -0
  56. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/calendar/templates/calendar/month.html.j2 +0 -0
  57. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/eddington/__init__.py +0 -0
  58. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/eddington/blueprint.py +0 -0
  59. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/eddington/controller.py +0 -0
  60. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/eddington/templates/eddington/index.html.j2 +0 -0
  61. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/entry_controller.py +0 -0
  62. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/equipment/__init__.py +0 -0
  63. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/equipment/blueprint.py +0 -0
  64. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/equipment/controller.py +0 -0
  65. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/equipment/templates/equipment/index.html.j2 +0 -0
  66. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/explorer/__init__.py +0 -0
  67. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/explorer/blueprint.py +0 -0
  68. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/explorer/controller.py +0 -0
  69. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/explorer/templates/explorer/index.html.j2 +0 -0
  70. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/heatmap/__init__.py +0 -0
  71. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/heatmap/blueprint.py +0 -0
  72. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/heatmap/templates/heatmap/index.html.j2 +0 -0
  73. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/plot_util.py +0 -0
  74. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/search/blueprint.py +0 -0
  75. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/settings/blueprint.py +0 -0
  76. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/settings/controller.py +0 -0
  77. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/settings/templates/settings/admin-password.html.j2 +0 -0
  78. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/settings/templates/settings/color-schemes.html.j2 +0 -0
  79. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/settings/templates/settings/equipment-offsets.html.j2 +0 -0
  80. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/settings/templates/settings/heart-rate.html.j2 +0 -0
  81. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/settings/templates/settings/index.html.j2 +0 -0
  82. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/settings/templates/settings/kind-renames.html.j2 +0 -0
  83. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/settings/templates/settings/kinds-without-achievements.html.j2 +0 -0
  84. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/settings/templates/settings/metadata-extraction.html.j2 +0 -0
  85. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/settings/templates/settings/privacy-zones.html.j2 +0 -0
  86. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/settings/templates/settings/segmentation.html.j2 +0 -0
  87. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/settings/templates/settings/sharepic.html.j2 +0 -0
  88. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/settings/templates/settings/strava.html.j2 +0 -0
  89. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/square_planner/__init__.py +0 -0
  90. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/square_planner/blueprint.py +0 -0
  91. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/square_planner/controller.py +0 -0
  92. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/square_planner/templates/square_planner/index.html.j2 +0 -0
  93. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/static/android-chrome-192x192.png +0 -0
  94. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/static/android-chrome-512x512.png +0 -0
  95. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/static/apple-touch-icon.png +0 -0
  96. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/static/bootstrap-dark-mode.js +0 -0
  97. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/static/browserconfig.xml +0 -0
  98. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/static/favicon-16x16.png +0 -0
  99. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/static/favicon-32x32.png +0 -0
  100. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/static/favicon-48x48.png +0 -0
  101. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/static/favicon.ico +0 -0
  102. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/static/favicon.svg +0 -0
  103. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/static/mstile-150x150.png +0 -0
  104. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/static/site.webmanifest +0 -0
  105. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/static/web-app-manifest-192x192.png +0 -0
  106. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/static/web-app-manifest-512x512.png +0 -0
  107. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/summary/__init__.py +0 -0
  108. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/summary/blueprint.py +0 -0
  109. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/summary/controller.py +0 -0
  110. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/templates/page.html.j2 +0 -0
  111. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/tile/__init__.py +0 -0
  112. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/tile/blueprint.py +0 -0
  113. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/tile/controller.py +0 -0
  114. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/upload/__init__.py +0 -0
  115. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/upload/blueprint.py +0 -0
  116. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/upload/controller.py +0 -0
  117. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/upload/templates/upload/index.html.j2 +0 -0
  118. {geo_activity_playground-0.30.0 → geo_activity_playground-0.31.0}/geo_activity_playground/webui/upload/templates/upload/reload.html.j2 +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: geo-activity-playground
3
- Version: 0.30.0
3
+ Version: 0.31.0
4
4
  Summary: Analysis of geo data activities like rides, runs or hikes.
5
5
  License: MIT
6
6
  Author: Martin Ueding
@@ -1,5 +1,6 @@
1
1
  import datetime
2
2
  import functools
3
+ import json
3
4
  import logging
4
5
  import pickle
5
6
  from typing import Any
@@ -16,6 +17,7 @@ from tqdm import tqdm
16
17
  from geo_activity_playground.core.paths import activities_file
17
18
  from geo_activity_playground.core.paths import activity_enriched_meta_dir
18
19
  from geo_activity_playground.core.paths import activity_enriched_time_series_dir
20
+ from geo_activity_playground.core.paths import activity_meta_override_dir
19
21
 
20
22
  logger = logging.getLogger(__name__)
21
23
 
@@ -83,7 +85,12 @@ def build_activity_meta() -> None:
83
85
  rows = []
84
86
  for new_id in tqdm(new_ids, desc="Register new activities"):
85
87
  with open(activity_enriched_meta_dir() / f"{new_id}.pickle", "rb") as f:
86
- rows.append(pickle.load(f))
88
+ data = pickle.load(f)
89
+ override_file = activity_meta_override_dir() / f"{new_id}.json"
90
+ if override_file.exists():
91
+ with open(override_file) as f:
92
+ data.update(json.load(f))
93
+ rows.append(data)
87
94
 
88
95
  if rows:
89
96
  new_shard = pd.DataFrame(rows)
@@ -140,7 +147,6 @@ class ActivityRepository:
140
147
  if not dropna or not pd.isna(row["start"]):
141
148
  yield row
142
149
 
143
- @functools.lru_cache()
144
150
  def get_activity_by_id(self, id: int) -> ActivityMeta:
145
151
  activity = self.meta.loc[id]
146
152
  assert isinstance(activity["name"], str), activity["name"]
@@ -158,6 +164,9 @@ class ActivityRepository:
158
164
 
159
165
  return df
160
166
 
167
+ def save(self) -> None:
168
+ self._meta.to_parquet(activities_file())
169
+
161
170
 
162
171
  def make_geojson_from_time_series(time_series: pd.DataFrame) -> str:
163
172
  fc = geojson.FeatureCollection(
@@ -53,6 +53,8 @@ _strava_last_activity_date_path = _cache_dir / "strava-last-activity-date.json"
53
53
 
54
54
  _new_config_file = pathlib.Path("config.json")
55
55
 
56
+ _activity_meta_override_dir = pathlib.Path("Metadata Override")
57
+
56
58
 
57
59
  cache_dir = dir_wrapper(_cache_dir)
58
60
 
@@ -63,6 +65,7 @@ activity_enriched_meta_dir = dir_wrapper(_activity_enriched_meta_dir)
63
65
  activity_enriched_time_series_dir = dir_wrapper(_activity_enriched_time_series_dir)
64
66
  tiles_per_time_series = dir_wrapper(_tiles_per_time_series)
65
67
  strava_api_dir = dir_wrapper(_strava_api_dir)
68
+ activity_meta_override_dir = dir_wrapper(_activity_meta_override_dir)
66
69
 
67
70
  activities_file = file_wrapper(_activities_file)
68
71
  strava_dynamic_config_path = file_wrapper(_strava_dynamic_config_path)
@@ -0,0 +1,109 @@
1
+ import json
2
+ import urllib.parse
3
+ from collections.abc import Collection
4
+
5
+ from flask import Blueprint
6
+ from flask import redirect
7
+ from flask import render_template
8
+ from flask import request
9
+ from flask import Response
10
+ from flask import url_for
11
+
12
+ from ...core.activities import ActivityRepository
13
+ from ...explorer.tile_visits import TileVisitAccessor
14
+ from .controller import ActivityController
15
+ from geo_activity_playground.core.config import Config
16
+ from geo_activity_playground.core.paths import activity_meta_override_dir
17
+ from geo_activity_playground.core.privacy_zones import PrivacyZone
18
+ from geo_activity_playground.webui.authenticator import Authenticator
19
+ from geo_activity_playground.webui.authenticator import needs_authentication
20
+
21
+
22
+ def make_activity_blueprint(
23
+ repository: ActivityRepository,
24
+ tile_visit_accessor: TileVisitAccessor,
25
+ config: Config,
26
+ authenticator: Authenticator,
27
+ ) -> Blueprint:
28
+ blueprint = Blueprint("activity", __name__, template_folder="templates")
29
+
30
+ activity_controller = ActivityController(repository, tile_visit_accessor, config)
31
+
32
+ @blueprint.route("/all")
33
+ def all():
34
+ return render_template(
35
+ "activity/lines.html.j2", **activity_controller.render_all()
36
+ )
37
+
38
+ @blueprint.route("/<id>")
39
+ def show(id: str):
40
+ return render_template(
41
+ "activity/show.html.j2", **activity_controller.render_activity(int(id))
42
+ )
43
+
44
+ @blueprint.route("/<id>/sharepic.png")
45
+ def sharepic(id: str):
46
+ return Response(
47
+ activity_controller.render_sharepic(int(id)),
48
+ mimetype="image/png",
49
+ )
50
+
51
+ @blueprint.route("/day/<year>/<month>/<day>")
52
+ def day(year: str, month: str, day: str):
53
+ return render_template(
54
+ "activity/day.html.j2",
55
+ **activity_controller.render_day(int(year), int(month), int(day)),
56
+ )
57
+
58
+ @blueprint.route("/name/<name>")
59
+ def name(name: str):
60
+ return render_template(
61
+ "activity/name.html.j2",
62
+ **activity_controller.render_name(urllib.parse.unquote(name)),
63
+ )
64
+
65
+ @blueprint.route("/edit/<id>", methods=["GET", "POST"])
66
+ @needs_authentication(authenticator)
67
+ def edit(id: str):
68
+ activity_id = int(id)
69
+ activity = repository.get_activity_by_id(activity_id)
70
+ override_file = activity_meta_override_dir() / f"{activity_id}.json"
71
+ if override_file.exists():
72
+ with open(override_file) as f:
73
+ override = json.load(f)
74
+ else:
75
+ override = {}
76
+
77
+ if request.method == "POST":
78
+ override = {}
79
+ if value := request.form.get("name"):
80
+ override["name"] = value
81
+ repository.meta.loc[activity_id, "name"] = value
82
+ if value := request.form.get("kind"):
83
+ override["kind"] = value
84
+ repository.meta.loc[activity_id, "kind"] = value
85
+ if value := request.form.get("equipment"):
86
+ override["equipment"] = value
87
+ repository.meta.loc[activity_id, "equipment"] = value
88
+ if value := request.form.get("commute"):
89
+ override["commute"] = True
90
+ repository.meta.loc[activity_id, "commute"] = True
91
+ if value := request.form.get("consider_for_achievements"):
92
+ override["consider_for_achievements"] = True
93
+ repository.meta.loc[activity_id, "consider_for_achievements"] = True
94
+
95
+ with open(override_file, "w") as f:
96
+ json.dump(override, f, ensure_ascii=False, indent=4, sort_keys=True)
97
+
98
+ repository.save()
99
+
100
+ return redirect(url_for(".show", id=activity_id))
101
+
102
+ return render_template(
103
+ "activity/edit.html.j2",
104
+ activity_id=activity_id,
105
+ activity=activity,
106
+ override=override,
107
+ )
108
+
109
+ return blueprint
@@ -58,9 +58,9 @@
58
58
  <td><span style="color: {{ activity['color'] }};">█</span> <a
59
59
  href="{{ url_for('activity.show', id=activity.id) }}">{{
60
60
  activity.name }}</a></td>
61
- <td>{{ activity.start }}</td>
61
+ <td>{{ activity.start|dt }}</td>
62
62
  <td>{{ activity.distance_km | round(1) }}</td>
63
- <td>{{ activity.elapsed_time }}</td>
63
+ <td>{{ activity.elapsed_time|td }}</td>
64
64
  <td>{{ activity["equipment"] }}</td>
65
65
  <td>{{ activity["kind"] }}</td>
66
66
  </tr>
@@ -70,7 +70,7 @@
70
70
  <td><b>Total</b></td>
71
71
  <td></td>
72
72
  <td><b>{{ total_distance | round(1) }}</b></td>
73
- <td><b>{{ total_elapsed_time }}</b></td>
73
+ <td><b>{{ total_elapsed_time|td }}</b></td>
74
74
  <td></td>
75
75
  <td></td>
76
76
  </tr>
@@ -0,0 +1,42 @@
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="{{ override['name'] }}" />
11
+ </div>
12
+
13
+ <div class="mb-3">
14
+ <label for="kind" class="form-label">Kind</label>
15
+ <input type="text" class="form-control" id="kind" name="kind" value="{{ override['kind'] }}" />
16
+ </div>
17
+
18
+ <div class="mb-3">
19
+ <label for="equipment" class="form-label">Equipment</label>
20
+ <input type="text" class="form-control" id="equipment" name="equipment" value="{{ override['equipment'] }}" />
21
+ </div>
22
+
23
+ <div class="mb-3">
24
+ <div class="form-check">
25
+ <input type="checkbox" class="form-check-input" id="commute" name="commute" {% if override['commute'] %}
26
+ checked {% endif %} />
27
+ <label for="commute" class="form-check-label">Commute</label>
28
+ </div>
29
+ </div>
30
+
31
+ <div class="mb-3">
32
+ <div class="form-check">
33
+ <input type="checkbox" class="form-check-input" id="consider_for_achievements"
34
+ name="consider_for_achievements" {% if override['consider_for_achievements'] %} checked {% endif %} />
35
+ <label for="consider_for_achievements" class="form-check-label">Consider for achievements</label>
36
+ </div>
37
+ </div>
38
+
39
+ <button type="submit" class="btn btn-primary">Save</button>
40
+ </form>
41
+
42
+ {% endblock %}
@@ -66,9 +66,9 @@
66
66
  <tr>
67
67
  <td><span style="color: {{ activity['color'] }};">█</span> <a href="{{ url_for('activity.show', id=activity.id) }}">{{
68
68
  activity.name }}</a></td>
69
- <td>{{ activity.start }}</td>
69
+ <td>{{ activity.start|dt }}</td>
70
70
  <td>{{ activity.distance_km | round(1) }}</td>
71
- <td>{{ activity.elapsed_time }}</td>
71
+ <td>{{ activity.elapsed_time|td }}</td>
72
72
  <td>{{ activity["equipment"] }}</td>
73
73
  <td>{{ activity["kind"] }}</td>
74
74
  </tr>
@@ -19,9 +19,9 @@
19
19
  <dt>Distance</dt>
20
20
  <dd>{{ activity.distance_km|round(1) }} km</dd>
21
21
  <dt>Elapsed time</dt>
22
- <dd>{{ activity.elapsed_time }}</dd>
22
+ <dd>{{ activity.elapsed_time|td }}</dd>
23
23
  <dt>Moving time</dt>
24
- <dd>{{ activity.moving_time }}</dd>
24
+ <dd>{{ activity.moving_time|td }}</dd>
25
25
  <dt>Start time</dt>
26
26
  <dd><a href="{{ url_for('activity.day', year=date.year, month=date.month, day=date.day) }}">{{ date }}</a>
27
27
  {{ time }}
@@ -31,7 +31,7 @@
31
31
  <dt>Steps</dt>
32
32
  <dd>{{ activity.steps }}</dd>
33
33
  <dt>Equipment</dt>
34
- <dd>{{ activity.equipment }}</dd>
34
+ <dd>{{ activity['equipment'] }}</dd>
35
35
  <dt>New Explorer Tiles</dt>
36
36
  <dd>{{ new_tiles[14] }}</dd>
37
37
  <dt>New Squadratinhos</dt>
@@ -41,6 +41,8 @@
41
41
  <dt>Source path</dt>
42
42
  <dd>{{ activity.path }}</dd>
43
43
  </dl>
44
+
45
+ <a href="{{ url_for('.edit', id=activity['id']) }}" class="btn btn-secondary btn-small">Edit metadata</a>
44
46
  </div>
45
47
  <div class="col-8">
46
48
  <div id="activity-map" style="height: 500px;" class="mb-3"></div>
@@ -201,10 +203,10 @@
201
203
  <tbody>
202
204
  {% for other_activity in similar_activites %}
203
205
  <tr>
204
- <td><a href="{{ url_for('.show', id=other_activity.id) }}">{{ other_activity.start
206
+ <td><a href="{{ url_for('.show', id=other_activity.id) }}">{{ other_activity.start|dt
205
207
  }}</a></td>
206
208
  <td>{{ other_activity.distance_km | round(1) }}</td>
207
- <td>{{ other_activity.elapsed_time }}</td>
209
+ <td>{{ other_activity.elapsed_time|td }}</td>
208
210
  <td>{{ other_activity["equipment"] }}</td>
209
211
  <td>{{ other_activity["kind"] }}</td>
210
212
  </tr>
@@ -1,3 +1,4 @@
1
+ import datetime
1
2
  import importlib
2
3
  import json
3
4
  import pathlib
@@ -60,6 +61,18 @@ def web_ui_main(
60
61
  app.config["UPLOAD_FOLDER"] = "Activities"
61
62
  app.secret_key = get_secret_key()
62
63
 
64
+ @app.template_filter()
65
+ def dt(value: datetime.datetime):
66
+ return value.strftime("%Y-%m-%d %H:%M")
67
+
68
+ @app.template_filter()
69
+ def td(v: datetime.timedelta):
70
+ seconds = v.total_seconds()
71
+ h = int(seconds // 3600)
72
+ m = int(seconds // 60 % 60)
73
+ s = int(seconds // 1 % 60)
74
+ return f"{h}:{m:02d}:{s:02d}"
75
+
63
76
  authenticator = Authenticator(config_accessor())
64
77
 
65
78
  route_start(app, repository, config_accessor())
@@ -68,9 +81,7 @@ def web_ui_main(
68
81
 
69
82
  app.register_blueprint(
70
83
  make_activity_blueprint(
71
- repository,
72
- tile_visit_accessor,
73
- config_accessor(),
84
+ repository, tile_visit_accessor, config_accessor(), authenticator
74
85
  ),
75
86
  url_prefix="/activity",
76
87
  )
@@ -90,23 +90,25 @@ class HeatmapController:
90
90
  tile_counts = np.zeros(tile_pixels, dtype=np.int32)
91
91
  tile_count_cache_path.parent.mkdir(parents=True, exist_ok=True)
92
92
  activity_ids = self.activities_per_tile[z].get((x, y), set())
93
- if activity_ids:
93
+ activity_ids_kind = set()
94
+ for activity_id in activity_ids:
95
+ activity = self._repository.get_activity_by_id(activity_id)
96
+ if activity["kind"] == kind:
97
+ activity_ids_kind.add(activity_id)
98
+ if activity_ids_kind:
94
99
  with work_tracker(
95
100
  tile_count_cache_path.with_suffix(".json")
96
101
  ) as parsed_activities:
97
- if parsed_activities - activity_ids:
102
+ if parsed_activities - activity_ids_kind:
98
103
  logger.warning(
99
104
  f"Resetting heatmap cache for {kind=}/{x=}/{y=}/{z=} because activities have been removed."
100
105
  )
101
106
  tile_counts = np.zeros(tile_pixels, dtype=np.int32)
102
107
  parsed_activities.clear()
103
- for activity_id in activity_ids:
108
+ for activity_id in activity_ids_kind:
104
109
  if activity_id in parsed_activities:
105
110
  continue
106
111
  parsed_activities.add(activity_id)
107
- activity = self._repository.get_activity_by_id(activity_id)
108
- if activity["kind"] != kind:
109
- continue
110
112
  time_series = self._repository.get_time_series(activity_id)
111
113
  for _, group in time_series.groupby("segment_id"):
112
114
  xy_pixels = (
@@ -78,10 +78,10 @@
78
78
  {% for index, activity in activities %}
79
79
  <tr>
80
80
  <td><a href="{{ url_for('activity.show', id=activity['id']) }}">{{ activity['name'] }}</a></td>
81
- <td>{{ activity['start'] }}</td>
81
+ <td>{{ activity['start']|dt }}</td>
82
82
  <td>{{ activity['kind'] }}</td>
83
83
  <td>{{ '%.1f' % activity["distance_km"] }} km</td>
84
- <td>{{ activity.elapsed_time }}</td>
84
+ <td>{{ activity.elapsed_time|td }}</td>
85
85
  </tr>
86
86
  {% endfor %}
87
87
  </tbody>
@@ -123,7 +123,7 @@
123
123
  </p>
124
124
  <p class="card-text"><small class="text-body-secondary"></small>{{ activity.kind }} with {{
125
125
  (activity.distance_km)|round(1) }} km in {{
126
- activity.elapsed_time }} on {{ activity.start }}</small></p>
126
+ activity.elapsed_time|td }} on {{ activity.start|dt }}</small></p>
127
127
  </div>
128
128
  </div>
129
129
  </div>
@@ -49,8 +49,8 @@
49
49
  <h5 class="card-title">{{ elem.activity["name"] }}</h5>
50
50
  </a>
51
51
  <p class="card-text">{{ elem.activity.kind }} with {{ (elem.activity.distance_km)|round(1) }} km in {{
52
- elem.activity.elapsed_time }}</p>
53
- <p class="card-text"><small class="text-body-secondary">{{ elem.activity.start }}</small></p>
52
+ elem.activity.elapsed_time|td }}</p>
53
+ <p class="card-text"><small class="text-body-secondary">{{ elem.activity.start|dt }}</small></p>
54
54
  </div>
55
55
  </div>
56
56
  </div>
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "geo-activity-playground"
3
- version = "0.30.0"
3
+ version = "0.31.0"
4
4
  description = "Analysis of geo data activities like rides, runs or hikes."
5
5
  authors = ["Martin Ueding <mu@martin-ueding.de>"]
6
6
  license = "MIT"
@@ -1,57 +0,0 @@
1
- import urllib.parse
2
- from collections.abc import Collection
3
-
4
- from flask import Blueprint
5
- from flask import render_template
6
- from flask import Response
7
-
8
- from ...core.activities import ActivityRepository
9
- from ...explorer.tile_visits import TileVisitAccessor
10
- from .controller import ActivityController
11
- from geo_activity_playground.core.config import Config
12
- from geo_activity_playground.core.privacy_zones import PrivacyZone
13
-
14
-
15
- def make_activity_blueprint(
16
- repository: ActivityRepository,
17
- tile_visit_accessor: TileVisitAccessor,
18
- config: Config,
19
- ) -> Blueprint:
20
- blueprint = Blueprint("activity", __name__, template_folder="templates")
21
-
22
- activity_controller = ActivityController(repository, tile_visit_accessor, config)
23
-
24
- @blueprint.route("/all")
25
- def all():
26
- return render_template(
27
- "activity/lines.html.j2", **activity_controller.render_all()
28
- )
29
-
30
- @blueprint.route("/<id>")
31
- def show(id: str):
32
- return render_template(
33
- "activity/show.html.j2", **activity_controller.render_activity(int(id))
34
- )
35
-
36
- @blueprint.route("/<id>/sharepic.png")
37
- def sharepic(id: str):
38
- return Response(
39
- activity_controller.render_sharepic(int(id)),
40
- mimetype="image/png",
41
- )
42
-
43
- @blueprint.route("/day/<year>/<month>/<day>")
44
- def day(year: str, month: str, day: str):
45
- return render_template(
46
- "activity/day.html.j2",
47
- **activity_controller.render_day(int(year), int(month), int(day))
48
- )
49
-
50
- @blueprint.route("/name/<name>")
51
- def name(name: str):
52
- return render_template(
53
- "activity/name.html.j2",
54
- **activity_controller.render_name(urllib.parse.unquote(name))
55
- )
56
-
57
- return blueprint