geo-activity-playground 0.24.0__tar.gz → 0.24.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.
Files changed (94) hide show
  1. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/PKG-INFO +1 -1
  2. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/core/activities.py +5 -0
  3. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/importers/strava_api.py +2 -1
  4. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/activity/blueprint.py +1 -1
  5. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/activity/controller.py +1 -1
  6. geo_activity_playground-0.24.2/geo_activity_playground/webui/equipment/controller.py +114 -0
  7. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/equipment/templates/equipment/index.html.j2 +14 -3
  8. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/heatmap/heatmap_controller.py +3 -1
  9. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/summary/controller.py +11 -8
  10. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/pyproject.toml +1 -1
  11. geo_activity_playground-0.24.0/geo_activity_playground/webui/equipment/controller.py +0 -73
  12. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/LICENSE +0 -0
  13. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/__init__.py +0 -0
  14. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/__main__.py +0 -0
  15. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/core/__init__.py +0 -0
  16. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/core/activity_parsers.py +0 -0
  17. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/core/cache_migrations.py +0 -0
  18. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/core/config.py +0 -0
  19. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/core/coordinates.py +0 -0
  20. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/core/heatmap.py +0 -0
  21. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/core/paths.py +0 -0
  22. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/core/privacy_zones.py +0 -0
  23. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/core/similarity.py +0 -0
  24. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/core/tasks.py +0 -0
  25. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/core/test_tiles.py +0 -0
  26. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/core/test_time_conversion.py +0 -0
  27. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/core/tiles.py +0 -0
  28. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/core/time_conversion.py +0 -0
  29. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/explorer/__init__.py +0 -0
  30. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/explorer/grid_file.py +0 -0
  31. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/explorer/tile_visits.py +0 -0
  32. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/explorer/video.py +0 -0
  33. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/importers/__init__.py +0 -0
  34. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/importers/directory.py +0 -0
  35. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/importers/strava_checkout.py +0 -0
  36. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/importers/test_directory.py +0 -0
  37. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/importers/test_strava_api.py +0 -0
  38. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/__init__.py +0 -0
  39. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/activity/__init__.py +0 -0
  40. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/activity/templates/activity/day.html.j2 +0 -0
  41. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/activity/templates/activity/lines.html.j2 +0 -0
  42. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/activity/templates/activity/name.html.j2 +0 -0
  43. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/activity/templates/activity/show.html.j2 +0 -0
  44. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/app.py +0 -0
  45. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/calendar/__init__.py +0 -0
  46. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/calendar/blueprint.py +0 -0
  47. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/calendar/controller.py +0 -0
  48. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/calendar/templates/calendar/index.html.j2 +0 -0
  49. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/calendar/templates/calendar/month.html.j2 +0 -0
  50. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/eddington/__init__.py +0 -0
  51. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/eddington/blueprint.py +0 -0
  52. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/eddington/controller.py +0 -0
  53. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/eddington/templates/eddington/index.html.j2 +0 -0
  54. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/entry_controller.py +0 -0
  55. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/equipment/__init__.py +0 -0
  56. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/equipment/blueprint.py +0 -0
  57. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/explorer/__init__.py +0 -0
  58. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/explorer/blueprint.py +0 -0
  59. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/explorer/controller.py +0 -0
  60. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/explorer/templates/explorer/index.html.j2 +0 -0
  61. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/heatmap/__init__.py +0 -0
  62. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/heatmap/blueprint.py +0 -0
  63. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/heatmap/templates/heatmap/index.html.j2 +0 -0
  64. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/search_controller.py +0 -0
  65. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/square_planner/__init__.py +0 -0
  66. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/square_planner/blueprint.py +0 -0
  67. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/square_planner/controller.py +0 -0
  68. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/square_planner/templates/square_planner/index.html.j2 +0 -0
  69. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/static/android-chrome-192x192.png +0 -0
  70. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/static/android-chrome-384x384.png +0 -0
  71. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/static/android-chrome-512x512.png +0 -0
  72. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/static/apple-touch-icon.png +0 -0
  73. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/static/browserconfig.xml +0 -0
  74. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/static/favicon-16x16.png +0 -0
  75. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/static/favicon-32x32.png +0 -0
  76. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/static/favicon.ico +0 -0
  77. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/static/mstile-150x150.png +0 -0
  78. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/static/safari-pinned-tab.svg +0 -0
  79. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/static/site.webmanifest +0 -0
  80. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/strava_controller.py +0 -0
  81. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/summary/__init__.py +0 -0
  82. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/summary/blueprint.py +0 -0
  83. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/summary/templates/summary/index.html.j2 +0 -0
  84. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/templates/home.html.j2 +0 -0
  85. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/templates/page.html.j2 +0 -0
  86. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/templates/search.html.j2 +0 -0
  87. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/templates/strava-connect.html.j2 +0 -0
  88. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/tile/__init__.py +0 -0
  89. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/tile/blueprint.py +0 -0
  90. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/tile/controller.py +0 -0
  91. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/upload/__init__.py +0 -0
  92. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/upload/blueprint.py +0 -0
  93. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/upload/controller.py +0 -0
  94. {geo_activity_playground-0.24.0 → geo_activity_playground-0.24.2}/geo_activity_playground/webui/upload/templates/upload/index.html.j2 +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: geo-activity-playground
3
- Version: 0.24.0
3
+ Version: 0.24.2
4
4
  Summary: Analysis of geo data activities like rides, runs or hikes.
5
5
  License: MIT
6
6
  Author: Martin Ueding
@@ -48,6 +48,10 @@ class ActivityRepository:
48
48
  self.meta = pd.read_parquet(activities_path())
49
49
  self.meta.index = self.meta["id"]
50
50
  self.meta.index.name = "index"
51
+ if not pd.api.types.is_dtype_equal(
52
+ self.meta["start"].dtype, "datetime64[ns]"
53
+ ):
54
+ self.meta["start"] = convert_to_datetime_ns(self.meta["start"])
51
55
  else:
52
56
  self.meta = pd.DataFrame()
53
57
 
@@ -88,6 +92,7 @@ class ActivityRepository:
88
92
  old_df = self.meta.loc[is_kept]
89
93
  else:
90
94
  old_df = self.meta
95
+
91
96
  self.meta = pd.concat([old_df, new_df])
92
97
  assert pd.api.types.is_dtype_equal(
93
98
  self.meta["start"].dtype, "datetime64[ns]"
@@ -17,6 +17,7 @@ from tqdm import tqdm
17
17
  from geo_activity_playground.core.activities import ActivityRepository
18
18
  from geo_activity_playground.core.config import get_config
19
19
  from geo_activity_playground.core.paths import cache_dir
20
+ from geo_activity_playground.core.time_conversion import convert_to_datetime_ns
20
21
 
21
22
 
22
23
  logger = logging.getLogger(__name__)
@@ -165,7 +166,7 @@ def try_import_strava(repository: ActivityRepository) -> bool:
165
166
  "distance_km": activity.distance.magnitude / 1000,
166
167
  "name": activity.name,
167
168
  "kind": str(activity.type),
168
- "start": activity.start_date,
169
+ "start": convert_to_datetime_ns(activity.start_date),
169
170
  "elapsed_time": activity.elapsed_time,
170
171
  "equipment": gear_names[activity.gear_id],
171
172
  "calories": detailed_activity.calories,
@@ -22,7 +22,7 @@ def make_activity_blueprint(
22
22
  repository, tile_visit_accessor, privacy_zones
23
23
  )
24
24
 
25
- @blueprint.route("/activity/all")
25
+ @blueprint.route("/all")
26
26
  def all():
27
27
  return render_template(
28
28
  "activity/lines.html.j2", **activity_controller.render_all()
@@ -452,7 +452,7 @@ def make_sharepic(activity: ActivityMeta, time_series: pd.DataFrame) -> bytes:
452
452
  f"\n{activity['distance_km']:.1f} km",
453
453
  re.sub(r"^0 days ", "", f"{activity['elapsed_time']}"),
454
454
  ]
455
- if activity["calories"]:
455
+ if activity["calories"] and not pd.isna(activity["calories"]):
456
456
  facts.append(f"{activity['calories']:.0f} kcal")
457
457
  if activity["steps"] and not pd.isna(activity["steps"]):
458
458
  facts.append(f"{activity['steps']:.0f} steps")
@@ -0,0 +1,114 @@
1
+ import altair as alt
2
+ import pandas as pd
3
+
4
+ from geo_activity_playground.core.activities import ActivityRepository
5
+ from geo_activity_playground.core.config import get_config
6
+
7
+
8
+ class EquipmentController:
9
+ def __init__(self, repository: ActivityRepository) -> None:
10
+ self._repository = repository
11
+
12
+ def render(self) -> dict:
13
+ kind_per_equipment = self._repository.meta.groupby("equipment").apply(
14
+ lambda group: group.groupby("kind")
15
+ .apply(lambda group2: sum(group2["distance_km"]), include_groups=False)
16
+ .idxmax(),
17
+ include_groups=False,
18
+ )
19
+
20
+ equipment_summary = (
21
+ self._repository.meta.groupby("equipment")
22
+ .apply(
23
+ lambda group: pd.Series(
24
+ {
25
+ "total_distance_km": group["distance_km"].sum(),
26
+ "first_use": group["start"].iloc[0],
27
+ "last_use": group["start"].iloc[-1],
28
+ },
29
+ ),
30
+ include_groups=False,
31
+ )
32
+ .sort_values("last_use", ascending=False)
33
+ )
34
+
35
+ equipment_summary["primarily_used_for"] = None
36
+ for equipment, kind in kind_per_equipment.items():
37
+ equipment_summary.loc[equipment, "primarily_used_for"] = kind
38
+
39
+ equipment_variables = {}
40
+ for equipment in equipment_summary.index:
41
+ selection = self._repository.meta.loc[
42
+ self._repository.meta["equipment"] == equipment
43
+ ]
44
+ total_distances = pd.DataFrame(
45
+ {
46
+ "time": selection["start"],
47
+ "total_distance_km": selection["distance_km"].cumsum(),
48
+ }
49
+ )
50
+
51
+ total_distances_plot = (
52
+ alt.Chart(
53
+ total_distances,
54
+ height=300,
55
+ width=300,
56
+ title="Usage over Time",
57
+ )
58
+ .mark_line(interpolate="step-after")
59
+ .encode(
60
+ alt.X("time", title="Date"),
61
+ alt.Y("total_distance_km", title="Cumulative distance / km"),
62
+ )
63
+ .interactive()
64
+ .to_json(format="vega")
65
+ )
66
+
67
+ yearly_distance_plot = (
68
+ alt.Chart(
69
+ selection,
70
+ height=300,
71
+ title="Yearly distance",
72
+ )
73
+ .mark_bar()
74
+ .encode(
75
+ alt.X("year(start):O", title="Year"),
76
+ alt.Y("sum(distance_km)", title="Distance / km"),
77
+ )
78
+ .to_json(format="vega")
79
+ )
80
+
81
+ usages_plot = (
82
+ alt.Chart(
83
+ selection,
84
+ height=300,
85
+ title="Kinds",
86
+ )
87
+ .mark_bar()
88
+ .encode(
89
+ alt.X("kind", title="Year"),
90
+ alt.Y("sum(distance_km)", title="Distance / km"),
91
+ )
92
+ .to_json(format="vega")
93
+ )
94
+
95
+ equipment_variables[equipment] = {
96
+ "total_distances_plot": total_distances_plot,
97
+ "total_distances_plot_id": f"total_distances_plot_{hash(equipment)}",
98
+ "yearly_distance_plot": yearly_distance_plot,
99
+ "yearly_distance_plot_id": f"yearly_distance_plot_{hash(equipment)}",
100
+ "usages_plot": usages_plot,
101
+ "usages_plot_id": f"usages_plot_{hash(equipment)}",
102
+ }
103
+
104
+ config = get_config()
105
+ if "offsets" in config:
106
+ for equipment, offset in config["offsets"].items():
107
+ equipment_summary.loc[equipment, "total_distance_km"] += offset
108
+
109
+ return {
110
+ "equipment_variables": equipment_variables,
111
+ "equipment_summary": equipment_summary.reset_index().to_dict(
112
+ orient="records"
113
+ ),
114
+ }
@@ -13,6 +13,7 @@
13
13
  <thead>
14
14
  <tr>
15
15
  <th>Equipment</th>
16
+ <th>Primarily used for</th>
16
17
  <th style="text-align: right;">Distance / km</th>
17
18
  <th style="text-align: right;">First use</th>
18
19
  <th style="text-align: right;">Last use</th>
@@ -22,6 +23,7 @@
22
23
  {% for equipment in equipment_summary %}
23
24
  <tr>
24
25
  <td>{{ equipment.equipment }}</td>
26
+ <td>{{ equipment.primarily_used_for }}</td>
25
27
  <td style="text-align: right;">{{ equipment.total_distance_km|int }}</td>
26
28
  <td style="text-align: right;">{{ equipment.first_use.date() }}</td>
27
29
  <td style="text-align: right;">{{ equipment.last_use.date() }}</td>
@@ -34,13 +36,22 @@
34
36
 
35
37
  <div class="row mb-3">
36
38
  <div class="col">
37
- <h2>Usage over time</h2>
39
+ <h2>Details for each equipment</h2>
38
40
  </div>
39
41
  </div>
40
42
 
43
+ {% for equipment, data in equipment_variables.items() %}
44
+ <h3>{{ equipment }}</h3>
41
45
  <div class="row mb-3">
42
- <div class="col">
43
- {{ vega_direct("total_distances_plot", total_distances_plot) }}
46
+ <div class="col-md-4">
47
+ {{ vega_direct(data.total_distances_plot_id, data.total_distances_plot) }}
48
+ </div>
49
+ <div class="col-md-4">
50
+ {{ vega_direct(data.yearly_distance_plot_id, data.yearly_distance_plot) }}
51
+ </div>
52
+ <div class="col-md-4">
53
+ {{ vega_direct(data.usages_plot_id, data.usages_plot) }}
44
54
  </div>
45
55
  </div>
56
+ {% endfor %}
46
57
  {% endblock %}
@@ -11,6 +11,7 @@ from geo_activity_playground.core.activities import ActivityRepository
11
11
  from geo_activity_playground.core.heatmap import convert_to_grayscale
12
12
  from geo_activity_playground.core.heatmap import GeoBounds
13
13
  from geo_activity_playground.core.heatmap import get_sensible_zoom_level
14
+ from geo_activity_playground.core.heatmap import PixelBounds
14
15
  from geo_activity_playground.core.tasks import work_tracker
15
16
  from geo_activity_playground.core.tiles import get_tile
16
17
  from geo_activity_playground.core.tiles import get_tile_upper_left_lat_lon
@@ -140,8 +141,9 @@ class HeatmapController:
140
141
  ) -> bytes:
141
142
  geo_bounds = GeoBounds(south, west, north, east)
142
143
  tile_bounds = get_sensible_zoom_level(geo_bounds, (4000, 4000))
144
+ pixel_bounds = PixelBounds.from_tile_bounds(tile_bounds)
143
145
 
144
- background = np.zeros((*tile_bounds.shape, 3))
146
+ background = np.zeros((*pixel_bounds.shape, 3))
145
147
  for x in range(tile_bounds.x_tile_min, tile_bounds.x_tile_max):
146
148
  for y in range(tile_bounds.y_tile_min, tile_bounds.y_tile_max):
147
149
  tile = np.array(get_tile(tile_bounds.zoom, x, y)) / 255
@@ -61,11 +61,13 @@ def nominate_activities(meta: pd.DataFrame) -> dict[int, list[str]]:
61
61
  i = subset["elapsed_time"].idxmax()
62
62
  nominations[i].append(f"Longest elapsed time: {meta.loc[i].elapsed_time}")
63
63
 
64
- i = subset["calories"].idxmax()
65
- nominations[i].append(f"Most calories burnt: {meta.loc[i].calories:.0f} kcal")
64
+ if "calories" in subset.columns:
65
+ i = subset["calories"].idxmax()
66
+ nominations[i].append(f"Most calories burnt: {meta.loc[i].calories:.0f} kcal")
66
67
 
67
- i = subset["steps"].idxmax()
68
- nominations[i].append(f"Most steps: {meta.loc[i].steps:.0f}")
68
+ if "steps" in subset:
69
+ i = subset["steps"].idxmax()
70
+ nominations[i].append(f"Most steps: {meta.loc[i].steps:.0f}")
69
71
 
70
72
  for kind, group in meta.groupby("kind"):
71
73
  for key, text in [
@@ -83,10 +85,11 @@ def nominate_activities(meta: pd.DataFrame) -> dict[int, list[str]]:
83
85
  ),
84
86
  ("steps", lambda row: f"Most steps for {row.kind}: {row.steps:.0f}"),
85
87
  ]:
86
- series = group[key]
87
- i = series.idxmax()
88
- if not pd.isna(i):
89
- nominations[i].append(text(meta.loc[i]))
88
+ if key in group.columns:
89
+ series = group[key]
90
+ i = series.idxmax()
91
+ if not pd.isna(i):
92
+ nominations[i].append(text(meta.loc[i]))
90
93
 
91
94
  return nominations
92
95
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "geo-activity-playground"
3
- version = "0.24.0"
3
+ version = "0.24.2"
4
4
  description = "Analysis of geo data activities like rides, runs or hikes."
5
5
  authors = ["Martin Ueding <mu@martin-ueding.de>"]
6
6
  license = "MIT"
@@ -1,73 +0,0 @@
1
- import altair as alt
2
- import pandas as pd
3
-
4
- from geo_activity_playground.core.activities import ActivityRepository
5
- from geo_activity_playground.core.config import get_config
6
-
7
-
8
- class EquipmentController:
9
- def __init__(self, repository: ActivityRepository) -> None:
10
- self._repository = repository
11
-
12
- def render(self) -> dict:
13
- total_distances = (
14
- self._repository.meta.groupby("equipment")
15
- .apply(
16
- lambda group: pd.DataFrame(
17
- {
18
- "time": group["start"],
19
- "total_distance_km": group["distance_km"].cumsum(),
20
- }
21
- ),
22
- include_groups=False,
23
- )
24
- .reset_index()
25
- )
26
-
27
- plot = (
28
- alt.Chart(
29
- total_distances,
30
- height=250,
31
- width=250,
32
- title="Equipment Usage over Time",
33
- )
34
- .mark_line(interpolate="step-after")
35
- .encode(
36
- alt.X("time", title="Date"),
37
- alt.Y("total_distance_km", title="Cumulative distance / km"),
38
- )
39
- .facet("equipment", columns=4, title="Equipment")
40
- .resolve_scale(y="independent")
41
- .resolve_axis(x="independent")
42
- .interactive()
43
- .to_json(format="vega")
44
- )
45
-
46
- equipment_summary = (
47
- self._repository.meta.groupby("equipment")
48
- .apply(
49
- lambda group: pd.DataFrame(
50
- {
51
- "total_distance_km": group["distance_km"].sum(),
52
- "first_use": group["start"].iloc[0],
53
- "last_use": group["start"].iloc[-1],
54
- },
55
- index=[0],
56
- ),
57
- include_groups=False,
58
- )
59
- .reset_index()
60
- .sort_values("last_use", ascending=False)
61
- )
62
-
63
- config = get_config()
64
- if "offsets" in config:
65
- for equipment, offset in config["offsets"].items():
66
- equipment_summary.loc[
67
- equipment_summary["equipment"] == equipment, "total_distance_km"
68
- ] += offset
69
-
70
- return {
71
- "total_distances_plot": plot,
72
- "equipment_summary": equipment_summary.to_dict(orient="records"),
73
- }