geo-activity-playground 0.22.0__py3-none-any.whl → 0.24.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 +1 -1
- geo_activity_playground/core/activities.py +16 -9
- geo_activity_playground/core/activity_parsers.py +17 -32
- geo_activity_playground/core/cache_migrations.py +24 -0
- geo_activity_playground/core/heatmap.py +21 -21
- geo_activity_playground/core/privacy_zones.py +16 -0
- geo_activity_playground/core/similarity.py +1 -1
- geo_activity_playground/core/test_time_conversion.py +37 -0
- geo_activity_playground/core/time_conversion.py +14 -0
- geo_activity_playground/explorer/tile_visits.py +44 -27
- geo_activity_playground/importers/__init__.py +0 -0
- geo_activity_playground/importers/directory.py +7 -2
- geo_activity_playground/importers/strava_api.py +6 -0
- geo_activity_playground/importers/strava_checkout.py +12 -3
- geo_activity_playground/webui/__init__.py +0 -0
- geo_activity_playground/webui/activity/__init__.py +0 -0
- geo_activity_playground/webui/activity/blueprint.py +58 -0
- geo_activity_playground/webui/{activity_controller.py → activity/controller.py} +128 -18
- geo_activity_playground/webui/{templates/activity-day.html.j2 → activity/templates/activity/day.html.j2} +14 -2
- geo_activity_playground/webui/{templates/activity-name.html.j2 → activity/templates/activity/name.html.j2} +1 -1
- geo_activity_playground/webui/{templates/activity.html.j2 → activity/templates/activity/show.html.j2} +9 -4
- geo_activity_playground/webui/app.py +68 -281
- geo_activity_playground/webui/calendar/__init__.py +0 -0
- geo_activity_playground/webui/calendar/blueprint.py +26 -0
- geo_activity_playground/webui/{calendar_controller.py → calendar/controller.py} +5 -5
- geo_activity_playground/webui/{templates/calendar.html.j2 → calendar/templates/calendar/index.html.j2} +3 -2
- geo_activity_playground/webui/{templates/calendar-month.html.j2 → calendar/templates/calendar/month.html.j2} +2 -2
- geo_activity_playground/webui/eddington/__init__.py +0 -0
- geo_activity_playground/webui/eddington/blueprint.py +19 -0
- geo_activity_playground/webui/{eddington_controller.py → eddington/controller.py} +14 -6
- geo_activity_playground/webui/eddington/templates/eddington/index.html.j2 +56 -0
- geo_activity_playground/webui/entry_controller.py +4 -2
- geo_activity_playground/webui/equipment/__init__.py +0 -0
- geo_activity_playground/webui/equipment/blueprint.py +19 -0
- geo_activity_playground/webui/{equipment_controller.py → equipment/controller.py} +5 -3
- geo_activity_playground/webui/explorer/__init__.py +0 -0
- geo_activity_playground/webui/explorer/blueprint.py +54 -0
- geo_activity_playground/webui/{explorer_controller.py → explorer/controller.py} +6 -2
- geo_activity_playground/webui/{templates/explorer.html.j2 → explorer/templates/explorer/index.html.j2} +2 -2
- geo_activity_playground/webui/heatmap/__init__.py +0 -0
- geo_activity_playground/webui/heatmap/blueprint.py +41 -0
- geo_activity_playground/webui/{heatmap_controller.py → heatmap/heatmap_controller.py} +36 -13
- geo_activity_playground/webui/{templates/heatmap.html.j2 → heatmap/templates/heatmap/index.html.j2} +17 -2
- geo_activity_playground/webui/search_controller.py +1 -9
- geo_activity_playground/webui/square_planner/__init__.py +0 -0
- geo_activity_playground/webui/square_planner/blueprint.py +38 -0
- geo_activity_playground/webui/summary/__init__.py +0 -0
- geo_activity_playground/webui/summary/blueprint.py +16 -0
- geo_activity_playground/webui/summary/controller.py +268 -0
- geo_activity_playground/webui/summary/templates/summary/index.html.j2 +135 -0
- geo_activity_playground/webui/templates/{index.html.j2 → home.html.j2} +1 -1
- geo_activity_playground/webui/templates/page.html.j2 +32 -19
- geo_activity_playground/webui/templates/search.html.j2 +1 -1
- geo_activity_playground/webui/tile/__init__.py +0 -0
- geo_activity_playground/webui/tile/blueprint.py +31 -0
- geo_activity_playground/webui/upload/__init__.py +0 -0
- geo_activity_playground/webui/upload/blueprint.py +28 -0
- geo_activity_playground/webui/{upload_controller.py → upload/controller.py} +15 -6
- geo_activity_playground/webui/{templates/upload.html.j2 → upload/templates/upload/index.html.j2} +12 -11
- {geo_activity_playground-0.22.0.dist-info → geo_activity_playground-0.24.0.dist-info}/METADATA +2 -1
- geo_activity_playground-0.24.0.dist-info/RECORD +95 -0
- geo_activity_playground/webui/config_controller.py +0 -12
- geo_activity_playground/webui/locations_controller.py +0 -28
- geo_activity_playground/webui/summary_controller.py +0 -60
- geo_activity_playground/webui/templates/config.html.j2 +0 -24
- geo_activity_playground/webui/templates/eddington.html.j2 +0 -18
- geo_activity_playground/webui/templates/locations.html.j2 +0 -38
- geo_activity_playground/webui/templates/summary.html.j2 +0 -21
- geo_activity_playground-0.22.0.dist-info/RECORD +0 -74
- /geo_activity_playground/webui/{templates/activity-lines.html.j2 → activity/templates/activity/lines.html.j2} +0 -0
- /geo_activity_playground/webui/{templates/equipment.html.j2 → equipment/templates/equipment/index.html.j2} +0 -0
- /geo_activity_playground/webui/{square_planner_controller.py → square_planner/controller.py} +0 -0
- /geo_activity_playground/webui/{templates/square-planner.html.j2 → square_planner/templates/square_planner/index.html.j2} +0 -0
- /geo_activity_playground/webui/{tile_controller.py → tile/controller.py} +0 -0
- {geo_activity_playground-0.22.0.dist-info → geo_activity_playground-0.24.0.dist-info}/LICENSE +0 -0
- {geo_activity_playground-0.22.0.dist-info → geo_activity_playground-0.24.0.dist-info}/WHEEL +0 -0
- {geo_activity_playground-0.22.0.dist-info → geo_activity_playground-0.24.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,58 @@
|
|
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.privacy_zones import PrivacyZone
|
12
|
+
|
13
|
+
|
14
|
+
def make_activity_blueprint(
|
15
|
+
repository: ActivityRepository,
|
16
|
+
tile_visit_accessor: TileVisitAccessor,
|
17
|
+
privacy_zones: Collection[PrivacyZone],
|
18
|
+
) -> Blueprint:
|
19
|
+
blueprint = Blueprint("activity", __name__, template_folder="templates")
|
20
|
+
|
21
|
+
activity_controller = ActivityController(
|
22
|
+
repository, tile_visit_accessor, privacy_zones
|
23
|
+
)
|
24
|
+
|
25
|
+
@blueprint.route("/activity/all")
|
26
|
+
def all():
|
27
|
+
return render_template(
|
28
|
+
"activity/lines.html.j2", **activity_controller.render_all()
|
29
|
+
)
|
30
|
+
|
31
|
+
@blueprint.route("/<id>")
|
32
|
+
def show(id: str):
|
33
|
+
return render_template(
|
34
|
+
"activity/show.html.j2", **activity_controller.render_activity(int(id))
|
35
|
+
)
|
36
|
+
|
37
|
+
@blueprint.route("/<id>/sharepic.png")
|
38
|
+
def sharepic(id: str):
|
39
|
+
return Response(
|
40
|
+
activity_controller.render_sharepic(int(id)),
|
41
|
+
mimetype="image/png",
|
42
|
+
)
|
43
|
+
|
44
|
+
@blueprint.route("/day/<year>/<month>/<day>")
|
45
|
+
def day(year: str, month: str, day: str):
|
46
|
+
return render_template(
|
47
|
+
"activity/day.html.j2",
|
48
|
+
**activity_controller.render_day(int(year), int(month), int(day))
|
49
|
+
)
|
50
|
+
|
51
|
+
@blueprint.route("/name/<name>")
|
52
|
+
def name(name: str):
|
53
|
+
return render_template(
|
54
|
+
"activity/name.html.j2",
|
55
|
+
**activity_controller.render_name(urllib.parse.unquote(name))
|
56
|
+
)
|
57
|
+
|
58
|
+
return blueprint
|
@@ -2,7 +2,8 @@ import datetime
|
|
2
2
|
import functools
|
3
3
|
import io
|
4
4
|
import logging
|
5
|
-
import
|
5
|
+
import re
|
6
|
+
from collections.abc import Collection
|
6
7
|
|
7
8
|
import altair as alt
|
8
9
|
import geojson
|
@@ -12,7 +13,9 @@ import numpy as np
|
|
12
13
|
import pandas as pd
|
13
14
|
from PIL import Image
|
14
15
|
from PIL import ImageDraw
|
16
|
+
from PIL import ImageFont
|
15
17
|
|
18
|
+
from geo_activity_playground.core.activities import ActivityMeta
|
16
19
|
from geo_activity_playground.core.activities import ActivityRepository
|
17
20
|
from geo_activity_playground.core.activities import extract_heart_rate_zones
|
18
21
|
from geo_activity_playground.core.activities import make_geojson_color_line
|
@@ -20,18 +23,29 @@ from geo_activity_playground.core.activities import make_geojson_from_time_serie
|
|
20
23
|
from geo_activity_playground.core.activities import make_speed_color_bar
|
21
24
|
from geo_activity_playground.core.heatmap import add_margin_to_geo_bounds
|
22
25
|
from geo_activity_playground.core.heatmap import build_map_from_tiles
|
23
|
-
from geo_activity_playground.core.heatmap import
|
26
|
+
from geo_activity_playground.core.heatmap import GeoBounds
|
24
27
|
from geo_activity_playground.core.heatmap import get_bounds
|
25
28
|
from geo_activity_playground.core.heatmap import get_sensible_zoom_level
|
26
29
|
from geo_activity_playground.core.heatmap import OSM_TILE_SIZE
|
30
|
+
from geo_activity_playground.core.heatmap import PixelBounds
|
31
|
+
from geo_activity_playground.core.heatmap import TileBounds
|
32
|
+
from geo_activity_playground.core.privacy_zones import PrivacyZone
|
27
33
|
from geo_activity_playground.core.tiles import compute_tile_float
|
34
|
+
from geo_activity_playground.explorer.tile_visits import TileVisitAccessor
|
28
35
|
|
29
36
|
logger = logging.getLogger(__name__)
|
30
37
|
|
31
38
|
|
32
39
|
class ActivityController:
|
33
|
-
def __init__(
|
40
|
+
def __init__(
|
41
|
+
self,
|
42
|
+
repository: ActivityRepository,
|
43
|
+
tile_visit_accessor: TileVisitAccessor,
|
44
|
+
privacy_zones: Collection[PrivacyZone],
|
45
|
+
) -> None:
|
34
46
|
self._repository = repository
|
47
|
+
self._tile_visit_accessor = tile_visit_accessor
|
48
|
+
self._privacy_zones = privacy_zones
|
35
49
|
|
36
50
|
@functools.lru_cache()
|
37
51
|
def render_activity(self, id: int) -> dict:
|
@@ -47,6 +61,14 @@ class ActivityController:
|
|
47
61
|
similar_activities = [row for _, row in similar_activities.iterrows()]
|
48
62
|
similar_activities.reverse()
|
49
63
|
|
64
|
+
new_tiles = {
|
65
|
+
zoom: sum(
|
66
|
+
self._tile_visit_accessor.histories[zoom]["activity_id"]
|
67
|
+
== activity["id"]
|
68
|
+
)
|
69
|
+
for zoom in [14, 17]
|
70
|
+
}
|
71
|
+
|
50
72
|
result = {
|
51
73
|
"activity": activity,
|
52
74
|
"line_json": line_json,
|
@@ -56,8 +78,9 @@ class ActivityController:
|
|
56
78
|
"speed_distribution_plot": speed_distribution_plot(time_series),
|
57
79
|
"similar_activites": similar_activities,
|
58
80
|
"speed_color_bar": make_speed_color_bar(time_series),
|
59
|
-
"date": activity
|
60
|
-
"time": activity
|
81
|
+
"date": activity["start"].date(),
|
82
|
+
"time": activity["start"].time(),
|
83
|
+
"new_tiles": new_tiles,
|
61
84
|
}
|
62
85
|
if (heart_zones := extract_heart_rate_zones(time_series)) is not None:
|
63
86
|
result["heart_zones_plot"] = heartrate_zone_plot(heart_zones)
|
@@ -68,8 +91,13 @@ class ActivityController:
|
|
68
91
|
return result
|
69
92
|
|
70
93
|
def render_sharepic(self, id: int) -> bytes:
|
94
|
+
activity = self._repository.get_activity_by_id(id)
|
71
95
|
time_series = self._repository.get_time_series(id)
|
72
|
-
|
96
|
+
for privacy_zone in self._privacy_zones:
|
97
|
+
time_series = privacy_zone.filter_time_series(time_series)
|
98
|
+
if len(time_series) == 0:
|
99
|
+
time_series = self._repository.get_time_series(id)
|
100
|
+
return make_sharepic(activity, time_series)
|
73
101
|
|
74
102
|
def render_day(self, year: int, month: int, day: int) -> dict:
|
75
103
|
meta = self._repository.meta
|
@@ -110,6 +138,8 @@ class ActivityController:
|
|
110
138
|
"activities": activities_list,
|
111
139
|
"geojson": geojson.dumps(fc),
|
112
140
|
"date": datetime.date(year, month, day).isoformat(),
|
141
|
+
"total_distance": activities_that_day["distance_km"].sum(),
|
142
|
+
"total_elapsed_time": activities_that_day["elapsed_time"].sum(),
|
113
143
|
}
|
114
144
|
|
115
145
|
def render_all(self) -> dict:
|
@@ -322,17 +352,83 @@ def name_minutes_plot(meta: pd.DataFrame) -> str:
|
|
322
352
|
)
|
323
353
|
|
324
354
|
|
325
|
-
def
|
355
|
+
def make_pixel_bounds_square(bounds: PixelBounds) -> PixelBounds:
|
356
|
+
x_radius = (bounds.x_max - bounds.x_min) // 2
|
357
|
+
y_radius = (bounds.y_max - bounds.y_min) // 2
|
358
|
+
x_center = (bounds.x_max + bounds.x_min) // 2
|
359
|
+
y_center = (bounds.y_max + bounds.y_min) // 2
|
360
|
+
|
361
|
+
radius = max(x_radius, y_radius)
|
362
|
+
|
363
|
+
return PixelBounds(
|
364
|
+
x_min=x_center - radius,
|
365
|
+
y_min=y_center - radius,
|
366
|
+
x_max=x_center + radius,
|
367
|
+
y_max=y_center + radius,
|
368
|
+
)
|
369
|
+
|
370
|
+
|
371
|
+
def make_tile_bounds_square(bounds: TileBounds) -> TileBounds:
|
372
|
+
x_radius = (bounds.x_tile_max - bounds.x_tile_min) / 2
|
373
|
+
y_radius = (bounds.y_tile_max - bounds.y_tile_min) / 2
|
374
|
+
x_center = (bounds.x_tile_max + bounds.x_tile_min) / 2
|
375
|
+
y_center = (bounds.y_tile_max + bounds.y_tile_min) / 2
|
376
|
+
|
377
|
+
radius = max(x_radius, y_radius)
|
378
|
+
|
379
|
+
return TileBounds(
|
380
|
+
zoom=bounds.zoom,
|
381
|
+
x_tile_min=int(x_center - radius),
|
382
|
+
y_tile_min=int(y_center - radius),
|
383
|
+
x_tile_max=int(np.ceil(x_center + radius)),
|
384
|
+
y_tile_max=int(np.ceil(y_center + radius)),
|
385
|
+
)
|
386
|
+
|
387
|
+
|
388
|
+
def get_crop_mask(geo_bounds: GeoBounds, tile_bounds: TileBounds) -> PixelBounds:
|
389
|
+
min_x, min_y = compute_tile_float(
|
390
|
+
geo_bounds.lat_max, geo_bounds.lon_min, tile_bounds.zoom
|
391
|
+
)
|
392
|
+
max_x, max_y = compute_tile_float(
|
393
|
+
geo_bounds.lat_min, geo_bounds.lon_max, tile_bounds.zoom
|
394
|
+
)
|
395
|
+
|
396
|
+
crop_mask = PixelBounds(
|
397
|
+
int((min_x - tile_bounds.x_tile_min) * OSM_TILE_SIZE),
|
398
|
+
int((max_x - tile_bounds.x_tile_min) * OSM_TILE_SIZE),
|
399
|
+
int((min_y - tile_bounds.y_tile_min) * OSM_TILE_SIZE),
|
400
|
+
int((max_y - tile_bounds.y_tile_min) * OSM_TILE_SIZE),
|
401
|
+
)
|
402
|
+
crop_mask = make_pixel_bounds_square(crop_mask)
|
403
|
+
|
404
|
+
return crop_mask
|
405
|
+
|
406
|
+
|
407
|
+
def pixels_in_bounds(bounds: PixelBounds) -> int:
|
408
|
+
return (bounds.x_max - bounds.x_min) * (bounds.y_max - bounds.y_min)
|
409
|
+
|
410
|
+
|
411
|
+
def make_sharepic(activity: ActivityMeta, time_series: pd.DataFrame) -> bytes:
|
326
412
|
lat_lon_data = np.array([time_series["latitude"], time_series["longitude"]]).T
|
327
413
|
|
328
414
|
geo_bounds = get_bounds(lat_lon_data)
|
329
415
|
geo_bounds = add_margin_to_geo_bounds(geo_bounds)
|
330
416
|
tile_bounds = get_sensible_zoom_level(geo_bounds, (1500, 1500))
|
417
|
+
tile_bounds = make_tile_bounds_square(tile_bounds)
|
331
418
|
background = build_map_from_tiles(tile_bounds)
|
332
419
|
# background = convert_to_grayscale(background)
|
333
420
|
|
334
|
-
|
335
|
-
|
421
|
+
crop_mask = get_crop_mask(geo_bounds, tile_bounds)
|
422
|
+
assert pixels_in_bounds(crop_mask) <= 10_000_000, crop_mask
|
423
|
+
|
424
|
+
background = background[
|
425
|
+
crop_mask.y_min : crop_mask.y_max,
|
426
|
+
crop_mask.x_min : crop_mask.x_max,
|
427
|
+
:,
|
428
|
+
]
|
429
|
+
|
430
|
+
img = Image.fromarray((background * 255).astype("uint8"), "RGB")
|
431
|
+
draw = ImageDraw.Draw(img, mode="RGBA")
|
336
432
|
|
337
433
|
for _, group in time_series.groupby("segment_id"):
|
338
434
|
xs, ys = compute_tile_float(
|
@@ -340,24 +436,38 @@ def make_sharepic(time_series: pd.DataFrame) -> bytes:
|
|
340
436
|
)
|
341
437
|
yx = list(
|
342
438
|
(
|
343
|
-
int((x - tile_bounds.x_tile_min) * OSM_TILE_SIZE),
|
344
|
-
int((y - tile_bounds.y_tile_min) * OSM_TILE_SIZE),
|
439
|
+
int((x - tile_bounds.x_tile_min) * OSM_TILE_SIZE - crop_mask.x_min),
|
440
|
+
int((y - tile_bounds.y_tile_min) * OSM_TILE_SIZE - crop_mask.y_min),
|
345
441
|
)
|
346
442
|
for x, y in zip(xs, ys)
|
347
443
|
)
|
348
444
|
|
349
445
|
draw.line(yx, fill="red", width=4)
|
350
446
|
|
351
|
-
|
447
|
+
draw.rectangle([0, img.height - 70, img.width, img.height], fill=(0, 0, 0, 128))
|
448
|
+
facts = [
|
449
|
+
f"{activity['kind']}",
|
450
|
+
f"{activity['start'].date()}",
|
451
|
+
f"{activity['equipment']}",
|
452
|
+
f"\n{activity['distance_km']:.1f} km",
|
453
|
+
re.sub(r"^0 days ", "", f"{activity['elapsed_time']}"),
|
454
|
+
]
|
455
|
+
if activity["calories"]:
|
456
|
+
facts.append(f"{activity['calories']:.0f} kcal")
|
457
|
+
if activity["steps"] and not pd.isna(activity["steps"]):
|
458
|
+
facts.append(f"{activity['steps']:.0f} steps")
|
459
|
+
|
460
|
+
draw.text((35, img.height - 70 + 10), " ".join(facts), font_size=20)
|
352
461
|
|
353
|
-
|
462
|
+
# img_array = np.array(img) / 255
|
354
463
|
|
355
|
-
|
356
|
-
background[background > 1.0] = 1.0
|
357
|
-
background[background < 0.0] = 0.0
|
464
|
+
# weight = np.dstack([img_array[:, :, 0]] * 3)
|
358
465
|
|
359
|
-
background =
|
466
|
+
# background = (1 - weight) * background + img_array
|
467
|
+
# background[background > 1.0] = 1.0
|
468
|
+
# background[background < 0.0] = 0.0
|
360
469
|
|
361
470
|
f = io.BytesIO()
|
362
|
-
|
471
|
+
img.save(f, format="png")
|
472
|
+
# pl.imsave(f, background, format="png")
|
363
473
|
return bytes(f.getbuffer())
|
@@ -29,7 +29,8 @@
|
|
29
29
|
<div class="col-md-3">
|
30
30
|
<ol>
|
31
31
|
{% for activity in activities %}
|
32
|
-
<li><span style="color: {{ activity['color'] }};">█</span> <a
|
32
|
+
<li><span style="color: {{ activity['color'] }};">█</span> <a
|
33
|
+
href="{{ url_for('activity.show', id=activity.id) }}">{{
|
33
34
|
activity.name }}</a></li>
|
34
35
|
{% endfor %}
|
35
36
|
</ol>
|
@@ -54,7 +55,8 @@
|
|
54
55
|
<tbody>
|
55
56
|
{% for activity in activities %}
|
56
57
|
<tr>
|
57
|
-
<td><span style="color: {{ activity['color'] }};">█</span> <a
|
58
|
+
<td><span style="color: {{ activity['color'] }};">█</span> <a
|
59
|
+
href="{{ url_for('activity.show', id=activity.id) }}">{{
|
58
60
|
activity.name }}</a></td>
|
59
61
|
<td>{{ activity.start }}</td>
|
60
62
|
<td>{{ activity.distance_km | round(1) }}</td>
|
@@ -63,6 +65,16 @@
|
|
63
65
|
<td>{{ activity["kind"] }}</td>
|
64
66
|
</tr>
|
65
67
|
{% endfor %}
|
68
|
+
{% if activities|length > 1 %}
|
69
|
+
<tr>
|
70
|
+
<td><b>Total</b></td>
|
71
|
+
<td></td>
|
72
|
+
<td><b>{{ total_distance | round(1) }}</b></td>
|
73
|
+
<td><b>{{ total_elapsed_time }}</b></td>
|
74
|
+
<td></td>
|
75
|
+
<td></td>
|
76
|
+
</tr>
|
77
|
+
{% endif %}
|
66
78
|
</tbody>
|
67
79
|
</table>
|
68
80
|
</div>
|
@@ -64,7 +64,7 @@
|
|
64
64
|
<tbody>
|
65
65
|
{% for activity in activities %}
|
66
66
|
<tr>
|
67
|
-
<td><span style="color: {{ activity['color'] }};">█</span> <a href="
|
67
|
+
<td><span style="color: {{ activity['color'] }};">█</span> <a href="{{ url_for('activity.show', id=activity.id) }}">{{
|
68
68
|
activity.name }}</a></td>
|
69
69
|
<td>{{ activity.start }}</td>
|
70
70
|
<td>{{ activity.distance_km | round(1) }}</td>
|
@@ -21,7 +21,8 @@
|
|
21
21
|
<dt>Elapsed time</dt>
|
22
22
|
<dd>{{ activity.elapsed_time }}</dd>
|
23
23
|
<dt>Start time</dt>
|
24
|
-
<dd><a href="
|
24
|
+
<dd><a href="{{ url_for('activity.day', year=date.year, month=date.month, day=date.day) }}">{{ date }}</a>
|
25
|
+
{{ time }}
|
25
26
|
</dd>
|
26
27
|
<dt>Calories</dt>
|
27
28
|
<dd>{{ activity.calories }}</dd>
|
@@ -29,6 +30,10 @@
|
|
29
30
|
<dd>{{ activity.steps }}</dd>
|
30
31
|
<dt>Equipment</dt>
|
31
32
|
<dd>{{ activity.equipment }}</dd>
|
33
|
+
<dt>New Explorer Tiles</dt>
|
34
|
+
<dd>{{ new_tiles[14] }}</dd>
|
35
|
+
<dt>New Squadratinhos</dt>
|
36
|
+
<dd>{{ new_tiles[17] }}</dd>
|
32
37
|
<dt>ID</dt>
|
33
38
|
<dd>{{ activity.id }}</dd>
|
34
39
|
<dt>Source path</dt>
|
@@ -126,7 +131,7 @@
|
|
126
131
|
<div class="col">
|
127
132
|
<h2>Share picture</h2>
|
128
133
|
|
129
|
-
<img src="
|
134
|
+
<img src="{{ url_for('.sharepic', id=activity.id) }}" />
|
130
135
|
</div>
|
131
136
|
</div>
|
132
137
|
|
@@ -135,7 +140,7 @@
|
|
135
140
|
<div class="col">
|
136
141
|
<h2>Activities with the same name</h2>
|
137
142
|
|
138
|
-
<p><a href="
|
143
|
+
<p><a href="{{ url_for('.name', name=activity['name']) }}">Overview over these activities</a></p>
|
139
144
|
|
140
145
|
<table class="table">
|
141
146
|
<thead>
|
@@ -150,7 +155,7 @@
|
|
150
155
|
<tbody>
|
151
156
|
{% for other_activity in similar_activites %}
|
152
157
|
<tr>
|
153
|
-
<td><a href="
|
158
|
+
<td><a href="{{ url_for('.show', id=other_activity.id) }}">{{ other_activity.start
|
154
159
|
}}</a></td>
|
155
160
|
<td>{{ other_activity.distance_km | round(1) }}</td>
|
156
161
|
<td>{{ other_activity.elapsed_time }}</td>
|