geo-activity-playground 1.5.2__py3-none-any.whl → 1.7.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 +8 -0
- geo_activity_playground/core/datamodel.py +28 -9
- geo_activity_playground/core/enrichment.py +2 -4
- geo_activity_playground/core/meta_search.py +1 -3
- geo_activity_playground/core/photos.py +7 -3
- geo_activity_playground/core/test_meta_search.py +4 -4
- geo_activity_playground/core/test_time_zone_from_location.py +6 -0
- geo_activity_playground/core/time_conversion.py +7 -0
- geo_activity_playground/explorer/tile_visits.py +23 -10
- geo_activity_playground/importers/activity_parsers.py +15 -9
- geo_activity_playground/importers/directory.py +16 -1
- geo_activity_playground/importers/strava_api.py +24 -6
- geo_activity_playground/importers/strava_checkout.py +2 -0
- geo_activity_playground/webui/app.py +18 -3
- geo_activity_playground/webui/blueprints/eddington_blueprints.py +1 -5
- geo_activity_playground/webui/blueprints/hall_of_fame_blueprint.py +1 -1
- geo_activity_playground/webui/blueprints/heatmap_blueprint.py +1 -1
- geo_activity_playground/webui/blueprints/photo_blueprint.py +1 -0
- geo_activity_playground/webui/blueprints/search_blueprint.py +1 -1
- geo_activity_playground/webui/blueprints/summary_blueprint.py +1 -1
- geo_activity_playground/webui/blueprints/upload_blueprint.py +6 -1
- geo_activity_playground/webui/static/activity-trim.js +39 -0
- geo_activity_playground/webui/templates/activity/trim.html.j2 +3 -38
- {geo_activity_playground-1.5.2.dist-info → geo_activity_playground-1.7.0.dist-info}/METADATA +2 -1
- {geo_activity_playground-1.5.2.dist-info → geo_activity_playground-1.7.0.dist-info}/RECORD +28 -27
- {geo_activity_playground-1.5.2.dist-info → geo_activity_playground-1.7.0.dist-info}/LICENSE +0 -0
- {geo_activity_playground-1.5.2.dist-info → geo_activity_playground-1.7.0.dist-info}/WHEEL +0 -0
- {geo_activity_playground-1.5.2.dist-info → geo_activity_playground-1.7.0.dist-info}/entry_points.txt +0 -0
@@ -53,6 +53,8 @@ def main() -> None:
|
|
53
53
|
options.skip_reload,
|
54
54
|
host=options.host,
|
55
55
|
port=options.port,
|
56
|
+
strava_begin=options.strava_begin,
|
57
|
+
strava_end=options.strava_end,
|
56
58
|
)
|
57
59
|
)
|
58
60
|
subparser.add_argument(
|
@@ -62,6 +64,12 @@ def main() -> None:
|
|
62
64
|
"--port", default=5000, type=int, help="the port to run listen on"
|
63
65
|
)
|
64
66
|
subparser.add_argument("--skip-reload", action=argparse.BooleanOptionalAction)
|
67
|
+
subparser.add_argument(
|
68
|
+
"--strava-begin", help="Start date to limit Strava sync, format YYYY-MM-DD"
|
69
|
+
)
|
70
|
+
subparser.add_argument(
|
71
|
+
"--strava-end", help="End date to limit Strava sync, format YYYY-MM-DD"
|
72
|
+
)
|
65
73
|
|
66
74
|
subparser = subparsers.add_parser(
|
67
75
|
"heatmap-video", help="Create a video with the evolution of the heatmap"
|
@@ -231,7 +231,7 @@ class Activity(DB.Model):
|
|
231
231
|
return self.start
|
232
232
|
|
233
233
|
@property
|
234
|
-
def
|
234
|
+
def start_local(self) -> Optional[datetime.datetime]:
|
235
235
|
if self.start:
|
236
236
|
return self.start.replace(microsecond=0, tzinfo=zoneinfo.ZoneInfo("UTC"))
|
237
237
|
else:
|
@@ -270,6 +270,7 @@ def query_activity_meta(clauses: list = []) -> pd.DataFrame:
|
|
270
270
|
Activity.path,
|
271
271
|
Activity.distance_km,
|
272
272
|
Activity.start,
|
273
|
+
Activity.iana_timezone,
|
273
274
|
Activity.elapsed_time,
|
274
275
|
Activity.moving_time,
|
275
276
|
Activity.start_latitude,
|
@@ -302,6 +303,24 @@ def query_activity_meta(clauses: list = []) -> pd.DataFrame:
|
|
302
303
|
# df["start"] = pd.Series(start)
|
303
304
|
df["elapsed_time"] = pd.to_timedelta(df["elapsed_time"])
|
304
305
|
|
306
|
+
start_local = []
|
307
|
+
for start, iana_timezone in zip(df["start"], df["iana_timezone"]):
|
308
|
+
if pd.isna(start) or iana_timezone is None:
|
309
|
+
start_local.append(start)
|
310
|
+
else:
|
311
|
+
start_local.append(
|
312
|
+
start.tz_localize(zoneinfo.ZoneInfo("UTC"))
|
313
|
+
.tz_convert(iana_timezone)
|
314
|
+
.tz_localize(None)
|
315
|
+
)
|
316
|
+
df["start_local"] = start_local
|
317
|
+
|
318
|
+
# Work around bytes stored in DB.
|
319
|
+
df["calories"] = [
|
320
|
+
sum(a * 256**b for b, a in enumerate(c)) if isinstance(c, bytes) else c
|
321
|
+
for c in df["calories"]
|
322
|
+
]
|
323
|
+
|
305
324
|
for old, new in [
|
306
325
|
("elapsed_time", "average_speed_elapsed_kmh"),
|
307
326
|
("moving_time", "average_speed_moving_kmh"),
|
@@ -312,14 +331,14 @@ def query_activity_meta(clauses: list = []) -> pd.DataFrame:
|
|
312
331
|
df.loc[mask, old].dt.total_seconds() / 3_600
|
313
332
|
)
|
314
333
|
|
315
|
-
df["date"] = df["
|
316
|
-
df["year"] = df["
|
317
|
-
df["month"] = df["
|
318
|
-
df["day"] = df["
|
319
|
-
df["week"] = df["
|
320
|
-
df["day_of_week"] = df["
|
321
|
-
df["iso_year"] = df["
|
322
|
-
df["iso_day"] = df["
|
334
|
+
df["date"] = df["start_local"].dt.date
|
335
|
+
df["year"] = df["start_local"].dt.year
|
336
|
+
df["month"] = df["start_local"].dt.month
|
337
|
+
df["day"] = df["start_local"].dt.day
|
338
|
+
df["week"] = df["start_local"].dt.isocalendar().week
|
339
|
+
df["day_of_week"] = df["start_local"].dt.day_of_week
|
340
|
+
df["iso_year"] = df["start_local"].dt.isocalendar().year
|
341
|
+
df["iso_day"] = df["start_local"].dt.isocalendar().day
|
323
342
|
df["hours"] = df["elapsed_time"].dt.total_seconds() / 3_600
|
324
343
|
df["hours_moving"] = df["moving_time"].dt.total_seconds() / 3_600
|
325
344
|
df["iso_year_week"] = [
|
@@ -13,7 +13,7 @@ from .datamodel import Activity
|
|
13
13
|
from .datamodel import DB
|
14
14
|
from .missing_values import some
|
15
15
|
from .tiles import compute_tile_float
|
16
|
-
from .time_conversion import
|
16
|
+
from .time_conversion import get_timezone
|
17
17
|
|
18
18
|
logger = logging.getLogger(__name__)
|
19
19
|
|
@@ -26,9 +26,7 @@ def enrichment_set_timezone(
|
|
26
26
|
), f"You cannot import an activity without points. {activity=}"
|
27
27
|
latitude, longitude = time_series[["latitude", "longitude"]].iloc[0].to_list()
|
28
28
|
if activity.iana_timezone is None or activity.start_country is None:
|
29
|
-
|
30
|
-
activity.iana_timezone = tz_str
|
31
|
-
activity.start_country = country
|
29
|
+
activity.iana_timezone = get_timezone(latitude, longitude)
|
32
30
|
return True
|
33
31
|
else:
|
34
32
|
return False
|
@@ -23,10 +23,14 @@ def get_metadata_from_image(path: pathlib.Path) -> dict:
|
|
23
23
|
except KeyError:
|
24
24
|
pass
|
25
25
|
try:
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
date_time_original = str(tags["EXIF DateTimeOriginal"]) + str(
|
27
|
+
tags.get("EXIF OffsetTime", "+00:00")
|
28
|
+
).replace(":", "")
|
29
|
+
print(f"Extracted date: {date_time_original}")
|
30
|
+
metadata["time"] = datetime.datetime.strptime(
|
31
|
+
date_time_original, "%Y:%m:%d %H:%M:%S%z"
|
29
32
|
).astimezone(zoneinfo.ZoneInfo("UTC"))
|
33
|
+
|
30
34
|
except KeyError:
|
31
35
|
pass
|
32
36
|
|
@@ -24,7 +24,7 @@ def test_empty_query() -> None:
|
|
24
24
|
|
25
25
|
search_query = SearchQuery()
|
26
26
|
|
27
|
-
actual = apply_search_query(
|
27
|
+
actual = apply_search_query(search_query)
|
28
28
|
assert (actual["id"] == activity_meta["id"]).all()
|
29
29
|
|
30
30
|
|
@@ -43,7 +43,7 @@ def test_equipment_query() -> None:
|
|
43
43
|
}
|
44
44
|
)
|
45
45
|
search_query = SearchQuery(equipment=["B"])
|
46
|
-
actual = apply_search_query(
|
46
|
+
actual = apply_search_query(search_query)
|
47
47
|
assert set(actual["id"]) == {2, 3}
|
48
48
|
|
49
49
|
|
@@ -62,7 +62,7 @@ def test_date_query() -> None:
|
|
62
62
|
}
|
63
63
|
)
|
64
64
|
search_query = SearchQuery(start_begin=datetime.date(2024, 12, 31))
|
65
|
-
actual = apply_search_query(
|
65
|
+
actual = apply_search_query(search_query)
|
66
66
|
assert set(actual["id"]) == {2}
|
67
67
|
|
68
68
|
|
@@ -81,7 +81,7 @@ def test_name_query() -> None:
|
|
81
81
|
}
|
82
82
|
)
|
83
83
|
search_query = SearchQuery(name="Test1")
|
84
|
-
actual = apply_search_query(
|
84
|
+
actual = apply_search_query(search_query)
|
85
85
|
assert set(actual["id"]) == {1}
|
86
86
|
|
87
87
|
|
@@ -1,7 +1,13 @@
|
|
1
1
|
from .time_conversion import get_country_timezone
|
2
|
+
from .time_conversion import get_timezone
|
2
3
|
|
3
4
|
|
4
5
|
def test_time_zone_from_location() -> None:
|
5
6
|
country, iana_timezone = get_country_timezone(50, 7)
|
6
7
|
assert country == "Germany"
|
7
8
|
assert iana_timezone == "Europe/Berlin"
|
9
|
+
|
10
|
+
|
11
|
+
def test_timezone_finder() -> None:
|
12
|
+
iana_timezone = get_timezone(50, 7)
|
13
|
+
assert iana_timezone == "Europe/Berlin"
|
@@ -2,8 +2,10 @@ import datetime
|
|
2
2
|
import json
|
3
3
|
import logging
|
4
4
|
import zoneinfo
|
5
|
+
from typing import Optional
|
5
6
|
|
6
7
|
import requests
|
8
|
+
import timezonefinder
|
7
9
|
|
8
10
|
from .paths import USER_CACHE_DIR
|
9
11
|
|
@@ -40,3 +42,8 @@ def get_country_timezone(latitude: float, longitude: float) -> tuple[str, str]:
|
|
40
42
|
with open(cache_file, "w") as f:
|
41
43
|
json.dump(data, f)
|
42
44
|
return data["location"], data["iana_timezone"]
|
45
|
+
|
46
|
+
|
47
|
+
def get_timezone(latitude: float, longitude: float) -> Optional[str]:
|
48
|
+
tf = timezonefinder.TimezoneFinder() # reuse
|
49
|
+
return tf.timezone_at(lng=longitude, lat=latitude)
|
@@ -4,6 +4,7 @@ import itertools
|
|
4
4
|
import logging
|
5
5
|
import pathlib
|
6
6
|
import pickle
|
7
|
+
import zoneinfo
|
7
8
|
from typing import Iterator
|
8
9
|
from typing import Optional
|
9
10
|
from typing import TypedDict
|
@@ -184,10 +185,13 @@ def compute_tile_visits_new(
|
|
184
185
|
|
185
186
|
for zoom in reversed(range(20)):
|
186
187
|
tile_state = tile_visit_accessor.tile_state
|
187
|
-
if
|
188
|
-
tile_state["tile_history"][zoom]
|
189
|
-
|
190
|
-
|
188
|
+
if (
|
189
|
+
len(tile_state["tile_history"][zoom])
|
190
|
+
and not (
|
191
|
+
tile_state["tile_history"][zoom]["time"].dropna().diff().dropna()
|
192
|
+
>= datetime.timedelta(seconds=0)
|
193
|
+
).all()
|
194
|
+
):
|
191
195
|
logger.warning(
|
192
196
|
f"The order of the tile history at {zoom=} is not chronological, resetting."
|
193
197
|
)
|
@@ -250,12 +254,17 @@ def _process_activity(
|
|
250
254
|
|
251
255
|
first_time = tile_visit.get("first_time", None)
|
252
256
|
last_time = tile_visit.get("last_time", None)
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
257
|
+
try:
|
258
|
+
if first_time is None or time < first_time:
|
259
|
+
tile_visit["first_id"] = activity_id
|
260
|
+
tile_visit["first_time"] = time
|
261
|
+
if last_time is None or time > last_time:
|
262
|
+
tile_visit["last_id"] = activity_id
|
263
|
+
tile_visit["last_time"] = time
|
264
|
+
except TypeError as e:
|
265
|
+
raise TypeError(
|
266
|
+
f"Mismatch in timezone awareness: {time=}, {first_time=}, {last_time=}"
|
267
|
+
) from e
|
259
268
|
|
260
269
|
activities_per_tile[tile].add(activity_id)
|
261
270
|
|
@@ -272,6 +281,10 @@ def _process_activity(
|
|
272
281
|
def _tiles_from_points(
|
273
282
|
time_series: pd.DataFrame, zoom: int
|
274
283
|
) -> Iterator[tuple[datetime.datetime, int, int]]:
|
284
|
+
# XXX Some people haven't localized their time series yet. This breaks the tile history part. Just assume that it is UTC, should be good enough for tiles.
|
285
|
+
if time_series["time"].dt.tz is None:
|
286
|
+
time_series = time_series.copy()
|
287
|
+
time_series["time"].dt.tz_localize(zoneinfo.ZoneInfo("UTC"))
|
275
288
|
xf = time_series["x"] * 2**zoom
|
276
289
|
yf = time_series["y"] * 2**zoom
|
277
290
|
for t1, x1, y1, x2, y2, s1, s2 in zip(
|
@@ -4,6 +4,7 @@ import logging
|
|
4
4
|
import pathlib
|
5
5
|
import xml
|
6
6
|
from collections.abc import Iterator
|
7
|
+
from typing import Union
|
7
8
|
|
8
9
|
import charset_normalizer
|
9
10
|
import dateutil.parser
|
@@ -127,7 +128,9 @@ def read_fit_activity(path: pathlib.Path, open) -> tuple[Activity, pd.DataFrame]
|
|
127
128
|
if "altitude" in fields:
|
128
129
|
row["elevation"] = values["altitude"]
|
129
130
|
if "enhanced_altitude" in fields:
|
130
|
-
row["elevation"] =
|
131
|
+
row["elevation"] = _first_of_tuple(
|
132
|
+
values["enhanced_altitude"]
|
133
|
+
)
|
131
134
|
if "speed" in fields:
|
132
135
|
factor = _fit_speed_unit_factor(fields["speed"].units)
|
133
136
|
row["speed"] = values["speed"] * factor
|
@@ -135,13 +138,9 @@ def read_fit_activity(path: pathlib.Path, open) -> tuple[Activity, pd.DataFrame]
|
|
135
138
|
factor = _fit_speed_unit_factor(
|
136
139
|
fields["enhanced_speed"].units
|
137
140
|
)
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
# https://github.com/martin-ueding/geo-activity-playground/issues/301
|
142
|
-
raise ActivityParseError(
|
143
|
-
f'Cannot work with {values["enhanced_speed"]!r}, {factor!r}'
|
144
|
-
) from e
|
141
|
+
row["speed"] = (
|
142
|
+
_first_of_tuple(values["enhanced_speed"]) * factor
|
143
|
+
)
|
145
144
|
if "grade" in fields:
|
146
145
|
row["grade"] = values["grade"]
|
147
146
|
if "temperature" in fields:
|
@@ -159,7 +158,7 @@ def read_fit_activity(path: pathlib.Path, open) -> tuple[Activity, pd.DataFrame]
|
|
159
158
|
kind_name += " " + str(values["sub_sport"])
|
160
159
|
activity.kind = get_or_make_kind(kind_name)
|
161
160
|
if "total_calories" in fields:
|
162
|
-
activity.calories = values["total_calories"]
|
161
|
+
activity.calories = int(str(values["total_calories"]))
|
163
162
|
if "total_strides" in fields:
|
164
163
|
activity.steps = 2 * int(values["total_strides"])
|
165
164
|
|
@@ -175,6 +174,13 @@ def _fit_speed_unit_factor(unit: str) -> float:
|
|
175
174
|
raise ActivityParseError(f"Unknown speed unit {unit}")
|
176
175
|
|
177
176
|
|
177
|
+
def _first_of_tuple(value: Union[float, tuple[float, float]]) -> float:
|
178
|
+
try:
|
179
|
+
return float(value)
|
180
|
+
except TypeError:
|
181
|
+
return float(value[0])
|
182
|
+
|
183
|
+
|
178
184
|
def read_gpx_activity(path: pathlib.Path, open) -> pd.DataFrame:
|
179
185
|
points = []
|
180
186
|
with open(path, "rb") as f:
|
@@ -4,6 +4,7 @@ import re
|
|
4
4
|
import traceback
|
5
5
|
|
6
6
|
import sqlalchemy
|
7
|
+
from tqdm import tqdm
|
7
8
|
|
8
9
|
from ..core.activities import ActivityRepository
|
9
10
|
from ..core.config import Config
|
@@ -37,8 +38,22 @@ def import_from_directory(
|
|
37
38
|
and not path.stem.startswith(".")
|
38
39
|
and not path.suffix in config.ignore_suffixes
|
39
40
|
]
|
41
|
+
activity_paths.sort()
|
40
42
|
|
41
|
-
|
43
|
+
paths_to_import = [
|
44
|
+
activity_path
|
45
|
+
for activity_path in tqdm(
|
46
|
+
activity_paths, desc="Scanning for new files", delay=1
|
47
|
+
)
|
48
|
+
if DB.session.scalar(
|
49
|
+
sqlalchemy.select(Activity).filter(Activity.path == str(activity_path))
|
50
|
+
)
|
51
|
+
is None
|
52
|
+
]
|
53
|
+
|
54
|
+
for i, activity_path in enumerate(
|
55
|
+
tqdm(paths_to_import, desc="Importing activity files", delay=0)
|
56
|
+
):
|
42
57
|
with DB.session.no_autoflush:
|
43
58
|
activity = DB.session.scalar(
|
44
59
|
sqlalchemy.select(Activity).filter(Activity.path == str(activity_path))
|
@@ -4,6 +4,7 @@ import pathlib
|
|
4
4
|
import pickle
|
5
5
|
import time
|
6
6
|
import zoneinfo
|
7
|
+
from typing import Optional
|
7
8
|
|
8
9
|
import pandas as pd
|
9
10
|
from stravalib import Client
|
@@ -80,8 +81,12 @@ def import_from_strava_api(
|
|
80
81
|
config: Config,
|
81
82
|
repository: ActivityRepository,
|
82
83
|
tile_visit_accessor: TileVisitAccessor,
|
84
|
+
strava_begin: Optional[str] = None,
|
85
|
+
strava_end: Optional[str] = None,
|
83
86
|
) -> None:
|
84
|
-
while try_import_strava(
|
87
|
+
while try_import_strava(
|
88
|
+
config, repository, tile_visit_accessor, strava_begin, strava_end
|
89
|
+
):
|
85
90
|
now = datetime.datetime.now()
|
86
91
|
next_quarter = round_to_next_quarter_hour(now)
|
87
92
|
seconds_to_wait = (next_quarter - now).total_seconds() + 10
|
@@ -95,8 +100,13 @@ def try_import_strava(
|
|
95
100
|
config: Config,
|
96
101
|
repository: ActivityRepository,
|
97
102
|
tile_visit_accessor: TileVisitAccessor,
|
103
|
+
strava_begin: Optional[str] = None,
|
104
|
+
strava_end: Optional[str] = None,
|
98
105
|
) -> bool:
|
99
|
-
|
106
|
+
if strava_begin:
|
107
|
+
get_after = f"{strava_begin}T00:00:00Z"
|
108
|
+
else:
|
109
|
+
get_after = get_state(strava_last_activity_date_path(), "2000-01-01T00:00:00Z")
|
100
110
|
|
101
111
|
gear_names = {None: "None"}
|
102
112
|
|
@@ -106,6 +116,13 @@ def try_import_strava(
|
|
106
116
|
for strava_activity in tqdm(
|
107
117
|
client.get_activities(after=get_after), desc="Downloading Strava activities"
|
108
118
|
):
|
119
|
+
if (
|
120
|
+
strava_end
|
121
|
+
and strava_activity.start_date is not None
|
122
|
+
and str(strava_activity.start_date) > strava_end
|
123
|
+
):
|
124
|
+
break
|
125
|
+
|
109
126
|
cache_file = (
|
110
127
|
pathlib.Path("Cache")
|
111
128
|
/ "Strava Activity Metadata"
|
@@ -171,10 +188,11 @@ def try_import_strava(
|
|
171
188
|
compute_tile_evolution(tile_visit_accessor.tile_state, config)
|
172
189
|
tile_visit_accessor.save()
|
173
190
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
191
|
+
if strava_begin is None and strava_end is None:
|
192
|
+
set_state(
|
193
|
+
strava_last_activity_date_path(),
|
194
|
+
strava_activity.start_date.isoformat().replace("+00:00", "Z"),
|
195
|
+
)
|
178
196
|
|
179
197
|
limit_exceeded = False
|
180
198
|
except RateLimitExceeded:
|
@@ -198,6 +198,8 @@ def import_from_strava_checkout(config: Config) -> None:
|
|
198
198
|
|
199
199
|
activity_file = checkout_path / row["Filename"]
|
200
200
|
|
201
|
+
logger.info(f"Importing '{activity_file}' …")
|
202
|
+
|
201
203
|
try:
|
202
204
|
activity, time_series = read_activity(activity_file)
|
203
205
|
except ActivityParseError as e:
|
@@ -11,15 +11,16 @@ import threading
|
|
11
11
|
import urllib.parse
|
12
12
|
import uuid
|
13
13
|
import warnings
|
14
|
+
from typing import Optional
|
14
15
|
|
15
16
|
import pandas as pd
|
16
17
|
import sqlalchemy
|
17
|
-
from flask import Config
|
18
18
|
from flask import Flask
|
19
19
|
from flask import request
|
20
20
|
from flask_alembic import Alembic
|
21
21
|
|
22
22
|
from ..core.activities import ActivityRepository
|
23
|
+
from ..core.config import Config
|
23
24
|
from ..core.config import ConfigAccessor
|
24
25
|
from ..core.config import import_old_config
|
25
26
|
from ..core.config import import_old_strava_config
|
@@ -84,9 +85,13 @@ def importer_thread(
|
|
84
85
|
repository: ActivityRepository,
|
85
86
|
tile_visit_accessor: TileVisitAccessor,
|
86
87
|
config: Config,
|
88
|
+
strava_begin: Optional[str],
|
89
|
+
strava_end: Optional[str],
|
87
90
|
) -> None:
|
88
91
|
with app.app_context():
|
89
|
-
scan_for_activities(
|
92
|
+
scan_for_activities(
|
93
|
+
repository, tile_visit_accessor, config, strava_begin, strava_end
|
94
|
+
)
|
90
95
|
logger.info("Importer thread is done.")
|
91
96
|
|
92
97
|
|
@@ -95,6 +100,8 @@ def web_ui_main(
|
|
95
100
|
skip_reload: bool,
|
96
101
|
host: str,
|
97
102
|
port: int,
|
103
|
+
strava_begin: Optional[str],
|
104
|
+
strava_end: Optional[str],
|
98
105
|
) -> None:
|
99
106
|
os.chdir(basedir)
|
100
107
|
|
@@ -109,6 +116,7 @@ def web_ui_main(
|
|
109
116
|
database_path = pathlib.Path("database.sqlite")
|
110
117
|
logger.info(f"Using database file at '{database_path.absolute()}'.")
|
111
118
|
app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{database_path.absolute()}"
|
119
|
+
# app.config["SQLALCHEMY_ECHO"] = True
|
112
120
|
app.config["ALEMBIC"] = {"script_location": "../alembic/versions"}
|
113
121
|
DB.init_app(app)
|
114
122
|
|
@@ -144,7 +152,14 @@ def web_ui_main(
|
|
144
152
|
if not skip_reload:
|
145
153
|
thread = threading.Thread(
|
146
154
|
target=importer_thread,
|
147
|
-
args=(
|
155
|
+
args=(
|
156
|
+
app,
|
157
|
+
repository,
|
158
|
+
tile_visit_accessor,
|
159
|
+
config_accessor(),
|
160
|
+
strava_begin,
|
161
|
+
strava_end,
|
162
|
+
),
|
148
163
|
)
|
149
164
|
thread.start()
|
150
165
|
|
@@ -64,11 +64,7 @@ def _render_eddington_template(
|
|
64
64
|
|
65
65
|
query = search_query_from_form(request.args)
|
66
66
|
search_query_history.register_query(query)
|
67
|
-
activities = (
|
68
|
-
apply_search_query(repository.meta, query)
|
69
|
-
.dropna(subset=["start", column_name])
|
70
|
-
.copy()
|
71
|
-
)
|
67
|
+
activities = apply_search_query(query).dropna(subset=["start", column_name]).copy()
|
72
68
|
|
73
69
|
assert (
|
74
70
|
len(activities) > 0
|
@@ -26,7 +26,7 @@ def make_hall_of_fame_blueprint(
|
|
26
26
|
def index() -> str:
|
27
27
|
query = search_query_from_form(request.args)
|
28
28
|
search_query_history.register_query(query)
|
29
|
-
activities = apply_search_query(
|
29
|
+
activities = apply_search_query(query)
|
30
30
|
df = activities
|
31
31
|
|
32
32
|
nominations = nominate_activities(df)
|
@@ -183,7 +183,7 @@ def _get_counts(
|
|
183
183
|
tile_count_cache_path.unlink(missing_ok=True)
|
184
184
|
tmp_path.rename(tile_count_cache_path)
|
185
185
|
else:
|
186
|
-
activities = apply_search_query(
|
186
|
+
activities = apply_search_query(query)
|
187
187
|
activity_ids = activities_per_tile[z].get((x, y), set())
|
188
188
|
for activity_id in activity_ids:
|
189
189
|
if activity_id not in activities["id"]:
|
@@ -42,6 +42,7 @@ def make_photo_blueprint(
|
|
42
42
|
if not small_path.exists():
|
43
43
|
with Image.open(original_path) as im:
|
44
44
|
target_size = (size, size)
|
45
|
+
im = ImageOps.exif_transpose(im)
|
45
46
|
im = ImageOps.contain(im, target_size)
|
46
47
|
small_path.parent.mkdir(exist_ok=True)
|
47
48
|
im.save(small_path)
|
@@ -35,7 +35,7 @@ def make_search_blueprint(
|
|
35
35
|
def index():
|
36
36
|
query = search_query_from_form(request.args)
|
37
37
|
search_query_history.register_query(query)
|
38
|
-
activities = apply_search_query(
|
38
|
+
activities = apply_search_query(query)
|
39
39
|
|
40
40
|
return render_template(
|
41
41
|
"search/index.html.j2",
|
@@ -178,7 +178,7 @@ def make_summary_blueprint(
|
|
178
178
|
def index():
|
179
179
|
query = search_query_from_form(request.args)
|
180
180
|
search_query_history.register_query(query)
|
181
|
-
df = apply_search_query(
|
181
|
+
df = apply_search_query(query)
|
182
182
|
|
183
183
|
kind_scale = make_kind_scale(repository.meta, config)
|
184
184
|
df_without_nan = df.loc[~pd.isna(df["start"])]
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import os
|
2
2
|
import pathlib
|
3
|
+
from typing import Optional
|
3
4
|
|
4
5
|
import sqlalchemy
|
5
6
|
from flask import Blueprint
|
@@ -115,6 +116,8 @@ def scan_for_activities(
|
|
115
116
|
repository: ActivityRepository,
|
116
117
|
tile_visit_accessor: TileVisitAccessor,
|
117
118
|
config: Config,
|
119
|
+
strava_begin: Optional[str] = None,
|
120
|
+
strava_end: Optional[str] = None,
|
118
121
|
skip_strava: bool = False,
|
119
122
|
) -> None:
|
120
123
|
if pathlib.Path("Activities").exists():
|
@@ -122,7 +125,9 @@ def scan_for_activities(
|
|
122
125
|
if pathlib.Path("Strava Export").exists():
|
123
126
|
import_from_strava_checkout(config)
|
124
127
|
if config.strava_client_code and not skip_strava:
|
125
|
-
import_from_strava_api(
|
128
|
+
import_from_strava_api(
|
129
|
+
config, repository, tile_visit_accessor, strava_begin, strava_end
|
130
|
+
)
|
126
131
|
|
127
132
|
if len(repository) > 0:
|
128
133
|
compute_tile_visits_new(repository, tile_visit_accessor)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
var map = L.map('activity-map', {
|
2
|
+
fullscreenControl: true
|
3
|
+
});
|
4
|
+
L.tileLayer('/tile/pastel/{z}/{x}/{y}.png', {
|
5
|
+
maxZoom: 19,
|
6
|
+
attribution: map_tile_attribution
|
7
|
+
}).addTo(map);
|
8
|
+
|
9
|
+
let copy_to_input = function (value, input) {
|
10
|
+
document.getElementById(input).value = value;
|
11
|
+
}
|
12
|
+
|
13
|
+
let layer = L.geoJSON(color_line_geojson, {
|
14
|
+
pointToLayer: function (feature, lat_lon) {
|
15
|
+
let p = feature.properties
|
16
|
+
|
17
|
+
let marker = null
|
18
|
+
if (p.markerType == "circle") {
|
19
|
+
marker = L.circleMarker(lat_lon, p.markerStyle)
|
20
|
+
} else {
|
21
|
+
marker = L.marker(lat_lon)
|
22
|
+
}
|
23
|
+
|
24
|
+
let text = ''
|
25
|
+
if (p.name) {
|
26
|
+
text += `<button class="btn btn-primary" onclick="copy_to_input(${p.name}, 'begin')">Use as Begin</button>`
|
27
|
+
text += ' '
|
28
|
+
text += `<button class="btn btn-primary" onclick="copy_to_input(${p.name}+1, 'end')">Use as End</button>`
|
29
|
+
}
|
30
|
+
if (text) {
|
31
|
+
marker = marker.bindPopup(text)
|
32
|
+
}
|
33
|
+
|
34
|
+
return marker
|
35
|
+
}
|
36
|
+
})
|
37
|
+
|
38
|
+
layer.addTo(map)
|
39
|
+
map.fitBounds(layer.getBounds());
|
@@ -9,47 +9,12 @@
|
|
9
9
|
|
10
10
|
<a href="{{ url_for('.show', id=activity['id']) }}" class="btn btn-primary btn-small">Back</a>
|
11
11
|
|
12
|
-
|
13
12
|
<div id="activity-map" style="height: 600px;" class="mb-3"></div>
|
14
13
|
<script>
|
15
|
-
var
|
16
|
-
|
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());
|
14
|
+
var map_tile_attribution = '{{ map_tile_attribution }}';
|
15
|
+
var color_line_geojson = {{ color_line_geojson | safe }};
|
52
16
|
</script>
|
17
|
+
<script src="/static/activity-trim.js"></script>
|
53
18
|
|
54
19
|
<form method="POST">
|
55
20
|
<div class="mb-3">
|
{geo_activity_playground-1.5.2.dist-info → geo_activity_playground-1.7.0.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: geo-activity-playground
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.7.0
|
4
4
|
Summary: Analysis of geo data activities like rides, runs or hikes.
|
5
5
|
License: MIT
|
6
6
|
Author: Martin Ueding
|
@@ -37,6 +37,7 @@ Requires-Dist: shapely (>=2.0.5,<3.0.0)
|
|
37
37
|
Requires-Dist: sqlalchemy (>=2.0.40,<3.0.0)
|
38
38
|
Requires-Dist: stravalib (>=2.0,<3.0)
|
39
39
|
Requires-Dist: tcxreader (>=0.4.5,<0.5.0)
|
40
|
+
Requires-Dist: timezonefinder (>=6.5.9,<7.0.0)
|
40
41
|
Requires-Dist: tomli (>=2.0.1,<3.0.0) ; python_version < "3.11"
|
41
42
|
Requires-Dist: tqdm (>=4.64.0,<5.0.0)
|
42
43
|
Requires-Dist: vegafusion-python-embed (>=1.4.3,<2.0.0)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
geo_activity_playground/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
geo_activity_playground/__main__.py,sha256=
|
2
|
+
geo_activity_playground/__main__.py,sha256=KZr5_l6K3mqTqGKU9mUsI1y6UZokxgcq7fLPwEUCMJY,3531
|
3
3
|
geo_activity_playground/alembic/README,sha256=MVlc9TYmr57RbhXET6QxgyCcwWP7w-vLkEsirENqiIQ,38
|
4
4
|
geo_activity_playground/alembic/env.py,sha256=46oMzwSROaAsYuYWTd46txFdRLD3adm_SCn01A_ex8Q,2081
|
5
5
|
geo_activity_playground/alembic/script.py.mako,sha256=g1k4U3D8y4PPYRzW3DH7GEu6yN4EiAr62GgCu6cRpBo,524
|
@@ -21,73 +21,74 @@ geo_activity_playground/core/activities.py,sha256=apP_-Rg1ub3lh7RARMGXf2BOmJTiah
|
|
21
21
|
geo_activity_playground/core/config.py,sha256=mmdMQ5iCLNGnAlriT1ETEVS-gM6Aq_9sg22QECHj4n8,5358
|
22
22
|
geo_activity_playground/core/coordinates.py,sha256=rW_OmMRpTUyIsQwrT6mgT9Y6uPGuwqTo5XDDMS7mGuo,1140
|
23
23
|
geo_activity_playground/core/copernicus_dem.py,sha256=t6Bc9fsyGyx1awdePXvlN-Zc-tiT2eGSJ80SV5B1Z9A,2944
|
24
|
-
geo_activity_playground/core/datamodel.py,sha256=
|
25
|
-
geo_activity_playground/core/enrichment.py,sha256=
|
24
|
+
geo_activity_playground/core/datamodel.py,sha256=DVKgvS8nIuvGBUiXGJbNci3xdY1TxXrFGhsYin86E5Q,16730
|
25
|
+
geo_activity_playground/core/enrichment.py,sha256=b27E_KK30xjq8MuGFZyIpKzz8fO2LwLVaGGTP0mb5N0,8618
|
26
26
|
geo_activity_playground/core/export.py,sha256=ayOmhWL72263oP9NLIZRYCg_Db0GLUFhgNIL_MCrV-E,4435
|
27
27
|
geo_activity_playground/core/heart_rate.py,sha256=-S3WAhS7AOywrw_Lk5jfuo_fu6zvZQ1VtjwEKSycWpU,1542
|
28
|
-
geo_activity_playground/core/meta_search.py,sha256=
|
28
|
+
geo_activity_playground/core/meta_search.py,sha256=Sevl4AtBwJZMW3VJ6ihRqwjC0M9fhJqX3MN6tw_hIFQ,6627
|
29
29
|
geo_activity_playground/core/missing_values.py,sha256=HjonaLV0PFMICnuMrbdUNnK9uy_8PBh_RxI5GuEMQK0,250
|
30
30
|
geo_activity_playground/core/parametric_plot.py,sha256=8CKB8dey7EmZtQnl6IOgBhpxkw0UCpQPWeiBw5PqW8k,5737
|
31
31
|
geo_activity_playground/core/paths.py,sha256=qQ4ujaIHmsxTGEWzf-76XS8FclEI2RC5COTUeuLEbDI,2938
|
32
|
-
geo_activity_playground/core/photos.py,sha256=
|
32
|
+
geo_activity_playground/core/photos.py,sha256=pHdVUpgKfbyrTddZL_mgmsXQJ3k_IHKf70jtcIjjcCA,1229
|
33
33
|
geo_activity_playground/core/privacy_zones.py,sha256=4TumHsVUN1uW6RG3ArqTXDykPVipF98DCxVBe7YNdO8,512
|
34
34
|
geo_activity_playground/core/raster_map.py,sha256=y7maiC_bmUwXsULC_XCZ1m8nGgU2jFe47QFB7TpC_V4,8257
|
35
35
|
geo_activity_playground/core/similarity.py,sha256=L2de3DPRdDeDY5AxZwLDcH7FjHWRWklr41VNU06q9kQ,3117
|
36
36
|
geo_activity_playground/core/summary_stats.py,sha256=v5FtWnE1imDF5axI6asVN55wCrlD73oZ6lvqzxsTN2c,1006
|
37
37
|
geo_activity_playground/core/tasks.py,sha256=-_9cxekoHSWzCW4XblNeqrwi2tTqr5AE7_-p8fdqhwc,2886
|
38
38
|
geo_activity_playground/core/test_datamodel.py,sha256=-VrGHgx5Z3MSQPqHGmmm7atRJYbg5y_ukvRHKxk22PI,569
|
39
|
-
geo_activity_playground/core/test_meta_search.py,sha256=
|
39
|
+
geo_activity_playground/core/test_meta_search.py,sha256=R0fvFvXR3N23HFcACidixe3Dl1CwBZMNRnOjG3cT39s,3031
|
40
40
|
geo_activity_playground/core/test_missing_values.py,sha256=7yvg6dUu7p8PR_rxUxgFaJCrGzfYUcF8Zf6y7bCEYKw,356
|
41
41
|
geo_activity_playground/core/test_pandas_timezone.py,sha256=-MDiLoLG5tDMqkI0iKno24kFn9X3tslh9ZpoDYLWNJU,771
|
42
42
|
geo_activity_playground/core/test_summary_stats.py,sha256=qH_45mPRFD2H-Rr0Ku-RYc67vhC7qKxbPr7J2F36uV8,3081
|
43
43
|
geo_activity_playground/core/test_tiles.py,sha256=zce1FxNfsSpOQt66jMehdQRVoNdl-oiFydx6iVBHZXM,764
|
44
|
-
geo_activity_playground/core/test_time_zone_from_location.py,sha256=
|
44
|
+
geo_activity_playground/core/test_time_zone_from_location.py,sha256=frL5QHV7-Tuwo2Igx2kIEyTPbW2t3lusCc4Sl5xWlz0,393
|
45
45
|
geo_activity_playground/core/test_time_zone_import.py,sha256=1CAJyGTHYPoxabBNVRfLhaRNbvvWcAYjJyDR-qvbRjg,3349
|
46
46
|
geo_activity_playground/core/test_timezone_sqlalchemy.py,sha256=OXjdowGXM-DsM4-Mpab8c3j3AtdW_yXp2NgI-B_cbvA,1172
|
47
47
|
geo_activity_playground/core/tiles.py,sha256=LBn2V6WAvMxZeXSIQ8ruZL71iyvOXoFZMz7PZgNUf7M,2189
|
48
|
-
geo_activity_playground/core/time_conversion.py,sha256=
|
48
|
+
geo_activity_playground/core/time_conversion.py,sha256=f5CByqyWSthCplrbXZThSFbt3eY-t1g-2H5lkfBlgc0,1556
|
49
49
|
geo_activity_playground/explorer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
50
50
|
geo_activity_playground/explorer/grid_file.py,sha256=YNL_c4O1-kxaajATJwj4ZLywCL5Hpj9qy2h-F7rk8Yg,3260
|
51
|
-
geo_activity_playground/explorer/tile_visits.py,sha256=
|
51
|
+
geo_activity_playground/explorer/tile_visits.py,sha256=QlotWptXgv_uvtBr4qSdGpC4as6PR5Idharv_utwNyU,16943
|
52
52
|
geo_activity_playground/explorer/video.py,sha256=7j6Qv3HG6On7Tn7xh7Olwrx_fbQnfzS7CeRg3TEApHg,4397
|
53
53
|
geo_activity_playground/heatmap_video.py,sha256=I8i1uVvbbPUXVtvLAROaLy58nQoUPnuMCZkERWNkQjg,3318
|
54
54
|
geo_activity_playground/importers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
55
|
-
geo_activity_playground/importers/activity_parsers.py,sha256=
|
55
|
+
geo_activity_playground/importers/activity_parsers.py,sha256=NAK_Fv_zy75WuNa1bgYgzjc7Dnsj1oVah6DW0faLCG4,11403
|
56
56
|
geo_activity_playground/importers/csv_parser.py,sha256=O1pP5GLhWhnWcy2Lsrr9g17Zspuibpt-GtZ3ZS5eZF4,2143
|
57
|
-
geo_activity_playground/importers/directory.py,sha256=
|
58
|
-
geo_activity_playground/importers/strava_api.py,sha256=
|
59
|
-
geo_activity_playground/importers/strava_checkout.py,sha256=
|
57
|
+
geo_activity_playground/importers/directory.py,sha256=sHcLIi0DOPEATt4wiE_vp00h89fO940wUQyJZUzzpnw,3978
|
58
|
+
geo_activity_playground/importers/strava_api.py,sha256=0lUmC6u829vJpaIs7plQq4mOPgc7yQI65vN9qhAEqVs,9015
|
59
|
+
geo_activity_playground/importers/strava_checkout.py,sha256=joJI_uic9fYtu7E5Odh6GUq_LyiLqQ72Ucy_Mbjr-X0,9289
|
60
60
|
geo_activity_playground/importers/test_csv_parser.py,sha256=nOTVTdlzIY0TDcbWp7xNyNaIO6Mkeu55hVziVl22QE4,1092
|
61
61
|
geo_activity_playground/importers/test_directory.py,sha256=_fn_-y98ZyElbG0BRxAmGFdtGobUShPU86SdEOpuv-A,691
|
62
62
|
geo_activity_playground/importers/test_strava_api.py,sha256=7b8bl5Rh2BctCmvTPEhCadxtUOq3mfzuadD6F5XxRio,398
|
63
63
|
geo_activity_playground/webui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
64
|
-
geo_activity_playground/webui/app.py,sha256=
|
64
|
+
geo_activity_playground/webui/app.py,sha256=YxzUhvAgmSHiUfIEmvbey1PNStgB5Wcdj-b734OehdY,11087
|
65
65
|
geo_activity_playground/webui/authenticator.py,sha256=dhREYOu_TCD_nzFNuSlHIbf5K6TmwKdXtr1wxD8fBcc,1491
|
66
66
|
geo_activity_playground/webui/blueprints/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
67
67
|
geo_activity_playground/webui/blueprints/activity_blueprint.py,sha256=tFy0GpOBhIP8xlmYc9PF4kAng-0MosXMJudVupGz2Yw,26771
|
68
68
|
geo_activity_playground/webui/blueprints/auth_blueprint.py,sha256=iCm3hZphQKR9qFgytOrfnSmr-Og1gHuQ1Djiv2o_bkE,1031
|
69
69
|
geo_activity_playground/webui/blueprints/bubble_chart_blueprint.py,sha256=8R1rUVoyofGhUgesPunys1HoLPYinvhA46BBnMvEn9Q,2880
|
70
70
|
geo_activity_playground/webui/blueprints/calendar_blueprint.py,sha256=SmOu5AfNNoWcJJNduEfPtaPRvr4EZLYAeIDLUK9P1LY,2939
|
71
|
-
geo_activity_playground/webui/blueprints/eddington_blueprints.py,sha256=
|
71
|
+
geo_activity_playground/webui/blueprints/eddington_blueprints.py,sha256=m0IhM1cr_LbAOP0us4MXUbSAuT5nx_yiHljpWTrz-js,8936
|
72
72
|
geo_activity_playground/webui/blueprints/entry_views.py,sha256=SDCzpUSb1FAb84tM0SnmrZQvtaTlO-Rqdj94hyIMDSc,2936
|
73
73
|
geo_activity_playground/webui/blueprints/equipment_blueprint.py,sha256=8L_7NZGErvu4jyigi2gg7HN_gegZRdsSFahUH7Dz6Lw,5727
|
74
74
|
geo_activity_playground/webui/blueprints/explorer_blueprint.py,sha256=6ldVAKhYLNSnkI4X_NbuKtnV_Pm9t_Bm5UZOJoiLN_s,20835
|
75
75
|
geo_activity_playground/webui/blueprints/export_blueprint.py,sha256=C9yFH5gEJs2YtWE-EhcGDEyGwwaLgC1umybgIRi6duE,1036
|
76
|
-
geo_activity_playground/webui/blueprints/hall_of_fame_blueprint.py,sha256=
|
77
|
-
geo_activity_playground/webui/blueprints/heatmap_blueprint.py,sha256=
|
78
|
-
geo_activity_playground/webui/blueprints/photo_blueprint.py,sha256=
|
76
|
+
geo_activity_playground/webui/blueprints/hall_of_fame_blueprint.py,sha256=NGVk8Xgwpt-YNX5_zgGWTMRctN_N4NCnMq10DSDteVg,3121
|
77
|
+
geo_activity_playground/webui/blueprints/heatmap_blueprint.py,sha256=prERVBOxq2xvmgyH_dwtLwMMMFILeu-6yZE9h9U_T9g,8418
|
78
|
+
geo_activity_playground/webui/blueprints/photo_blueprint.py,sha256=i4-AS9icK6pDypTdl-coTZyKldXtpukGiNbKsihfpl0,7299
|
79
79
|
geo_activity_playground/webui/blueprints/plot_builder_blueprint.py,sha256=nGtYblRTJ0rasJvl_L35cs1Iry4LONPy_9TY4ytXB-Q,3838
|
80
|
-
geo_activity_playground/webui/blueprints/search_blueprint.py,sha256=
|
80
|
+
geo_activity_playground/webui/blueprints/search_blueprint.py,sha256=hY3hfGTip24AEf7NVTPtQt2hj7MEAcG3gbJd42NY3Ic,2258
|
81
81
|
geo_activity_playground/webui/blueprints/settings_blueprint.py,sha256=cwes3QmRrC_HMP1g-Yc-x2BJycF4jF3StJl75v9acWo,20377
|
82
82
|
geo_activity_playground/webui/blueprints/square_planner_blueprint.py,sha256=xVaxJxmt8Dysl3UL9f2y__LVLtTH2Np1Ust4OSXKRAk,4746
|
83
|
-
geo_activity_playground/webui/blueprints/summary_blueprint.py,sha256=
|
83
|
+
geo_activity_playground/webui/blueprints/summary_blueprint.py,sha256=7aJYvo8EERGnItL9PFCMLM6T26xiQa3VP2RUvWglgy4,6734
|
84
84
|
geo_activity_playground/webui/blueprints/tile_blueprint.py,sha256=YzZf9OrNdjhc1_j4MtO1DMcw1uCv29ueNsYd-mWqgbg,837
|
85
85
|
geo_activity_playground/webui/blueprints/time_zone_fixer_blueprint.py,sha256=PEHsk3kRHx2EvQ-6VPD4xeOmXGjh64GMAagFkQ0wbeg,2301
|
86
|
-
geo_activity_playground/webui/blueprints/upload_blueprint.py,sha256=
|
86
|
+
geo_activity_playground/webui/blueprints/upload_blueprint.py,sha256=mwoLeanW8oN6u7msE_Lx0grxDOYev4beqPbkPZRemkM,4763
|
87
87
|
geo_activity_playground/webui/columns.py,sha256=FP0YfX-WFQk1JEXhrywv3NUEVq-x7Hv0or35X3Ltf9c,1525
|
88
88
|
geo_activity_playground/webui/flasher.py,sha256=Covc1D9cO_jjokRWnvyiXCc2tfp3aZ8XkNqFdA1AXtk,500
|
89
89
|
geo_activity_playground/webui/plot_util.py,sha256=5Uesjj-xcMskQX2z9viDZYHSxLGrH2a5dHA1ogsJW9U,261
|
90
90
|
geo_activity_playground/webui/search_util.py,sha256=gH2cOM1FTAozZUlSQ4C1dR1xlV-8e82pD1PPi_pPBNY,2647
|
91
|
+
geo_activity_playground/webui/static/activity-trim.js,sha256=TVU32IGVweK9KfcMIXbtEo8IeQ36mS--_D8gjBXqBz0,1076
|
91
92
|
geo_activity_playground/webui/static/bootstrap/bootstrap-dark-mode.js,sha256=XfyhxIFgjDc6aEj0kYgKpG5zjS5gvyhWCJmNfUG4HJY,2622
|
92
93
|
geo_activity_playground/webui/static/bootstrap/bootstrap.bundle.min.js,sha256=gvZPYrsDwbwYJLD5yeBfcNujPhRoGOY831wwbIzz3t0,80663
|
93
94
|
geo_activity_playground/webui/static/bootstrap/bootstrap.min.css,sha256=MBffSnbbXwHCuZtgPYiwMQbfE7z-GOZ7fBPCNB06Z98,232948
|
@@ -130,7 +131,7 @@ geo_activity_playground/webui/templates/activity/edit.html.j2,sha256=r979JPqaZi_
|
|
130
131
|
geo_activity_playground/webui/templates/activity/lines.html.j2,sha256=_ZDg1ruW-9UMJfOudy1-uY_-IcSSaagq7tPCih5Bb8g,1079
|
131
132
|
geo_activity_playground/webui/templates/activity/name.html.j2,sha256=RYbNGzPexa4gRUWRjw-C9nWvp5lI7agAZZCS3Du7nAs,2661
|
132
133
|
geo_activity_playground/webui/templates/activity/show.html.j2,sha256=gE4RraLi-h63KvHO4oXLDWPcXN1FB6wqMxVKB75Zn0k,10974
|
133
|
-
geo_activity_playground/webui/templates/activity/trim.html.j2,sha256=
|
134
|
+
geo_activity_playground/webui/templates/activity/trim.html.j2,sha256=4ALQ6ZruhGfK4gAyCzVFl4jEHC-3w9iWjmigsunCe7s,1022
|
134
135
|
geo_activity_playground/webui/templates/auth/index.html.j2,sha256=wzA0uxpiT1ktDadgnjvFXc9iUGMFm9GhjDkavl3S1h4,646
|
135
136
|
geo_activity_playground/webui/templates/bubble_chart/index.html.j2,sha256=yd7lWjtxxVmJZqCiXb0Y1gMEOQ7LQYJXEdpE7JB1OZY,1616
|
136
137
|
geo_activity_playground/webui/templates/calendar/index.html.j2,sha256=8dV9yeDwfv0Mm81mhiPHN5r3hdPUWl1Yye03x0Rqbo8,1601
|
@@ -174,8 +175,8 @@ geo_activity_playground/webui/templates/summary/vega-chart.html.j2,sha256=mw8Hti
|
|
174
175
|
geo_activity_playground/webui/templates/time_zone_fixer/index.html.j2,sha256=s9r6BJMXmd7kLSyjkvH4xLi6e01S5bpGRcMgMMJyCAE,1760
|
175
176
|
geo_activity_playground/webui/templates/upload/index.html.j2,sha256=I1Ix8tDS3YBdi-HdaNfjkzYXVVCjfUTe5PFTnap1ydc,775
|
176
177
|
geo_activity_playground/webui/templates/upload/reload.html.j2,sha256=YZWX5eDeNyqKJdQAywDBcU8DZBm22rRBbZqFjrFrCvQ,556
|
177
|
-
geo_activity_playground-1.
|
178
|
-
geo_activity_playground-1.
|
179
|
-
geo_activity_playground-1.
|
180
|
-
geo_activity_playground-1.
|
181
|
-
geo_activity_playground-1.
|
178
|
+
geo_activity_playground-1.7.0.dist-info/LICENSE,sha256=4RpAwKO8bPkfXH2lnpeUW0eLkNWglyG4lbrLDU_MOwY,1070
|
179
|
+
geo_activity_playground-1.7.0.dist-info/METADATA,sha256=HCy1RVSW8KTPYzUWo9XW5ozzP9HV87ueYQO3Aq6mXjY,1937
|
180
|
+
geo_activity_playground-1.7.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
181
|
+
geo_activity_playground-1.7.0.dist-info/entry_points.txt,sha256=pbNlLI6IIZIp7nPYCfAtiSiz2oxJSCl7DODD6SPkLKk,81
|
182
|
+
geo_activity_playground-1.7.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
{geo_activity_playground-1.5.2.dist-info → geo_activity_playground-1.7.0.dist-info}/entry_points.txt
RENAMED
File without changes
|