geo-activity-playground 1.0.0__py3-none-any.whl → 1.2.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.
Files changed (73) hide show
  1. geo_activity_playground/alembic/versions/dc8073871da7_add_plotspec_group_by.py +28 -0
  2. geo_activity_playground/core/config.py +1 -0
  3. geo_activity_playground/core/datamodel.py +41 -3
  4. geo_activity_playground/core/parametric_plot.py +101 -47
  5. geo_activity_playground/webui/app.py +7 -0
  6. geo_activity_playground/webui/blueprints/activity_blueprint.py +11 -10
  7. geo_activity_playground/webui/blueprints/auth_blueprint.py +3 -2
  8. geo_activity_playground/webui/blueprints/bubble_chart_blueprint.py +2 -1
  9. geo_activity_playground/webui/blueprints/calendar_blueprint.py +3 -2
  10. geo_activity_playground/webui/blueprints/eddington_blueprints.py +3 -2
  11. geo_activity_playground/webui/blueprints/entry_views.py +11 -11
  12. geo_activity_playground/webui/blueprints/equipment_blueprint.py +2 -1
  13. geo_activity_playground/webui/blueprints/explorer_blueprint.py +47 -13
  14. geo_activity_playground/webui/blueprints/export_blueprint.py +3 -2
  15. geo_activity_playground/webui/blueprints/hall_of_fame_blueprint.py +79 -0
  16. geo_activity_playground/webui/blueprints/photo_blueprint.py +65 -56
  17. geo_activity_playground/webui/blueprints/plot_builder_blueprint.py +38 -19
  18. geo_activity_playground/webui/blueprints/settings_blueprint.py +17 -0
  19. geo_activity_playground/webui/blueprints/summary_blueprint.py +114 -240
  20. geo_activity_playground/webui/columns.py +40 -7
  21. geo_activity_playground/webui/static/{browserconfig.xml → favicons/browserconfig.xml} +1 -1
  22. geo_activity_playground/webui/static/{site.webmanifest → favicons/site.webmanifest} +2 -2
  23. geo_activity_playground/webui/static/server-side-explorer.js +7 -2
  24. geo_activity_playground/webui/templates/activity/name.html.j2 +4 -4
  25. geo_activity_playground/webui/templates/activity/show.html.j2 +8 -8
  26. geo_activity_playground/webui/templates/eddington/distance.html.j2 +3 -3
  27. geo_activity_playground/webui/templates/eddington/elevation_gain.html.j2 +3 -3
  28. geo_activity_playground/webui/templates/elevation_eddington/index.html.j2 +3 -3
  29. geo_activity_playground/webui/templates/equipment/index.html.j2 +4 -4
  30. geo_activity_playground/webui/templates/explorer/server-side.html.j2 +5 -4
  31. geo_activity_playground/webui/templates/hall_of_fame/index.html.j2 +57 -0
  32. geo_activity_playground/webui/templates/home.html.j2 +2 -12
  33. geo_activity_playground/webui/templates/page.html.j2 +23 -37
  34. geo_activity_playground/webui/templates/photo/new.html.j2 +1 -1
  35. geo_activity_playground/webui/templates/plot-macros.html.j2 +72 -0
  36. geo_activity_playground/webui/templates/plot_builder/edit.html.j2 +12 -7
  37. geo_activity_playground/webui/templates/plot_builder/import-spec.html.j2 +24 -0
  38. geo_activity_playground/webui/templates/plot_builder/index.html.j2 +5 -0
  39. geo_activity_playground/webui/templates/settings/index.html.j2 +9 -0
  40. geo_activity_playground/webui/templates/settings/tile-source.html.j2 +33 -0
  41. geo_activity_playground/webui/templates/summary/index.html.j2 +23 -230
  42. geo_activity_playground/webui/templates/summary/vega-chart.html.j2 +3 -0
  43. {geo_activity_playground-1.0.0.dist-info → geo_activity_playground-1.2.0.dist-info}/METADATA +1 -1
  44. {geo_activity_playground-1.0.0.dist-info → geo_activity_playground-1.2.0.dist-info}/RECORD +73 -66
  45. /geo_activity_playground/webui/static/{bootstrap-dark-mode.js → bootstrap/bootstrap-dark-mode.js} +0 -0
  46. /geo_activity_playground/webui/static/{bootstrap.bundle.min.js → bootstrap/bootstrap.bundle.min.js} +0 -0
  47. /geo_activity_playground/webui/static/{bootstrap.min.css → bootstrap/bootstrap.min.css} +0 -0
  48. /geo_activity_playground/webui/static/{android-chrome-192x192.png → favicons/android-chrome-192x192.png} +0 -0
  49. /geo_activity_playground/webui/static/{android-chrome-512x512.png → favicons/android-chrome-512x512.png} +0 -0
  50. /geo_activity_playground/webui/static/{apple-touch-icon.png → favicons/apple-touch-icon.png} +0 -0
  51. /geo_activity_playground/webui/static/{favicon-16x16.png → favicons/favicon-16x16.png} +0 -0
  52. /geo_activity_playground/webui/static/{favicon-32x32.png → favicons/favicon-32x32.png} +0 -0
  53. /geo_activity_playground/webui/static/{favicon-48x48.png → favicons/favicon-48x48.png} +0 -0
  54. /geo_activity_playground/webui/static/{favicon.ico → favicons/favicon.ico} +0 -0
  55. /geo_activity_playground/webui/static/{favicon.svg → favicons/favicon.svg} +0 -0
  56. /geo_activity_playground/webui/static/{mstile-150x150.png → favicons/mstile-150x150.png} +0 -0
  57. /geo_activity_playground/webui/static/{web-app-manifest-192x192.png → favicons/web-app-manifest-192x192.png} +0 -0
  58. /geo_activity_playground/webui/static/{web-app-manifest-512x512.png → favicons/web-app-manifest-512x512.png} +0 -0
  59. /geo_activity_playground/webui/static/{Leaflet.fullscreen.min.js → leaflet/Leaflet.fullscreen.min.js} +0 -0
  60. /geo_activity_playground/webui/static/{MarkerCluster.Default.css → leaflet/MarkerCluster.Default.css} +0 -0
  61. /geo_activity_playground/webui/static/{MarkerCluster.css → leaflet/MarkerCluster.css} +0 -0
  62. /geo_activity_playground/webui/static/{fullscreen.png → leaflet/fullscreen.png} +0 -0
  63. /geo_activity_playground/webui/static/{fullscreen@2x.png → leaflet/fullscreen@2x.png} +0 -0
  64. /geo_activity_playground/webui/static/{leaflet.css → leaflet/leaflet.css} +0 -0
  65. /geo_activity_playground/webui/static/{leaflet.fullscreen.css → leaflet/leaflet.fullscreen.css} +0 -0
  66. /geo_activity_playground/webui/static/{leaflet.js → leaflet/leaflet.js} +0 -0
  67. /geo_activity_playground/webui/static/{leaflet.markercluster.js → leaflet/leaflet.markercluster.js} +0 -0
  68. /geo_activity_playground/webui/static/{vega-embed@6 → vega/vega-embed@6.js} +0 -0
  69. /geo_activity_playground/webui/static/{vega-lite@4 → vega/vega-lite@4.js} +0 -0
  70. /geo_activity_playground/webui/static/{vega@5 → vega/vega@5.js} +0 -0
  71. {geo_activity_playground-1.0.0.dist-info → geo_activity_playground-1.2.0.dist-info}/LICENSE +0 -0
  72. {geo_activity_playground-1.0.0.dist-info → geo_activity_playground-1.2.0.dist-info}/WHEEL +0 -0
  73. {geo_activity_playground-1.0.0.dist-info → geo_activity_playground-1.2.0.dist-info}/entry_points.txt +0 -0
@@ -1,4 +1,3 @@
1
- import collections
2
1
  import datetime
3
2
 
4
3
  import altair as alt
@@ -9,224 +8,41 @@ from flask import render_template
9
8
  from flask import request
10
9
 
11
10
  from ...core.activities import ActivityRepository
12
- from ...core.activities import make_geojson_from_time_series
13
11
  from ...core.config import Config
14
12
  from ...core.datamodel import DB
15
13
  from ...core.datamodel import PlotSpec
16
14
  from ...core.meta_search import apply_search_query
17
15
  from ...core.parametric_plot import make_parametric_plot
18
- from ..columns import column_distance
19
- from ..columns import column_elevation_gain
20
16
  from ..columns import ColumnDescription
17
+ from ..columns import META_COLUMNS
21
18
  from ..plot_util import make_kind_scale
22
19
  from ..search_util import search_query_from_form
23
20
  from ..search_util import SearchQueryHistory
24
21
 
25
22
 
26
- def make_summary_blueprint(
27
- repository: ActivityRepository,
28
- config: Config,
29
- search_query_history: SearchQueryHistory,
30
- ) -> Blueprint:
31
- blueprint = Blueprint("summary", __name__, template_folder="templates")
32
-
33
- @blueprint.route("/")
34
- def index():
35
- query = search_query_from_form(request.args)
36
- search_query_history.register_query(query)
37
- activities = apply_search_query(repository.meta, query)
38
-
39
- kind_scale = make_kind_scale(repository.meta, config)
40
- df = activities
41
-
42
- nominations = nominate_activities(df)
43
-
44
- return render_template(
45
- "summary/index.html.j2",
46
- plot_distance_heatmaps=plot_heatmaps(df, column_distance, config),
47
- plot_elevation_gain_heatmaps=plot_heatmaps(
48
- df, column_elevation_gain, config
49
- ),
50
- plot_monthly_distance=plot_monthly_sums(df, column_distance, kind_scale),
51
- plot_monthly_elevation_gain=plot_monthly_sums(
52
- df, column_elevation_gain, kind_scale
53
- ),
54
- plot_yearly_distance=plot_yearly_sums(df, column_distance, kind_scale),
55
- plot_yearly_elevation_gain=plot_yearly_sums(
56
- df, column_elevation_gain, kind_scale
57
- ),
58
- plot_year_cumulative=plot_year_cumulative(df, column_distance),
59
- plot_year_elevation_gain_cumulative=plot_year_cumulative(
60
- df, column_elevation_gain
61
- ),
62
- tabulate_year_kind_mean=tabulate_year_kind_mean(df, column_distance)
63
- .reset_index()
64
- .to_dict(orient="split"),
65
- tabulate_year_kind_mean_elevation_gain=tabulate_year_kind_mean(
66
- df, column_elevation_gain
67
- )
68
- .reset_index()
69
- .to_dict(orient="split"),
70
- plot_weekly_distance=plot_weekly_sums(df, column_distance, kind_scale),
71
- plot_weekly_elevation_gain=plot_weekly_sums(
72
- df, column_elevation_gain, kind_scale
73
- ),
74
- nominations=[
75
- (
76
- repository.get_activity_by_id(activity_id),
77
- reasons,
78
- make_geojson_from_time_series(
79
- repository.get_time_series(activity_id)
80
- ),
81
- )
82
- for activity_id, reasons in nominations.items()
83
- ],
84
- query=query.to_jinja(),
85
- custom_plots=[
86
- (spec, make_parametric_plot(repository.meta, spec))
87
- for spec in DB.session.scalars(sqlalchemy.select(PlotSpec)).all()
88
- ],
89
- )
90
-
91
- return blueprint
92
-
93
-
94
- def nominate_activities(meta: pd.DataFrame) -> dict[int, list[str]]:
95
- nominations: dict[int, list[str]] = collections.defaultdict(list)
96
-
97
- _nominate_activities_inner(meta, "", nominations)
98
-
99
- for kind, group in meta.groupby("kind"):
100
- _nominate_activities_inner(group, f" for {kind}", nominations)
101
- for equipment, group in meta.groupby("equipment"):
102
- _nominate_activities_inner(group, f" with {equipment}", nominations)
103
-
104
- return nominations
105
-
106
-
107
- def _nominate_activities_inner(
108
- meta: pd.DataFrame, title_suffix: str, nominations: dict[int, list[str]]
109
- ) -> None:
110
- ratings = [
111
- ("distance_km", "Greatest distance", "{:.1f} km"),
112
- ("elapsed_time", "Longest elapsed time", "{}"),
113
- ("average_speed_moving_kmh", "Highest average moving speed", "{:.1f} km/h"),
114
- ("average_speed_elapsed_kmh", "Highest average elapsed speed", "{:.1f} km/h"),
115
- ("calories", "Most calories burnt", "{:.0f}"),
116
- ("steps", "Most steps", "{:.0f}"),
117
- ("elevation_gain", "Largest elevation gain", "{:.0f} m"),
118
- ]
119
-
120
- for variable, title, format_str in ratings:
121
- if variable in meta.columns and not pd.isna(meta[variable]).all():
122
- i = meta[variable].idxmax()
123
- value = meta.loc[i, variable]
124
- format_applied = format_str.format(value)
125
- nominations[i].append(f"{title}{title_suffix}: {format_applied}")
126
-
127
-
128
- def plot_heatmaps(
129
- meta: pd.DataFrame, column: ColumnDescription, config: Config
130
- ) -> dict[int, str]:
131
- return {
132
- year: alt.Chart(
133
- meta.loc[(meta["year"] == year)],
134
- title=f"Daily {column.display_name} Heatmap",
135
- )
136
- .mark_rect()
137
- .encode(
138
- alt.X("date(start):O", title="Day of month"),
139
- alt.Y(
140
- "yearmonth(start):O",
141
- # scale=alt.Scale(reverse=True),
142
- title="Year and month",
143
- ),
144
- alt.Color(
145
- f"sum({column.name})",
146
- scale=alt.Scale(scheme=config.color_scheme_for_counts),
147
- ),
148
- [
149
- alt.Tooltip("yearmonthdate(start)", title="Date"),
150
- alt.Tooltip(
151
- f"sum({column.name})",
152
- format=column.format,
153
- title=f"Total {column.display_name} / {column.unit}",
154
- ),
155
- alt.Tooltip(f"count({column.name})", title="Number of activities"),
156
- ],
157
- )
158
- .to_json(format="vega")
159
- for year in sorted(meta["year"].unique())
160
- }
161
-
162
-
163
- def plot_monthly_sums(
164
- meta: pd.DataFrame, column: ColumnDescription, kind_scale: alt.Scale
165
- ) -> str:
23
+ def plot_per_year_per_kind(df: pd.DataFrame, column: ColumnDescription) -> str:
166
24
  return (
167
25
  alt.Chart(
168
- meta.loc[
169
- (
170
- meta["start"]
171
- >= pd.to_datetime(
172
- datetime.datetime.now() - datetime.timedelta(days=2 * 365)
173
- )
174
- )
175
- ],
176
- title=f"Monthly {column.display_name}",
26
+ df,
27
+ title=f"{column.display_name} per Year",
177
28
  )
178
29
  .mark_bar()
179
30
  .encode(
180
- alt.X("month(start)", title="Month"),
31
+ alt.X("year:O", title="Year"),
181
32
  alt.Y(
182
- f"sum({column.name})",
183
- title=f"{column.display_name} / {column.unit}",
33
+ f"sum({column.name})", title=f"{column.display_name} / {column.unit}"
184
34
  ),
185
- alt.Color("kind", scale=kind_scale, title="Kind"),
186
- alt.Column("year(start):O", title="Year"),
35
+ alt.Color("kind", title="Kind"),
187
36
  [
188
- alt.Tooltip("yearmonth(start)", title="Year and Month"),
37
+ alt.Tooltip("year", title="Year"),
189
38
  alt.Tooltip("kind", title="Kind"),
190
39
  alt.Tooltip(
191
40
  f"sum({column.name})",
192
- format=column.format,
193
- title=f"Total {column.display_name} / {column.unit}",
194
- ),
195
- alt.Tooltip(f"count({column.name})", title="Number of activities"),
196
- ],
197
- )
198
- .resolve_axis(x="independent")
199
- .to_json(format="vega")
200
- )
201
-
202
-
203
- def plot_yearly_sums(
204
- df: pd.DataFrame, column: ColumnDescription, kind_scale: alt.Scale
205
- ) -> str:
206
- year_kind_total = (
207
- df[["year", "kind", column.name, "hours"]]
208
- .groupby(["year", "kind"])
209
- .sum()
210
- .reset_index()
211
- )
212
-
213
- return (
214
- alt.Chart(year_kind_total, title=f"Total {column.display_name} per Year")
215
- .mark_bar()
216
- .encode(
217
- alt.X("year:O", title="Year"),
218
- alt.Y(column.name, title=f"{column.display_name} / {column.unit}"),
219
- alt.Color("kind", scale=kind_scale, title="Kind"),
220
- [
221
- alt.Tooltip("year:O", title="Year"),
222
- alt.Tooltip("kind", title="Kind"),
223
- alt.Tooltip(
224
- column.name,
225
41
  title=f"{column.display_name} / {column.unit}",
226
- format=column.format,
227
42
  ),
228
43
  ],
229
44
  )
45
+ .interactive()
230
46
  .to_json(format="vega")
231
47
  )
232
48
 
@@ -250,7 +66,6 @@ def plot_year_cumulative(df: pd.DataFrame, column: ColumnDescription) -> str:
250
66
  return (
251
67
  alt.Chart(
252
68
  year_cumulative,
253
- width=500,
254
69
  title=f"Cumulative {column.display_name} per Year",
255
70
  )
256
71
  .mark_line()
@@ -273,65 +88,124 @@ def plot_year_cumulative(df: pd.DataFrame, column: ColumnDescription) -> str:
273
88
  )
274
89
 
275
90
 
276
- def tabulate_year_kind_mean(
277
- df: pd.DataFrame, column: ColumnDescription
278
- ) -> pd.DataFrame:
279
- year_kind_mean = (
280
- df[["year", "kind", column.name, "hours"]]
281
- .groupby(["year", "kind"])
282
- .mean()
283
- .reset_index()
284
- )
285
-
286
- year_kind_mean_distance = year_kind_mean.pivot(
287
- index="year", columns="kind", values=column.name
288
- )
289
-
290
- return year_kind_mean_distance
291
-
292
-
293
- def plot_weekly_sums(
294
- df: pd.DataFrame, column: ColumnDescription, kind_scale: alt.Scale
295
- ) -> str:
296
- week_kind_total_distance = (
297
- df[["iso_year", "week", "kind", column.name]]
298
- .groupby(["iso_year", "week", "kind"])
299
- .sum()
300
- .reset_index()
301
- )
302
- week_kind_total_distance["year_week"] = [
303
- f"{year}-{week:02d}"
304
- for year, week in zip(
305
- week_kind_total_distance["iso_year"], week_kind_total_distance["week"]
91
+ def plot_per_iso_week(df: pd.DataFrame, column: ColumnDescription) -> str:
92
+ return (
93
+ alt.Chart(
94
+ df,
95
+ title=f"{column.display_name} per Week",
306
96
  )
307
- ]
97
+ .mark_circle()
98
+ .encode(
99
+ alt.X("week:O", title="ISO Week"),
100
+ alt.Y("iso_year:O", title="ISO Year"),
101
+ alt.Size(
102
+ f"sum({column.name})", title=f"{column.display_name} / {column.unit}"
103
+ ),
104
+ [
105
+ alt.Tooltip("iso_year", title="ISO Year"),
106
+ alt.Tooltip("week", title="ISO Week"),
107
+ alt.Tooltip(
108
+ f"sum({column.name})",
109
+ title=f"{column.display_name} / {column.unit}",
110
+ format=column.format,
111
+ ),
112
+ ],
113
+ )
114
+ .interactive()
115
+ .to_json(format="vega")
116
+ )
308
117
 
309
- last_year = week_kind_total_distance["iso_year"].iloc[-1]
310
- last_week = week_kind_total_distance["week"].iloc[-1]
311
118
 
119
+ def heatmap_per_day(df: pd.DataFrame, column: ColumnDescription) -> str:
312
120
  return (
313
121
  alt.Chart(
314
- week_kind_total_distance.loc[
315
- (week_kind_total_distance["iso_year"] == last_year)
316
- | (week_kind_total_distance["iso_year"] == last_year - 1)
317
- & (week_kind_total_distance["week"] >= last_week)
318
- ],
319
- title=f"Weekly {column.display_name}",
122
+ _filter_past_year(df),
123
+ title=f"{column.display_name} per day",
320
124
  )
321
- .mark_bar()
125
+ .mark_rect()
322
126
  .encode(
323
- alt.X("year_week", title="Year and Week"),
324
- alt.Y(column.name, title=f"{column.display_name} / {column.unit}"),
325
- alt.Color("kind", scale=kind_scale, title="Kind"),
127
+ alt.X("iso_year_week:O", title="ISO Year and Week"),
128
+ alt.Y(
129
+ "iso_day:O",
130
+ # scale=alt.Scale(
131
+ # domain=list(range(1, 8)),
132
+ # range=[
133
+ # "Monday",
134
+ # "Tuesday",
135
+ # "Wednesday",
136
+ # "Thursday",
137
+ # "Friday",
138
+ # "Saturday",
139
+ # "Sunday",
140
+ # ],
141
+ # ),
142
+ title="ISO Weekday",
143
+ ),
144
+ alt.Color(
145
+ f"sum({column.name})",
146
+ scale=alt.Scale(scheme="viridis"),
147
+ title=f"{column.display_name} / {column.unit}",
148
+ ),
326
149
  [
327
- alt.Tooltip("year_week", title="Year and Week"),
328
- alt.Tooltip("kind", title="Kind"),
150
+ alt.Tooltip("iso_year_week", title="ISO Year and Week"),
151
+ alt.Tooltip("iso_day", title="ISO Day"),
329
152
  alt.Tooltip(
330
- column.name,
153
+ f"sum({column.name})",
331
154
  title=f"{column.display_name} / {column.unit}",
332
155
  format=column.format,
333
156
  ),
334
157
  ],
335
158
  )
159
+ .interactive()
336
160
  .to_json(format="vega")
337
161
  )
162
+
163
+
164
+ def _filter_past_year(df: pd.DataFrame) -> pd.DataFrame:
165
+ now = datetime.datetime.combine(datetime.date.today(), datetime.time.min)
166
+ start = now - datetime.timedelta(days=365)
167
+ return df.loc[df["start"] >= start]
168
+
169
+
170
+ def make_summary_blueprint(
171
+ repository: ActivityRepository,
172
+ config: Config,
173
+ search_query_history: SearchQueryHistory,
174
+ ) -> Blueprint:
175
+ blueprint = Blueprint("summary", __name__, template_folder="templates")
176
+
177
+ @blueprint.route("/")
178
+ def index():
179
+ query = search_query_from_form(request.args)
180
+ search_query_history.register_query(query)
181
+ activities = apply_search_query(repository.meta, query)
182
+
183
+ kind_scale = make_kind_scale(repository.meta, config)
184
+ df = activities
185
+
186
+ return render_template(
187
+ "summary/index.html.j2",
188
+ query=query.to_jinja(),
189
+ custom_plots=[
190
+ (spec, make_parametric_plot(repository.meta, spec))
191
+ for spec in DB.session.scalars(sqlalchemy.select(PlotSpec)).all()
192
+ ],
193
+ plot_per_year_per_kind={
194
+ column.display_name: plot_per_year_per_kind(df, column)
195
+ for column in META_COLUMNS
196
+ },
197
+ plot_per_year_cumulative={
198
+ column.display_name: plot_year_cumulative(df, column)
199
+ for column in META_COLUMNS
200
+ },
201
+ plot_per_iso_week={
202
+ column.display_name: plot_per_iso_week(df, column)
203
+ for column in META_COLUMNS
204
+ },
205
+ heatmap_per_day={
206
+ column.display_name: heatmap_per_day(df, column)
207
+ for column in META_COLUMNS
208
+ },
209
+ )
210
+
211
+ return blueprint
@@ -15,19 +15,44 @@ column_distance = ColumnDescription(
15
15
  unit="km",
16
16
  format=".1f",
17
17
  )
18
-
19
- column_elevation = ColumnDescription(
20
- name="elevation",
21
- display_name="Elevation",
22
- unit="m",
23
- format=".0f",
24
- )
25
18
  column_elevation_gain = ColumnDescription(
26
19
  name="elevation_gain",
27
20
  display_name="Elevation Gain",
28
21
  unit="m",
29
22
  format=".0f",
30
23
  )
24
+ column_hours = ColumnDescription(
25
+ name="hours",
26
+ display_name="Elapsed time",
27
+ unit="h",
28
+ format=".1f",
29
+ )
30
+ column_hours_moving = ColumnDescription(
31
+ name="hours_moving",
32
+ display_name="Moving time",
33
+ unit="h",
34
+ format=".1f",
35
+ )
36
+ column_calories = ColumnDescription(
37
+ name="calories",
38
+ display_name="Energy",
39
+ unit="kcal",
40
+ format=".1f",
41
+ )
42
+ column_steps = ColumnDescription(
43
+ name="steps",
44
+ display_name="Steps",
45
+ unit="1",
46
+ format=".1f",
47
+ )
48
+ META_COLUMNS = [
49
+ column_distance,
50
+ column_elevation_gain,
51
+ column_hours,
52
+ column_hours_moving,
53
+ column_calories,
54
+ column_steps,
55
+ ]
31
56
 
32
57
  column_speed = ColumnDescription(
33
58
  name="speed",
@@ -35,3 +60,11 @@ column_speed = ColumnDescription(
35
60
  unit="km/h",
36
61
  format=".1f",
37
62
  )
63
+ column_elevation = ColumnDescription(
64
+ name="elevation",
65
+ display_name="Elevation",
66
+ unit="m",
67
+ format=".0f",
68
+ )
69
+
70
+ TIME_SERIES_COLUMNS = [column_speed, column_elevation]
@@ -2,7 +2,7 @@
2
2
  <browserconfig>
3
3
  <msapplication>
4
4
  <tile>
5
- <square150x150logo src="/static/mstile-150x150.png"/>
5
+ <square150x150logo src="/static/favicons/mstile-150x150.png"/>
6
6
  <TileColor>#da532c</TileColor>
7
7
  </tile>
8
8
  </msapplication>
@@ -3,13 +3,13 @@
3
3
  "short_name": "MySite",
4
4
  "icons": [
5
5
  {
6
- "src": "/static/web-app-manifest-192x192.png",
6
+ "src": "/static/favicons/web-app-manifest-192x192.png",
7
7
  "sizes": "192x192",
8
8
  "type": "image/png",
9
9
  "purpose": "maskable"
10
10
  },
11
11
  {
12
- "src": "/static/web-app-manifest-512x512.png",
12
+ "src": "/static/favicons/web-app-manifest-512x512.png",
13
13
  "sizes": "512x512",
14
14
  "type": "image/png",
15
15
  "purpose": "maskable"
@@ -17,7 +17,7 @@ let map = L.map('explorer-map', {
17
17
  zoom: 12
18
18
  });
19
19
 
20
- changeColor('cluster')
20
+ changeColor('default')
21
21
 
22
22
  if (bbox) {
23
23
  map.fitBounds(L.geoJSON(bbox).getBounds())
@@ -52,4 +52,9 @@ map.on('click', e => {
52
52
  .openOn(map);
53
53
  }
54
54
  );
55
- });
55
+ });
56
+
57
+ function downloadAs(suffix) {
58
+ bounds = map.getBounds();
59
+ window.location.href = `/explorer/${zoom}/${bounds.getNorth()}/${bounds.getEast()}/${bounds.getSouth()}/${bounds.getWest()}/${suffix}`
60
+ }
@@ -30,19 +30,19 @@
30
30
 
31
31
  <div class="row mb-3">
32
32
  <div class="col-md-4">
33
- {{ vega_direct("tick_plot", tick_plot) }}
33
+ {{ vega_direct(tick_plot) }}
34
34
  </div>
35
35
  <div class="col-md-4">
36
- {{ vega_direct("equipment_plot", equipment_plot) }}
36
+ {{ vega_direct(equipment_plot) }}
37
37
  </div>
38
38
  </div>
39
39
 
40
40
  <div class="row mb-3">
41
41
  <div class="col-md-4">
42
- {{ vega_direct("distance_plot", distance_plot) }}
42
+ {{ vega_direct(distance_plot) }}
43
43
  </div>
44
44
  <div class="col-md-4">
45
- {{ vega_direct("minutes_plot", minutes_plot) }}
45
+ {{ vega_direct(minutes_plot) }}
46
46
  </div>
47
47
  </div>
48
48
 
@@ -182,13 +182,13 @@
182
182
 
183
183
  <div class="row mb-3">
184
184
  <div class="col-md-4">
185
- {{ vega_direct("distance_time_plot", distance_time_plot) }}
185
+ {{ vega_direct(distance_time_plot) }}
186
186
  </div>
187
187
  <div class="col-md-4">
188
- {{ vega_direct("speed_time_plot", speed_time_plot) }}
188
+ {{ vega_direct(speed_time_plot) }}
189
189
  </div>
190
190
  <div class="col-md-4">
191
- {{ vega_direct("speed_distribution_plot", speed_distribution_plot) }}
191
+ {{ vega_direct(speed_distribution_plot) }}
192
192
  </div>
193
193
  </div>
194
194
 
@@ -201,11 +201,11 @@
201
201
 
202
202
  <div class="row mb-3">
203
203
  <div class="col-md-4">
204
- {{ vega_direct("elevation_time_plot", elevation_time_plot) }}
204
+ {{ vega_direct(elevation_time_plot) }}
205
205
  </div>
206
206
  {% if elevation_gain_cum_plot is defined %}
207
207
  <div class="col-md-4">
208
- {{ vega_direct("elevation_gain_cum_plot", elevation_gain_cum_plot) }}
208
+ {{ vega_direct(elevation_gain_cum_plot) }}
209
209
  </div>
210
210
  {% endif %}
211
211
  </div>
@@ -216,11 +216,11 @@
216
216
 
217
217
  <div class="row mb-3">
218
218
  <div class="col-md-4">
219
- {{ vega_direct("heartrate_time_plot", heartrate_time_plot) }}
219
+ {{ vega_direct(heartrate_time_plot) }}
220
220
  </div>
221
221
  <div class="col-md-4">
222
222
  {% if heart_zones_plot is defined %}
223
- {{ vega_direct("heart_zones_plot", heart_zones_plot) }}
223
+ {{ vega_direct(heart_zones_plot) }}
224
224
  {% else %}
225
225
  <p>Your activity has heart data, but this program doesn't know your maximum heart rate (or birth year) and
226
226
  therefore cannot compute the heart rate zones. Go to the <a
@@ -235,7 +235,7 @@
235
235
 
236
236
  <div class="row mb-3">
237
237
  <div class="col-md-4">
238
- {{ vega_direct("cadence_time_plot", cadence_time_plot) }}
238
+ {{ vega_direct(cadence_time_plot) }}
239
239
  </div>
240
240
  </div>
241
241
  {% endif %}
@@ -54,7 +54,7 @@
54
54
  <p>In a graphical representation, the Eddington number is the distance where the red line intersects with the
55
55
  blue area.</p>
56
56
 
57
- {{ vega_direct("logarithmic_plot", logarithmic_plot) }}
57
+ {{ vega_direct(logarithmic_plot) }}
58
58
  </div>
59
59
  </div>
60
60
 
@@ -63,7 +63,7 @@
63
63
 
64
64
  <p>How did the Eddington number evolve over time?</p>
65
65
 
66
- {{ vega_direct("eddington_number_history_plot", eddington_number_history_plot) }}
66
+ {{ vega_direct(eddington_number_history_plot) }}
67
67
  </div>
68
68
 
69
69
  <div class="mb-3">
@@ -94,7 +94,7 @@
94
94
 
95
95
  <div class="row mb-3">
96
96
  <div class="col-md-8">
97
- {{ vega_direct("eddington_per_week_plot", eddington_per_week_plot) }}
97
+ {{ vega_direct(eddington_per_week_plot) }}
98
98
  </div>
99
99
 
100
100
  <div class="col-md-4">
@@ -76,7 +76,7 @@
76
76
  <p>In a graphical representation, the Eddington number is the elevation gain where the red line intersects with the
77
77
  blue area.</p>
78
78
 
79
- {{ vega_direct("logarithmic_plot", logarithmic_plot) }}
79
+ {{ vega_direct(logarithmic_plot) }}
80
80
  </div>
81
81
  </div>
82
82
 
@@ -85,7 +85,7 @@
85
85
 
86
86
  <p>How did the Eddington number evolve over time?</p>
87
87
 
88
- {{ vega_direct("eddington_number_history_plot", eddington_number_history_plot) }}
88
+ {{ vega_direct(eddington_number_history_plot) }}
89
89
  </div>
90
90
 
91
91
  <div class="mb-3">
@@ -116,7 +116,7 @@
116
116
 
117
117
  <div class="row mb-3">
118
118
  <div class="col-md-8">
119
- {{ vega_direct("eddington_per_week_plot", eddington_per_week_plot) }}
119
+ {{ vega_direct(eddington_per_week_plot) }}
120
120
  </div>
121
121
 
122
122
  <div class="col-md-4">
@@ -80,7 +80,7 @@
80
80
  the
81
81
  blue area.</p>
82
82
 
83
- {{ vega_direct("logarithmic_plot", logarithmic_plot) }}
83
+ {{ vega_direct(logarithmic_plot) }}
84
84
  </div>
85
85
  </div>
86
86
 
@@ -89,7 +89,7 @@
89
89
 
90
90
  <p>How did the Eddington number evolve over time?</p>
91
91
 
92
- {{ vega_direct("eddington_number_history_plot", eddington_number_history_plot) }}
92
+ {{ vega_direct(eddington_number_history_plot) }}
93
93
  </div>
94
94
 
95
95
  <div class="mb-3">
@@ -120,7 +120,7 @@
120
120
 
121
121
  <div class="row mb-3">
122
122
  <div class="col-md-8">
123
- {{ vega_direct("eddington_per_week_plot", eddington_per_week_plot) }}
123
+ {{ vega_direct(eddington_per_week_plot) }}
124
124
  </div>
125
125
 
126
126
  <div class="col-md-4">