geo-activity-playground 0.29.0__tar.gz → 0.29.2__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.
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/PKG-INFO +1 -1
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/core/activities.py +7 -7
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/core/time_conversion.py +1 -1
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/explorer/tile_visits.py +2 -2
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/importers/activity_parsers.py +28 -17
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/importers/csv_parser.py +1 -2
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/importers/directory.py +2 -1
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/importers/strava_api.py +1 -1
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/activity/controller.py +3 -1
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/activity/templates/activity/show.html.j2 +1 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/explorer/controller.py +2 -2
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/settings/blueprint.py +2 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/settings/controller.py +4 -3
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/settings/templates/settings/metadata-extraction.html.j2 +10 -11
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/square_planner/controller.py +2 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/upload/controller.py +1 -1
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/pyproject.toml +1 -1
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/LICENSE +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/__init__.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/__main__.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/core/__init__.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/core/config.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/core/coordinates.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/core/enrichment.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/core/heart_rate.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/core/heatmap.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/core/paths.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/core/privacy_zones.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/core/similarity.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/core/tasks.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/core/test_tiles.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/core/test_time_conversion.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/core/tiles.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/explorer/__init__.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/explorer/grid_file.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/explorer/video.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/importers/__init__.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/importers/strava_checkout.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/importers/test_csv_parser.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/importers/test_directory.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/importers/test_strava_api.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/__init__.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/activity/__init__.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/activity/blueprint.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/activity/templates/activity/day.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/activity/templates/activity/lines.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/activity/templates/activity/name.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/app.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/auth/blueprint.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/auth/templates/auth/index.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/authenticator.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/calendar/__init__.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/calendar/blueprint.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/calendar/controller.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/calendar/templates/calendar/index.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/calendar/templates/calendar/month.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/eddington/__init__.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/eddington/blueprint.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/eddington/controller.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/eddington/templates/eddington/index.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/entry_controller.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/equipment/__init__.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/equipment/blueprint.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/equipment/controller.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/equipment/templates/equipment/index.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/explorer/__init__.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/explorer/blueprint.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/explorer/templates/explorer/index.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/heatmap/__init__.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/heatmap/blueprint.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/heatmap/heatmap_controller.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/heatmap/templates/heatmap/index.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/plot_util.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/search/blueprint.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/search/templates/search/index.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/settings/templates/settings/admin-password.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/settings/templates/settings/color-schemes.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/settings/templates/settings/equipment-offsets.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/settings/templates/settings/heart-rate.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/settings/templates/settings/index.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/settings/templates/settings/kinds-without-achievements.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/settings/templates/settings/privacy-zones.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/settings/templates/settings/sharepic.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/settings/templates/settings/strava.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/square_planner/__init__.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/square_planner/blueprint.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/square_planner/templates/square_planner/index.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/static/android-chrome-192x192.png +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/static/android-chrome-384x384.png +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/static/android-chrome-512x512.png +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/static/apple-touch-icon.png +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/static/bootstrap-dark-mode.js +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/static/browserconfig.xml +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/static/favicon-16x16.png +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/static/favicon-32x32.png +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/static/favicon.ico +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/static/mstile-150x150.png +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/static/safari-pinned-tab.svg +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/static/site.webmanifest +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/summary/__init__.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/summary/blueprint.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/summary/controller.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/summary/templates/summary/index.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/templates/home.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/templates/page.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/tile/__init__.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/tile/blueprint.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/tile/controller.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/upload/__init__.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/upload/blueprint.py +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/upload/templates/upload/index.html.j2 +0 -0
- {geo_activity_playground-0.29.0 → geo_activity_playground-0.29.2}/geo_activity_playground/webui/upload/templates/upload/reload.html.j2 +0 -0
@@ -2,6 +2,7 @@ import datetime
|
|
2
2
|
import functools
|
3
3
|
import logging
|
4
4
|
import pickle
|
5
|
+
from typing import Any
|
5
6
|
from typing import Iterator
|
6
7
|
from typing import Optional
|
7
8
|
from typing import TypedDict
|
@@ -88,7 +89,10 @@ def build_activity_meta() -> None:
|
|
88
89
|
new_shard = pd.DataFrame(rows)
|
89
90
|
new_shard.index = new_shard["id"]
|
90
91
|
new_shard.index.name = "index"
|
91
|
-
|
92
|
+
if len(meta):
|
93
|
+
meta = pd.concat([meta, new_shard])
|
94
|
+
else:
|
95
|
+
meta = new_shard
|
92
96
|
|
93
97
|
if len(meta):
|
94
98
|
assert pd.api.types.is_dtype_equal(meta["start"].dtype, "datetime64[ns]"), (
|
@@ -103,7 +107,7 @@ def build_activity_meta() -> None:
|
|
103
107
|
|
104
108
|
class ActivityRepository:
|
105
109
|
def __init__(self) -> None:
|
106
|
-
self.meta =
|
110
|
+
self.meta = pd.DataFrame()
|
107
111
|
|
108
112
|
def __len__(self) -> int:
|
109
113
|
return len(self.meta)
|
@@ -116,10 +120,6 @@ class ActivityRepository:
|
|
116
120
|
if activity_id in self.meta["id"]:
|
117
121
|
return True
|
118
122
|
|
119
|
-
for activity_meta in self._loose_activities:
|
120
|
-
if activity_meta["id"] == activity_id:
|
121
|
-
return True
|
122
|
-
|
123
123
|
return False
|
124
124
|
|
125
125
|
def last_activity_date(self) -> Optional[datetime.datetime]:
|
@@ -198,7 +198,7 @@ def make_geojson_color_line(time_series: pd.DataFrame) -> str:
|
|
198
198
|
return geojson.dumps(feature_collection)
|
199
199
|
|
200
200
|
|
201
|
-
def make_speed_color_bar(time_series: pd.DataFrame) -> dict[str,
|
201
|
+
def make_speed_color_bar(time_series: pd.DataFrame) -> dict[str, Any]:
|
202
202
|
speed_without_na = time_series["speed"].dropna()
|
203
203
|
low = min(speed_without_na)
|
204
204
|
high = max(speed_without_na)
|
@@ -59,7 +59,7 @@ class TileEvolutionState:
|
|
59
59
|
class TileState(TypedDict):
|
60
60
|
tile_visits: dict[int, dict[tuple[int, int], TileInfo]]
|
61
61
|
tile_history: dict[int, pd.DataFrame]
|
62
|
-
activities_per_tile: dict[int, set[int]]
|
62
|
+
activities_per_tile: dict[int, dict[tuple[int, int], set[int]]]
|
63
63
|
processed_activities: set[int]
|
64
64
|
evolution_state: dict[int, TileEvolutionState]
|
65
65
|
version: int
|
@@ -165,7 +165,7 @@ def _process_activity(
|
|
165
165
|
for zoom in reversed(range(20)):
|
166
166
|
activities_per_tile = tile_state["activities_per_tile"][zoom]
|
167
167
|
|
168
|
-
new_tile_history_soa = {
|
168
|
+
new_tile_history_soa: dict[str, list] = {
|
169
169
|
"activity_id": [],
|
170
170
|
"time": [],
|
171
171
|
"tile_x": [],
|
@@ -3,10 +3,10 @@ import gzip
|
|
3
3
|
import logging
|
4
4
|
import pathlib
|
5
5
|
import xml
|
6
|
+
from collections.abc import Iterator
|
6
7
|
|
7
8
|
import charset_normalizer
|
8
9
|
import dateutil.parser
|
9
|
-
import fitdecode
|
10
10
|
import fitdecode.exceptions
|
11
11
|
import gpxpy
|
12
12
|
import pandas as pd
|
@@ -246,26 +246,37 @@ def read_kml_activity(path: pathlib.Path, opener) -> pd.DataFrame:
|
|
246
246
|
with opener(path, "rb") as f:
|
247
247
|
kml_dict = xmltodict.parse(f)
|
248
248
|
doc = kml_dict["kml"]["Document"]
|
249
|
-
keypoint_folder = doc["Folder"]
|
250
|
-
placemark = keypoint_folder["Placemark"]
|
251
|
-
track = placemark["gx:Track"]
|
252
249
|
rows = []
|
253
|
-
for
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
250
|
+
for keypoint_folder in _list_or_scalar(doc["Folder"]):
|
251
|
+
for placemark in _list_or_scalar(keypoint_folder["Placemark"]):
|
252
|
+
for track in _list_or_scalar(placemark.get("gx:Track", [])):
|
253
|
+
for when, where in zip(track["when"], track["gx:coord"]):
|
254
|
+
time = dateutil.parser.parse(when)
|
255
|
+
time = convert_to_datetime_ns(time)
|
256
|
+
parts = where.split(" ")
|
257
|
+
if len(parts) == 2:
|
258
|
+
lon, lat = parts
|
259
|
+
alt = None
|
260
|
+
if len(parts) == 3:
|
261
|
+
lon, lat, alt = parts
|
262
|
+
row = {
|
263
|
+
"time": time,
|
264
|
+
"latitude": float(lat),
|
265
|
+
"longitude": float(lon),
|
266
|
+
}
|
267
|
+
if alt is not None:
|
268
|
+
row["altitude"] = float(alt)
|
269
|
+
rows.append(row)
|
266
270
|
return pd.DataFrame(rows)
|
267
271
|
|
268
272
|
|
273
|
+
def _list_or_scalar(thing) -> Iterator:
|
274
|
+
if isinstance(thing, list):
|
275
|
+
yield from thing
|
276
|
+
else:
|
277
|
+
yield thing
|
278
|
+
|
279
|
+
|
269
280
|
def read_simra_activity(path: pathlib.Path, opener) -> pd.DataFrame:
|
270
281
|
data = pd.read_csv(path, header=1)
|
271
282
|
data["time"] = data["timeStamp"].apply(
|
@@ -20,9 +20,8 @@ This module implements a "recursive descent parser" that parses this grammar.
|
|
20
20
|
|
21
21
|
def parse_csv(text: str) -> list[list]:
|
22
22
|
text = text.strip() + "\n"
|
23
|
-
result = {}
|
24
23
|
index = 0
|
25
|
-
result = []
|
24
|
+
result: list[list] = []
|
26
25
|
while index < len(text):
|
27
26
|
line, index = _parse_line(text, index)
|
28
27
|
result.append(line)
|
@@ -126,11 +126,12 @@ def _cache_single_file(path: pathlib.Path) -> Optional[tuple[pathlib.Path, str]]
|
|
126
126
|
raise
|
127
127
|
|
128
128
|
if len(timeseries) == 0:
|
129
|
-
return
|
129
|
+
return None
|
130
130
|
|
131
131
|
timeseries.to_parquet(timeseries_path)
|
132
132
|
with open(file_metadata_path, "wb") as f:
|
133
133
|
pickle.dump(activity_meta_from_file, f)
|
134
|
+
return None
|
134
135
|
|
135
136
|
|
136
137
|
def get_file_hash(path: pathlib.Path) -> int:
|
@@ -137,7 +137,7 @@ def try_import_strava(config: Config) -> bool:
|
|
137
137
|
**{
|
138
138
|
"id": activity.id,
|
139
139
|
"commute": activity.commute,
|
140
|
-
"distance_km": activity.distance
|
140
|
+
"distance_km": activity.distance / 1000,
|
141
141
|
"name": activity.name,
|
142
142
|
"kind": str(activity.type),
|
143
143
|
"start": convert_to_datetime_ns(activity.start_date),
|
@@ -72,6 +72,7 @@ class ActivityController:
|
|
72
72
|
}
|
73
73
|
|
74
74
|
new_tiles_geojson = {}
|
75
|
+
new_tiles_per_zoom = {}
|
75
76
|
for zoom in sorted(self._config.explorer_zoom_levels):
|
76
77
|
new_tiles = self._tile_visit_accessor.tile_state["tile_history"][zoom].loc[
|
77
78
|
self._tile_visit_accessor.tile_state["tile_history"][zoom][
|
@@ -88,6 +89,7 @@ class ActivityController:
|
|
88
89
|
zoom,
|
89
90
|
)
|
90
91
|
new_tiles_geojson[zoom] = make_grid_file_geojson(points)
|
92
|
+
new_tiles_per_zoom[zoom] = len(new_tiles)
|
91
93
|
|
92
94
|
result = {
|
93
95
|
"activity": activity,
|
@@ -100,7 +102,7 @@ class ActivityController:
|
|
100
102
|
"speed_color_bar": make_speed_color_bar(time_series),
|
101
103
|
"date": activity["start"].date(),
|
102
104
|
"time": activity["start"].time(),
|
103
|
-
"new_tiles":
|
105
|
+
"new_tiles": new_tiles_per_zoom,
|
104
106
|
"new_tiles_geojson": new_tiles_geojson,
|
105
107
|
}
|
106
108
|
if (
|
@@ -160,6 +160,7 @@
|
|
160
160
|
{% for zoom, geojson in new_tiles_geojson.items() %}
|
161
161
|
<div class="col-md-6">
|
162
162
|
<h3>Zoom {{ zoom }}</h3>
|
163
|
+
<p>There are {{ new_tiles[zoom] }} new tiles:</p>
|
163
164
|
<div id="map-{{ zoom }}" style="height: 300px; width: 100%;"></div>
|
164
165
|
<script>
|
165
166
|
let map{{ zoom }} = add_map("{{ zoom }}", {{ geojson | safe }})
|
@@ -95,7 +95,7 @@ class ExplorerController:
|
|
95
95
|
x2, y2 = compute_tile(south, east, zoom)
|
96
96
|
tile_bounds = Bounds(x1, y1, x2 + 2, y2 + 2)
|
97
97
|
|
98
|
-
tile_histories = self._tile_visit_accessor.
|
98
|
+
tile_histories = self._tile_visit_accessor.tile_state["tile_history"]
|
99
99
|
tiles = tile_histories[zoom]
|
100
100
|
points = get_border_tiles(tiles, zoom, tile_bounds)
|
101
101
|
if suffix == "geojson":
|
@@ -108,7 +108,7 @@ class ExplorerController:
|
|
108
108
|
x2, y2 = compute_tile(south, east, zoom)
|
109
109
|
tile_bounds = Bounds(x1, y1, x2 + 2, y2 + 2)
|
110
110
|
|
111
|
-
tile_visits = self._tile_visit_accessor.
|
111
|
+
tile_visits = self._tile_visit_accessor.tile_state["tile_visits"]
|
112
112
|
tiles = tile_visits[zoom]
|
113
113
|
points = make_grid_points(
|
114
114
|
(tile for tile in tiles.keys() if tile_bounds.contains(*tile)), zoom
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import json
|
2
2
|
import re
|
3
3
|
import urllib.parse
|
4
|
+
from typing import Any
|
4
5
|
from typing import Optional
|
5
6
|
|
6
7
|
from flask import flash
|
@@ -70,7 +71,7 @@ class SettingsController:
|
|
70
71
|
flash("Updated equipment offsets.", category="success")
|
71
72
|
|
72
73
|
def render_heart_rate(self) -> dict:
|
73
|
-
result = {
|
74
|
+
result: dict[str, Any] = {
|
74
75
|
"birth_year": self._config_accessor().birth_year,
|
75
76
|
"heart_rate_resting": self._config_accessor().heart_rate_resting,
|
76
77
|
"heart_rate_maximum": self._config_accessor().heart_rate_maximum,
|
@@ -90,7 +91,7 @@ class SettingsController:
|
|
90
91
|
heart_rate_maximum: Optional[int],
|
91
92
|
) -> None:
|
92
93
|
self._config_accessor().birth_year = birth_year
|
93
|
-
self._config_accessor().heart_rate_resting = heart_rate_resting
|
94
|
+
self._config_accessor().heart_rate_resting = heart_rate_resting or 0
|
94
95
|
self._config_accessor().heart_rate_maximum = heart_rate_maximum
|
95
96
|
self._config_accessor.save()
|
96
97
|
flash("Updated heart rate data.", category="success")
|
@@ -251,7 +252,7 @@ class SettingsController:
|
|
251
252
|
return f"https://www.strava.com/oauth/authorize?{arg_string}"
|
252
253
|
|
253
254
|
def save_strava_code(self, code: str) -> None:
|
254
|
-
self._config_accessor().strava_client_id = self._strava_client_id
|
255
|
+
self._config_accessor().strava_client_id = int(self._strava_client_id)
|
255
256
|
self._config_accessor().strava_client_secret = self._strava_client_secret
|
256
257
|
self._config_accessor().strava_client_code = code
|
257
258
|
self._config_accessor.save()
|
@@ -4,8 +4,7 @@
|
|
4
4
|
|
5
5
|
<h1 class="mb-3">Metadata Extraction</h1>
|
6
6
|
|
7
|
-
<p>
|
8
|
-
default value. These are:</p>
|
7
|
+
<p>If the current activity metadata is not to your liking, you can adjust how these fields are populated:</p>
|
9
8
|
|
10
9
|
<ul>
|
11
10
|
<li><tt>kind</tt>: The kind of the activity, like "Ride" or "Run".</li>
|
@@ -13,8 +12,9 @@
|
|
13
12
|
<li><tt>name</tt>: Name for the activity, like "Ride with Friends".</li>
|
14
13
|
</ul>
|
15
14
|
|
16
|
-
<p>By default these fields are
|
17
|
-
|
15
|
+
<p>By default these fields are extracted from files that contain this data. Otherwise the filename is set as <tt>name</tt>.
|
16
|
+
You can overwrite this by setting up a directory structure with corresponding regular expressions using named capture groups.</p>
|
17
|
+
|
18
18
|
|
19
19
|
<form method="POST">
|
20
20
|
<div class="row">
|
@@ -32,19 +32,18 @@
|
|
32
32
|
</div>
|
33
33
|
|
34
34
|
<div class="col-md-6">
|
35
|
-
<p>
|
36
|
-
<tt>
|
37
|
-
|
38
|
-
following regular expression:
|
39
|
-
</p>
|
35
|
+
<p>Consider a directory structure <tt>Ride/Red Roadbike/2024-08-10 11-45-00 Ride with Friends.fit</tt> under Activities.
|
36
|
+
You can extract <tt>kind</tt>: "Ride", <tt>equipment</tt>: "Red Roadbike", <tt>name</tt>: "Ride with Friends"
|
37
|
+
with the following regular expression:</p>
|
40
38
|
|
41
39
|
<div class="code">
|
42
40
|
<pre
|
43
41
|
class="code literal-block">(?P<kind>[^/]+)/(?P<equipment>[^/]+)/[-\d_ .]+(?P<name>[^/\.]+)</pre>
|
44
42
|
</div>
|
45
43
|
|
46
|
-
<p>
|
47
|
-
|
44
|
+
<p>Have a look at the documentation
|
45
|
+
<a href="https://martin-ueding.github.io/geo-activity-playground/getting-started/advanced-metadata-extraction">Advanced Metadata Extraction</a>
|
46
|
+
for explanations and examples.</p>
|
48
47
|
</div>
|
49
48
|
</div>
|
50
49
|
|
@@ -79,6 +79,8 @@ class SquarePlannerController:
|
|
79
79
|
return make_grid_file_geojson(points)
|
80
80
|
elif suffix == "gpx":
|
81
81
|
return make_grid_file_gpx(points)
|
82
|
+
else:
|
83
|
+
raise RuntimeError(f"Unsupported suffix {suffix}.")
|
82
84
|
|
83
85
|
def _get_explored_tiles(self, zoom: int) -> set[tuple[int, int]]:
|
84
86
|
return set(self._tile_visits[zoom].keys())
|
@@ -60,7 +60,7 @@ class UploadController:
|
|
60
60
|
flash("No selected file", "warning")
|
61
61
|
return redirect("/upload")
|
62
62
|
if file:
|
63
|
-
filename =
|
63
|
+
filename = file.filename
|
64
64
|
target_path = pathlib.Path(request.form["directory"]) / filename
|
65
65
|
assert target_path.suffix in [
|
66
66
|
".csv",
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|