geo-activity-playground 1.6.0__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.
@@ -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 start_utc(self) -> Optional[datetime.datetime]:
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["start"].dt.date
316
- df["year"] = df["start"].dt.year
317
- df["month"] = df["start"].dt.month
318
- df["day"] = df["start"].dt.day
319
- df["week"] = df["start"].dt.isocalendar().week
320
- df["day_of_week"] = df["start"].dt.day_of_week
321
- df["iso_year"] = df["start"].dt.isocalendar().year
322
- df["iso_day"] = df["start"].dt.isocalendar().day
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"] = [
@@ -124,9 +124,7 @@ class SearchQuery:
124
124
  )
125
125
 
126
126
 
127
- def apply_search_query(
128
- activity_meta: pd.DataFrame, search_query: SearchQuery
129
- ) -> pd.DataFrame:
127
+ def apply_search_query(search_query: SearchQuery) -> pd.DataFrame:
130
128
 
131
129
  filter_clauses = []
132
130
 
@@ -24,7 +24,7 @@ def test_empty_query() -> None:
24
24
 
25
25
  search_query = SearchQuery()
26
26
 
27
- actual = apply_search_query(activity_meta, 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(activity_meta, 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(activity_meta, 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(activity_meta, search_query)
84
+ actual = apply_search_query(search_query)
85
85
  assert set(actual["id"]) == {1}
86
86
 
87
87
 
@@ -185,10 +185,13 @@ def compute_tile_visits_new(
185
185
 
186
186
  for zoom in reversed(range(20)):
187
187
  tile_state = tile_visit_accessor.tile_state
188
- if not (
189
- tile_state["tile_history"][zoom]["time"].diff().dropna()
190
- >= datetime.timedelta(seconds=0)
191
- ).all():
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
+ ):
192
195
  logger.warning(
193
196
  f"The order of the tile history at {zoom=} is not chronological, resetting."
194
197
  )
@@ -251,12 +254,17 @@ def _process_activity(
251
254
 
252
255
  first_time = tile_visit.get("first_time", None)
253
256
  last_time = tile_visit.get("last_time", None)
254
- if first_time is None or time < first_time:
255
- tile_visit["first_id"] = activity_id
256
- tile_visit["first_time"] = time
257
- if last_time is None or time > last_time:
258
- tile_visit["last_id"] = activity_id
259
- tile_visit["last_time"] = time
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
260
268
 
261
269
  activities_per_tile[tile].add(activity_id)
262
270
 
@@ -273,7 +281,7 @@ def _process_activity(
273
281
  def _tiles_from_points(
274
282
  time_series: pd.DataFrame, zoom: int
275
283
  ) -> Iterator[tuple[datetime.datetime, int, int]]:
276
- # 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.
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.
277
285
  if time_series["time"].dt.tz is None:
278
286
  time_series = time_series.copy()
279
287
  time_series["time"].dt.tz_localize(zoneinfo.ZoneInfo("UTC"))
@@ -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"] = values["enhanced_altitude"]
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,12 +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
- try:
139
- row["speed"] = values["enhanced_speed"] * factor
140
- except TypeError as e:
141
- logger.warning(
142
- f'Cannot work with {values["enhanced_speed"]!r}, {factor!r}'
143
- )
141
+ row["speed"] = (
142
+ _first_of_tuple(values["enhanced_speed"]) * factor
143
+ )
144
144
  if "grade" in fields:
145
145
  row["grade"] = values["grade"]
146
146
  if "temperature" in fields:
@@ -158,7 +158,7 @@ def read_fit_activity(path: pathlib.Path, open) -> tuple[Activity, pd.DataFrame]
158
158
  kind_name += " " + str(values["sub_sport"])
159
159
  activity.kind = get_or_make_kind(kind_name)
160
160
  if "total_calories" in fields:
161
- activity.calories = values["total_calories"]
161
+ activity.calories = int(str(values["total_calories"]))
162
162
  if "total_strides" in fields:
163
163
  activity.steps = 2 * int(values["total_strides"])
164
164
 
@@ -174,6 +174,13 @@ def _fit_speed_unit_factor(unit: str) -> float:
174
174
  raise ActivityParseError(f"Unknown speed unit {unit}")
175
175
 
176
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
+
177
184
  def read_gpx_activity(path: pathlib.Path, open) -> pd.DataFrame:
178
185
  points = []
179
186
  with open(path, "rb") as f:
@@ -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(config, repository, tile_visit_accessor):
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
- get_after = get_state(strava_last_activity_date_path(), "2000-01-01T00:00:00Z")
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
- set_state(
175
- strava_last_activity_date_path(),
176
- strava_activity.start_date.isoformat().replace("+00:00", "Z"),
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:
@@ -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(repository, tile_visit_accessor, config)
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=(app, repository, tile_visit_accessor, config_accessor()),
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(repository.meta, 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(repository.meta, 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(repository.meta, 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(repository.meta, 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(config, repository, tile_visit_accessor)
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: geo-activity-playground
3
- Version: 1.6.0
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
@@ -1,5 +1,5 @@
1
1
  geo_activity_playground/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- geo_activity_playground/__main__.py,sha256=y49XBk0PmAiunx2pZvxLjng1C-AMgAB-f_DljtTCb34,3209
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,11 +21,11 @@ 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=PRqxKlExXxRXkHYIJeNsRr1DZQmdzAwa3PLyivJoix8,15983
24
+ geo_activity_playground/core/datamodel.py,sha256=DVKgvS8nIuvGBUiXGJbNci3xdY1TxXrFGhsYin86E5Q,16730
25
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=nyvCuR7v0pd6KjA8W5Kr71bBafRdE_ol7uSFRJs4eAM,6662
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
@@ -36,7 +36,7 @@ geo_activity_playground/core/similarity.py,sha256=L2de3DPRdDeDY5AxZwLDcH7FjHWRWk
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=zhuD343Xce-4Fkznw81DHQ7pK5eyX5UbcyCHuYRKsr8,3091
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
@@ -48,42 +48,42 @@ geo_activity_playground/core/tiles.py,sha256=LBn2V6WAvMxZeXSIQ8ruZL71iyvOXoFZMz7
48
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=7B8c9xf7jJFVjXD3SNEgoQb707dHFa4JN0hUdn5z-xQ,16594
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=MJIsBi2SH5y8Q5D-w9djB4ZFnKxCsoNx5qpynqrubhw,11311
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
57
  geo_activity_playground/importers/directory.py,sha256=sHcLIi0DOPEATt4wiE_vp00h89fO940wUQyJZUzzpnw,3978
58
- geo_activity_playground/importers/strava_api.py,sha256=Fiqlc-VeuzsvgDcWt71JoPMri221cMjkeL4SH80gC5s,8426
58
+ geo_activity_playground/importers/strava_api.py,sha256=0lUmC6u829vJpaIs7plQq4mOPgc7yQI65vN9qhAEqVs,9015
59
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=F1gHR2BtiMizwACxFrR_Egm2cNL_cAhWEEToP5H9EEg,10695
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=Ya5GJxfVESwmRlgMTYe9g75g8JHHTAAvYFmSD-3Uz4Q,8987
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=9CfcXE7v-p6IDR4L6ccBmSXlt89xTwitYetSF8xxI9g,3138
77
- geo_activity_playground/webui/blueprints/heatmap_blueprint.py,sha256=5LlYKMeOMIE7c3xGRZ52ld4Jxtdc3GNcb6lvt3v7NVA,8435
78
- geo_activity_playground/webui/blueprints/photo_blueprint.py,sha256=ql8gfJ-HYgy99PXPHGdbF_sp5wHsxjePQjWJipfpw1A,7250
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=Sv_KL1Cdai26y51qVfI-5jZLhtElREsEar1dbR_VAC4,2275
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=ZF9Mocewddpn09pElSqrVS6jkqajhEJMn-7KbIvZodY,6751
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=K7bWCN9kotklbkrjPR-IV08p9O2aekzG_vuksduSJB4,4609
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
@@ -175,8 +175,8 @@ geo_activity_playground/webui/templates/summary/vega-chart.html.j2,sha256=mw8Hti
175
175
  geo_activity_playground/webui/templates/time_zone_fixer/index.html.j2,sha256=s9r6BJMXmd7kLSyjkvH4xLi6e01S5bpGRcMgMMJyCAE,1760
176
176
  geo_activity_playground/webui/templates/upload/index.html.j2,sha256=I1Ix8tDS3YBdi-HdaNfjkzYXVVCjfUTe5PFTnap1ydc,775
177
177
  geo_activity_playground/webui/templates/upload/reload.html.j2,sha256=YZWX5eDeNyqKJdQAywDBcU8DZBm22rRBbZqFjrFrCvQ,556
178
- geo_activity_playground-1.6.0.dist-info/LICENSE,sha256=4RpAwKO8bPkfXH2lnpeUW0eLkNWglyG4lbrLDU_MOwY,1070
179
- geo_activity_playground-1.6.0.dist-info/METADATA,sha256=gj1svTD4GNI5A8Y8-ZXacM2oDOdvtY7Gk8SM3aENH2A,1937
180
- geo_activity_playground-1.6.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
181
- geo_activity_playground-1.6.0.dist-info/entry_points.txt,sha256=pbNlLI6IIZIp7nPYCfAtiSiz2oxJSCl7DODD6SPkLKk,81
182
- geo_activity_playground-1.6.0.dist-info/RECORD,,
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,,