geo-activity-playground 0.33.4__py3-none-any.whl → 0.34.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/importers/strava_checkout.py +8 -2
- geo_activity_playground/webui/activity/blueprint.py +7 -0
- geo_activity_playground/webui/activity/controller.py +85 -15
- geo_activity_playground/webui/activity/templates/activity/day.html.j2 +5 -1
- geo_activity_playground/webui/equipment/controller.py +1 -1
- geo_activity_playground/webui/summary/controller.py +11 -10
- {geo_activity_playground-0.33.4.dist-info → geo_activity_playground-0.34.0.dist-info}/METADATA +1 -1
- {geo_activity_playground-0.33.4.dist-info → geo_activity_playground-0.34.0.dist-info}/RECORD +11 -11
- {geo_activity_playground-0.33.4.dist-info → geo_activity_playground-0.34.0.dist-info}/LICENSE +0 -0
- {geo_activity_playground-0.33.4.dist-info → geo_activity_playground-0.34.0.dist-info}/WHEEL +0 -0
- {geo_activity_playground-0.33.4.dist-info → geo_activity_playground-0.34.0.dist-info}/entry_points.txt +0 -0
@@ -146,7 +146,7 @@ def float_with_comma_or_period(x: str) -> Optional[float]:
|
|
146
146
|
|
147
147
|
def import_from_strava_checkout() -> None:
|
148
148
|
checkout_path = pathlib.Path("Strava Export")
|
149
|
-
with open(checkout_path / "activities.csv") as f:
|
149
|
+
with open(checkout_path / "activities.csv", encoding="utf-8") as f:
|
150
150
|
rows = parse_csv(f.read())
|
151
151
|
header = rows[0]
|
152
152
|
|
@@ -159,9 +159,15 @@ def import_from_strava_checkout() -> None:
|
|
159
159
|
|
160
160
|
if header[0] == EXPECTED_COLUMNS[0]:
|
161
161
|
dayfirst = False
|
162
|
-
|
162
|
+
elif header[0] == "Aktivitäts-ID":
|
163
163
|
header = EXPECTED_COLUMNS
|
164
164
|
dayfirst = True
|
165
|
+
else:
|
166
|
+
logger.error(
|
167
|
+
f"You are trying to import a Strava checkout where the `activities.csv` contains an unexpected header format. In order to import this, we need to map these to the English ones. Unfortunately Strava often changes the number of columns. This means that the program needs to be updated to match the new Strava export format. Please go to https://github.com/martin-ueding/geo-activity-playground/issues and open a new issue and share the following output in the ticket:"
|
168
|
+
)
|
169
|
+
print(header)
|
170
|
+
sys.exit(1)
|
165
171
|
|
166
172
|
table = {
|
167
173
|
header[i]: [rows[r][i] for r in range(1, len(rows))] for i in range(len(header))
|
@@ -55,6 +55,13 @@ def make_activity_blueprint(
|
|
55
55
|
**activity_controller.render_day(int(year), int(month), int(day)),
|
56
56
|
)
|
57
57
|
|
58
|
+
@blueprint.route("/day-sharepic/<year>/<month>/<day>/sharepic.png")
|
59
|
+
def day_sharepic(year: str, month: str, day: str):
|
60
|
+
return Response(
|
61
|
+
activity_controller.render_day_sharepic(int(year), int(month), int(day)),
|
62
|
+
mimetype="image/png",
|
63
|
+
)
|
64
|
+
|
58
65
|
@blueprint.route("/name/<name>")
|
59
66
|
def name(name: str):
|
60
67
|
return render_template(
|
@@ -170,8 +170,24 @@ class ActivityController:
|
|
170
170
|
"date": datetime.date(year, month, day).isoformat(),
|
171
171
|
"total_distance": activities_that_day["distance_km"].sum(),
|
172
172
|
"total_elapsed_time": activities_that_day["elapsed_time"].sum(),
|
173
|
+
"day": day,
|
174
|
+
"month": month,
|
175
|
+
"year": year,
|
173
176
|
}
|
174
177
|
|
178
|
+
def render_day_sharepic(self, year: int, month: int, day: int) -> bytes:
|
179
|
+
meta = self._repository.meta
|
180
|
+
selection = meta["start"].dt.date == datetime.date(year, month, day)
|
181
|
+
activities_that_day = meta.loc[selection]
|
182
|
+
|
183
|
+
time_series = [
|
184
|
+
self._repository.get_time_series(activity_id)
|
185
|
+
for activity_id in activities_that_day["id"]
|
186
|
+
]
|
187
|
+
assert len(activities_that_day) > 0
|
188
|
+
assert len(time_series) > 0
|
189
|
+
return (make_day_sharepic(activities_that_day, time_series, self._config),)
|
190
|
+
|
175
191
|
def render_all(self) -> dict:
|
176
192
|
cmap = matplotlib.colormaps["Dark2"]
|
177
193
|
fc = geojson.FeatureCollection(
|
@@ -452,14 +468,10 @@ def pixels_in_bounds(bounds: PixelBounds) -> int:
|
|
452
468
|
return (bounds.x_max - bounds.x_min) * (bounds.y_max - bounds.y_min)
|
453
469
|
|
454
470
|
|
455
|
-
def
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
config: Config,
|
460
|
-
) -> bytes:
|
461
|
-
tile_x = time_series["x"]
|
462
|
-
tile_y = time_series["y"]
|
471
|
+
def make_sharepic_base(time_series_list: list[pd.DataFrame], config: Config):
|
472
|
+
all_time_series = pd.concat(time_series_list)
|
473
|
+
tile_x = all_time_series["x"]
|
474
|
+
tile_y = all_time_series["y"]
|
463
475
|
tile_width = tile_x.max() - tile_x.min()
|
464
476
|
tile_height = tile_y.max() - tile_y.min()
|
465
477
|
|
@@ -495,16 +507,32 @@ def make_sharepic(
|
|
495
507
|
img = Image.fromarray((background * 255).astype("uint8"), "RGB")
|
496
508
|
draw = ImageDraw.Draw(img, mode="RGBA")
|
497
509
|
|
498
|
-
for
|
499
|
-
|
500
|
-
|
501
|
-
(
|
502
|
-
|
510
|
+
for time_series in time_series_list:
|
511
|
+
for _, group in time_series.groupby("segment_id"):
|
512
|
+
yx = list(
|
513
|
+
zip(
|
514
|
+
(tile_xz - tile_xz_center[0]) * OSM_TILE_SIZE + target_width / 2,
|
515
|
+
(tile_yz - tile_xz_center[1]) * OSM_TILE_SIZE
|
516
|
+
+ target_map_height / 2,
|
517
|
+
)
|
503
518
|
)
|
504
|
-
)
|
505
519
|
|
506
|
-
|
520
|
+
draw.line(yx, fill="red", width=4)
|
521
|
+
|
522
|
+
return img
|
523
|
+
|
507
524
|
|
525
|
+
def make_sharepic(
|
526
|
+
activity: ActivityMeta,
|
527
|
+
time_series: pd.DataFrame,
|
528
|
+
sharepic_suppressed_fields: list[str],
|
529
|
+
config: Config,
|
530
|
+
) -> bytes:
|
531
|
+
footer_height = 100
|
532
|
+
|
533
|
+
img = make_sharepic_base([time_series], config)
|
534
|
+
|
535
|
+
draw = ImageDraw.Draw(img, mode="RGBA")
|
508
536
|
draw.rectangle(
|
509
537
|
[0, img.height - footer_height, img.width, img.height], fill=(0, 0, 0, 180)
|
510
538
|
)
|
@@ -545,6 +573,48 @@ def make_sharepic(
|
|
545
573
|
return bytes(f.getbuffer())
|
546
574
|
|
547
575
|
|
576
|
+
def make_day_sharepic(
|
577
|
+
activities: pd.DataFrame,
|
578
|
+
time_series_list: list[pd.DataFrame],
|
579
|
+
config: Config,
|
580
|
+
) -> bytes:
|
581
|
+
footer_height = 100
|
582
|
+
|
583
|
+
img = make_sharepic_base(time_series_list, config)
|
584
|
+
|
585
|
+
draw = ImageDraw.Draw(img, mode="RGBA")
|
586
|
+
draw.rectangle(
|
587
|
+
[0, img.height - footer_height, img.width, img.height], fill=(0, 0, 0, 180)
|
588
|
+
)
|
589
|
+
|
590
|
+
date = activities.iloc[0]["start"].date()
|
591
|
+
distance_km = activities["distance_km"].sum()
|
592
|
+
elapsed_time: pd.Timedelta = activities["elapsed_time"].sum()
|
593
|
+
elapsed_time = elapsed_time.round("s")
|
594
|
+
|
595
|
+
facts = {
|
596
|
+
"date": f"{date}",
|
597
|
+
"distance_km": f"{distance_km:.1f} km",
|
598
|
+
"elapsed_time": re.sub(r"^0 days ", "", f"{elapsed_time}"),
|
599
|
+
}
|
600
|
+
|
601
|
+
draw.text(
|
602
|
+
(35, img.height - footer_height + 10),
|
603
|
+
" ".join(facts.values()),
|
604
|
+
font_size=20,
|
605
|
+
)
|
606
|
+
|
607
|
+
draw.text(
|
608
|
+
(img.width - 250, img.height - 20),
|
609
|
+
"Map: © Open Street Map Contributors",
|
610
|
+
font_size=14,
|
611
|
+
)
|
612
|
+
|
613
|
+
f = io.BytesIO()
|
614
|
+
img.save(f, format="png")
|
615
|
+
return bytes(f.getbuffer())
|
616
|
+
|
617
|
+
|
548
618
|
def _extract_heart_rate_zones(
|
549
619
|
time_series: pd.DataFrame, heart_rate_zone_computer: HeartRateZoneComputer
|
550
620
|
) -> Optional[pd.DataFrame]:
|
@@ -30,7 +30,7 @@
|
|
30
30
|
<ol>
|
31
31
|
{% for activity in activities %}
|
32
32
|
<li><span style="color: {{ activity['color'] }};">█</span> <a
|
33
|
-
href="{{ url_for('
|
33
|
+
href="{{ url_for('.show', id=activity.id) }}">{{
|
34
34
|
activity.name }}</a></li>
|
35
35
|
{% endfor %}
|
36
36
|
</ol>
|
@@ -80,4 +80,8 @@
|
|
80
80
|
</div>
|
81
81
|
</div>
|
82
82
|
|
83
|
+
<h2>Share picture</h2>
|
84
|
+
|
85
|
+
<p><img src="{{ url_for('.day_sharepic', year=year, month=month, day=day) }}" /></p>
|
86
|
+
|
83
87
|
{% endblock %}
|
@@ -105,6 +105,7 @@ def embellished_activities(meta: pd.DataFrame) -> pd.DataFrame:
|
|
105
105
|
df["month"] = [start.month for start in df["start"]]
|
106
106
|
df["day"] = [start.day for start in df["start"]]
|
107
107
|
df["week"] = [start.isocalendar().week for start in df["start"]]
|
108
|
+
df["iso_year"] = [start.isocalendar().year for start in df["start"]]
|
108
109
|
df["hours"] = [
|
109
110
|
elapsed_time.total_seconds() / 3600 for elapsed_time in df["elapsed_time"]
|
110
111
|
]
|
@@ -188,8 +189,8 @@ def plot_yearly_distance(year_kind_total: pd.DataFrame, kind_scale: alt.Scale) -
|
|
188
189
|
|
189
190
|
def plot_year_cumulative(df: pd.DataFrame) -> str:
|
190
191
|
year_cumulative = (
|
191
|
-
df[["
|
192
|
-
.groupby("
|
192
|
+
df[["iso_year", "week", "distance_km"]]
|
193
|
+
.groupby("iso_year")
|
193
194
|
.apply(
|
194
195
|
lambda group: pd.DataFrame(
|
195
196
|
{"week": group["week"], "distance_km": group["distance_km"].cumsum()}
|
@@ -205,10 +206,10 @@ def plot_year_cumulative(df: pd.DataFrame) -> str:
|
|
205
206
|
.encode(
|
206
207
|
alt.X("week", title="Week"),
|
207
208
|
alt.Y("distance_km", title="Distance / km"),
|
208
|
-
alt.Color("
|
209
|
+
alt.Color("iso_year:N", title="Year"),
|
209
210
|
[
|
210
211
|
alt.Tooltip("week", title="Week"),
|
211
|
-
alt.Tooltip("
|
212
|
+
alt.Tooltip("iso_year:N", title="Year"),
|
212
213
|
alt.Tooltip("distance_km", title="Distance / km"),
|
213
214
|
],
|
214
215
|
)
|
@@ -234,26 +235,26 @@ def tabulate_year_kind_mean(df: pd.DataFrame) -> pd.DataFrame:
|
|
234
235
|
|
235
236
|
def plot_weekly_distance(df: pd.DataFrame, kind_scale: alt.Scale) -> str:
|
236
237
|
week_kind_total_distance = (
|
237
|
-
df[["
|
238
|
-
.groupby(["
|
238
|
+
df[["iso_year", "week", "kind", "distance_km"]]
|
239
|
+
.groupby(["iso_year", "week", "kind"])
|
239
240
|
.sum()
|
240
241
|
.reset_index()
|
241
242
|
)
|
242
243
|
week_kind_total_distance["year_week"] = [
|
243
244
|
f"{year}-{week:02d}"
|
244
245
|
for year, week in zip(
|
245
|
-
week_kind_total_distance["
|
246
|
+
week_kind_total_distance["iso_year"], week_kind_total_distance["week"]
|
246
247
|
)
|
247
248
|
]
|
248
249
|
|
249
|
-
last_year = week_kind_total_distance["
|
250
|
+
last_year = week_kind_total_distance["iso_year"].iloc[-1]
|
250
251
|
last_week = week_kind_total_distance["week"].iloc[-1]
|
251
252
|
|
252
253
|
return (
|
253
254
|
alt.Chart(
|
254
255
|
week_kind_total_distance.loc[
|
255
|
-
(week_kind_total_distance["
|
256
|
-
| (week_kind_total_distance["
|
256
|
+
(week_kind_total_distance["iso_year"] == last_year)
|
257
|
+
| (week_kind_total_distance["iso_year"] == last_year - 1)
|
257
258
|
& (week_kind_total_distance["week"] >= last_week)
|
258
259
|
],
|
259
260
|
title="Weekly Distance",
|
{geo_activity_playground-0.33.4.dist-info → geo_activity_playground-0.34.0.dist-info}/RECORD
RENAMED
@@ -24,15 +24,15 @@ geo_activity_playground/importers/activity_parsers.py,sha256=XNQs0ziqAcVqIoiLAX5
|
|
24
24
|
geo_activity_playground/importers/csv_parser.py,sha256=O1pP5GLhWhnWcy2Lsrr9g17Zspuibpt-GtZ3ZS5eZF4,2143
|
25
25
|
geo_activity_playground/importers/directory.py,sha256=CA-vFOMm8G4MSM_Q09OwQKduCApL2PWaxLTVxgw_xpw,5908
|
26
26
|
geo_activity_playground/importers/strava_api.py,sha256=cJCZsLemhOlxTtZh0z_npidgae9SD5HyEUry2uvem_A,7775
|
27
|
-
geo_activity_playground/importers/strava_checkout.py,sha256=
|
27
|
+
geo_activity_playground/importers/strava_checkout.py,sha256=dAKW1wVqlA5zRw25SvpYZZrEikJtaUluInyhJ0RfsFc,10002
|
28
28
|
geo_activity_playground/importers/test_csv_parser.py,sha256=LXqva7GuSAfXYE2zZQrg-69lCtfy5MxLSq6BRwL_VyI,1191
|
29
29
|
geo_activity_playground/importers/test_directory.py,sha256=ljXokx7q0OgtHvEdHftcQYEmZJUDVv3OOF5opklxdT4,724
|
30
30
|
geo_activity_playground/importers/test_strava_api.py,sha256=4vX7wDr1a9aRh8myxNrIq6RwDBbP8ZeoXXPc10CAbW4,431
|
31
31
|
geo_activity_playground/webui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
32
32
|
geo_activity_playground/webui/activity/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
33
|
-
geo_activity_playground/webui/activity/blueprint.py,sha256=
|
34
|
-
geo_activity_playground/webui/activity/controller.py,sha256=
|
35
|
-
geo_activity_playground/webui/activity/templates/activity/day.html.j2,sha256=
|
33
|
+
geo_activity_playground/webui/activity/blueprint.py,sha256=2Fa_6a86Aa3Yr3hQdkRylQqqVusyyVzPd1x3il5ZXNk,4176
|
34
|
+
geo_activity_playground/webui/activity/controller.py,sha256=0HXQZAD9Muf62jV-GqQKJPq5qNgdbV-KEcPmHX_8iGo,21429
|
35
|
+
geo_activity_playground/webui/activity/templates/activity/day.html.j2,sha256=wkYmcnIsMlvE9wgKemYCNU6jwsk5IJvg8pcBA2OMh00,2795
|
36
36
|
geo_activity_playground/webui/activity/templates/activity/edit.html.j2,sha256=ckcTTxcQOhmvvAGNTEOtWCUG4LhvO4HfQImlIa5qKs8,1530
|
37
37
|
geo_activity_playground/webui/activity/templates/activity/lines.html.j2,sha256=_ZDg1ruW-9UMJfOudy1-uY_-IcSSaagq7tPCih5Bb8g,1079
|
38
38
|
geo_activity_playground/webui/activity/templates/activity/name.html.j2,sha256=tKviMqMouHEGv3xBQVIsJgwj_hjwAsmGVefM3UMqlYg,2437
|
@@ -53,7 +53,7 @@ geo_activity_playground/webui/eddington/templates/eddington/index.html.j2,sha256
|
|
53
53
|
geo_activity_playground/webui/entry_controller.py,sha256=McxbyouKWHJ3a2R9agPazZoG7VHiFO1RvnkBr08dMH8,2168
|
54
54
|
geo_activity_playground/webui/equipment/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
55
55
|
geo_activity_playground/webui/equipment/blueprint.py,sha256=_NIhRJuJNbXpEd_nEPo01AqnUqPgo1vawFn7E3yoeng,636
|
56
|
-
geo_activity_playground/webui/equipment/controller.py,sha256=
|
56
|
+
geo_activity_playground/webui/equipment/controller.py,sha256=lMivui3EBUnkYZf9Lgv1kHZ0c7IxRAza-ox8YOz3ONY,4079
|
57
57
|
geo_activity_playground/webui/equipment/templates/equipment/index.html.j2,sha256=fvRaDbCuiSZ8AzJTpu1dk8FTAGZ2yfsLhprtVYHFZWo,1802
|
58
58
|
geo_activity_playground/webui/explorer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
59
59
|
geo_activity_playground/webui/explorer/blueprint.py,sha256=EKnBs8llqT6Wy1uac18dF2epp3TebF9p3iGlSbj6Vl0,2337
|
@@ -100,7 +100,7 @@ geo_activity_playground/webui/static/web-app-manifest-192x192.png,sha256=eEImN6i
|
|
100
100
|
geo_activity_playground/webui/static/web-app-manifest-512x512.png,sha256=vU9oQ4HnQerFDZVzcAT9twj4_Doc6_9v9wVvoRI-f_E,48318
|
101
101
|
geo_activity_playground/webui/summary/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
102
102
|
geo_activity_playground/webui/summary/blueprint.py,sha256=tfA2aPF19yKwkQOb5lPQBySoQYYhTn49Iuh0SYvsGP8,593
|
103
|
-
geo_activity_playground/webui/summary/controller.py,sha256=
|
103
|
+
geo_activity_playground/webui/summary/controller.py,sha256=FyPdC98maX0P5sJ4j5Z7-ZSiirLh_jmu_PszKXqTV8A,9425
|
104
104
|
geo_activity_playground/webui/summary/templates/summary/index.html.j2,sha256=ctOx3Qjx6nRDpUtFf1DlJhK_gtU77Vwx_S6euLz9-W4,5183
|
105
105
|
geo_activity_playground/webui/templates/home.html.j2,sha256=IdCqI_LLcYrpUjjCO-tbXR4s05XYrPOateiJ4idF3bo,2202
|
106
106
|
geo_activity_playground/webui/templates/page.html.j2,sha256=znTbtD2NALrhmUN_Q-F1ElGlKtecoUv8vOCcUtojrdI,11134
|
@@ -110,8 +110,8 @@ geo_activity_playground/webui/tile/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQe
|
|
110
110
|
geo_activity_playground/webui/tile/blueprint.py,sha256=q0sw_F8L367Df01yjZijikEIglFBgg9lN61sbTAEOKQ,1018
|
111
111
|
geo_activity_playground/webui/tile/controller.py,sha256=XjUTbyAMeQET1D3mFtT8r5-xMcMOaELPZWtQ1Xp7Cuw,1428
|
112
112
|
geo_activity_playground/webui/upload_blueprint.py,sha256=topLI9ytDUFkqCc9AlOqDkjhABUwnPJ1tX_7XrBPbxc,4412
|
113
|
-
geo_activity_playground-0.
|
114
|
-
geo_activity_playground-0.
|
115
|
-
geo_activity_playground-0.
|
116
|
-
geo_activity_playground-0.
|
117
|
-
geo_activity_playground-0.
|
113
|
+
geo_activity_playground-0.34.0.dist-info/LICENSE,sha256=4RpAwKO8bPkfXH2lnpeUW0eLkNWglyG4lbrLDU_MOwY,1070
|
114
|
+
geo_activity_playground-0.34.0.dist-info/METADATA,sha256=36V6xU7OXmcZrN-eqQfkzaRnFKCfA4ICmVjx-dzqWP0,1573
|
115
|
+
geo_activity_playground-0.34.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
116
|
+
geo_activity_playground-0.34.0.dist-info/entry_points.txt,sha256=pbNlLI6IIZIp7nPYCfAtiSiz2oxJSCl7DODD6SPkLKk,81
|
117
|
+
geo_activity_playground-0.34.0.dist-info/RECORD,,
|
{geo_activity_playground-0.33.4.dist-info → geo_activity_playground-0.34.0.dist-info}/LICENSE
RENAMED
File without changes
|
File without changes
|
File without changes
|