geo-activity-playground 0.41.0__py3-none-any.whl → 0.42.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.
@@ -2,6 +2,7 @@ import datetime
2
2
  import functools
3
3
  import logging
4
4
  from collections.abc import Callable
5
+ from collections.abc import Sequence
5
6
  from typing import Any
6
7
  from typing import Optional
7
8
 
@@ -21,17 +22,6 @@ from geo_activity_playground.core.datamodel import query_activity_meta
21
22
  logger = logging.getLogger(__name__)
22
23
 
23
24
 
24
- def make_activity_meta() -> ActivityMeta:
25
- return ActivityMeta(
26
- calories=None,
27
- commute=False,
28
- consider_for_achievements=True,
29
- equipment="Unknown",
30
- kind="Unknown",
31
- steps=None,
32
- )
33
-
34
-
35
25
  class ActivityRepository:
36
26
  def __len__(self) -> int:
37
27
  return len(self.get_activity_ids())
@@ -52,14 +42,14 @@ class ActivityRepository:
52
42
  else:
53
43
  return None
54
44
 
55
- def get_activity_ids(self, only_achievements: bool = False) -> list[int]:
45
+ def get_activity_ids(self, only_achievements: bool = False) -> Sequence[int]:
56
46
  query = sqlalchemy.select(Activity.id)
57
47
  if only_achievements:
58
48
  query = query.where(Kind.consider_for_achievements)
59
- result = DB.session.scalars(query).all()
49
+ result = DB.session.scalars(query.order_by(Activity.start)).all()
60
50
  return result
61
51
 
62
- def iter_activities(self, new_to_old=True, drop_na=False) -> list[Activity]:
52
+ def iter_activities(self, new_to_old=True, drop_na=False) -> Sequence[Activity]:
63
53
  query = sqlalchemy.select(Activity)
64
54
  if drop_na:
65
55
  query = query.where(Activity.start.is_not(None))
@@ -117,9 +117,6 @@ class Activity(DB.Model):
117
117
  secondary=activity_tag_association_table, back_populates="activities"
118
118
  )
119
119
 
120
- def __getitem__(self, item) -> Any:
121
- return self.to_dict()[item]
122
-
123
120
  def __str__(self) -> str:
124
121
  return f"{self.start} {self.name}"
125
122
 
@@ -127,11 +124,15 @@ class Activity(DB.Model):
127
124
  def average_speed_moving_kmh(self) -> Optional[float]:
128
125
  if self.moving_time:
129
126
  return self.distance_km / (self.moving_time.total_seconds() / 3_600)
127
+ else:
128
+ return None
130
129
 
131
130
  @property
132
131
  def average_speed_elapsed_kmh(self) -> Optional[float]:
133
132
  if self.elapsed_time:
134
133
  return self.distance_km / (self.elapsed_time.total_seconds() / 3_600)
134
+ else:
135
+ return None
135
136
 
136
137
  @property
137
138
  def raw_time_series(self) -> pd.DataFrame:
@@ -154,38 +155,6 @@ class Activity(DB.Model):
154
155
  else:
155
156
  return self.raw_time_series
156
157
 
157
- def to_dict(self) -> ActivityMeta:
158
- equipment = self.equipment.name if self.equipment is not None else "Unknown"
159
- kind = self.kind.name if self.kind is not None else "Unknown"
160
- consider_for_achievements = (
161
- self.kind.consider_for_achievements if self.kind is not None else True
162
- )
163
- return ActivityMeta(
164
- id=self.id,
165
- name=self.name,
166
- path=self.path,
167
- distance_km=self.distance_km,
168
- start=self.start,
169
- elapsed_time=self.elapsed_time,
170
- moving_time=self.moving_time,
171
- start_latitude=self.start_latitude,
172
- start_longitude=self.start_longitude,
173
- end_latitude=self.end_latitude,
174
- end_longitude=self.end_longitude,
175
- elevation_gain=self.elevation_gain,
176
- start_elevation=self.start_elevation,
177
- end_elevation=self.end_elevation,
178
- calories=self.calories,
179
- steps=self.steps,
180
- num_new_tiles_14=self.num_new_tiles_14,
181
- num_new_tiles_17=self.num_new_tiles_17,
182
- equipment=equipment,
183
- kind=kind,
184
- average_speed_moving_kmh=self.average_speed_moving_kmh,
185
- average_speed_elapsed_kmh=self.average_speed_elapsed_kmh,
186
- consider_for_achievements=consider_for_achievements,
187
- )
188
-
189
158
 
190
159
  class Tag(DB.Model):
191
160
  __tablename__ = "tags"
@@ -199,6 +168,17 @@ class Tag(DB.Model):
199
168
  )
200
169
 
201
170
 
171
+ def get_or_make_tag(tag: str) -> Tag:
172
+ tags = DB.session.scalars(sqlalchemy.select(Tag).where(Tag.tag == tag)).all()
173
+ if tags:
174
+ assert len(tags) == 1, f"There must be only one tag with name '{tag}'."
175
+ return tags[0]
176
+ else:
177
+ tag = Tag(tag=tag)
178
+ DB.session.add(tag)
179
+ return tag
180
+
181
+
202
182
  def query_activity_meta() -> pd.DataFrame:
203
183
  rows = DB.session.execute(
204
184
  sqlalchemy.select(
@@ -276,6 +256,23 @@ class Equipment(DB.Model):
276
256
  __table_args__ = (sa.UniqueConstraint("name", name="equipments_name"),)
277
257
 
278
258
 
259
+ def get_or_make_equipment(name: str, config: Config) -> Equipment:
260
+ equipments = DB.session.scalars(
261
+ sqlalchemy.select(Equipment).where(Equipment.name == name)
262
+ ).all()
263
+ if equipments:
264
+ assert (
265
+ len(equipments) == 1
266
+ ), f"There must be only one equipment with name '{name}'."
267
+ return equipments[0]
268
+ else:
269
+ equipment = Equipment(
270
+ name=name, offset_km=config.equipment_offsets.get(name, 0)
271
+ )
272
+ DB.session.add(equipment)
273
+ return equipment
274
+
275
+
279
276
  class Kind(DB.Model):
280
277
  __tablename__ = "kinds"
281
278
 
@@ -299,20 +296,6 @@ class Kind(DB.Model):
299
296
  __table_args__ = (sa.UniqueConstraint("name", name="kinds_name"),)
300
297
 
301
298
 
302
- class SquarePlannerBookmark(DB.Model):
303
- __tablename__ = "square_planner_bookmarks"
304
-
305
- id: Mapped[int] = mapped_column(primary_key=True)
306
-
307
- zoom: Mapped[int] = mapped_column(sa.Integer, nullable=False)
308
- x: Mapped[int] = mapped_column(sa.Integer, nullable=False)
309
- y: Mapped[int] = mapped_column(sa.Integer, nullable=False)
310
- size: Mapped[int] = mapped_column(sa.Integer, nullable=False)
311
- name: Mapped[str] = mapped_column(sa.String, nullable=False)
312
-
313
- __table_args__ = (sa.UniqueConstraint("zoom", "x", "y", "size", name="kinds_name"),)
314
-
315
-
316
299
  def get_or_make_kind(name: str, config: Config) -> Kind:
317
300
  kinds = DB.session.scalars(sqlalchemy.select(Kind).where(Kind.name == name)).all()
318
301
  if kinds:
@@ -327,21 +310,18 @@ def get_or_make_kind(name: str, config: Config) -> Kind:
327
310
  return kind
328
311
 
329
312
 
330
- def get_or_make_equipment(name: str, config: Config) -> Equipment:
331
- equipments = DB.session.scalars(
332
- sqlalchemy.select(Equipment).where(Equipment.name == name)
333
- ).all()
334
- if equipments:
335
- assert (
336
- len(equipments) == 1
337
- ), f"There must be only one equipment with name '{name}'."
338
- return equipments[0]
339
- else:
340
- equipment = Equipment(
341
- name=name, offset_km=config.equipment_offsets.get(name, 0)
342
- )
343
- DB.session.add(equipment)
344
- return equipment
313
+ class SquarePlannerBookmark(DB.Model):
314
+ __tablename__ = "square_planner_bookmarks"
315
+
316
+ id: Mapped[int] = mapped_column(primary_key=True)
317
+
318
+ zoom: Mapped[int] = mapped_column(sa.Integer, nullable=False)
319
+ x: Mapped[int] = mapped_column(sa.Integer, nullable=False)
320
+ y: Mapped[int] = mapped_column(sa.Integer, nullable=False)
321
+ size: Mapped[int] = mapped_column(sa.Integer, nullable=False)
322
+ name: Mapped[str] = mapped_column(sa.String, nullable=False)
323
+
324
+ __table_args__ = (sa.UniqueConstraint("zoom", "x", "y", "size", name="kinds_name"),)
345
325
 
346
326
 
347
327
  class PlotSpec(DB.Model):
@@ -15,6 +15,7 @@ from .datamodel import ActivityMeta
15
15
  from .datamodel import DB
16
16
  from .datamodel import get_or_make_equipment
17
17
  from .datamodel import get_or_make_kind
18
+ from .missing_values import some
18
19
  from .paths import activity_extracted_meta_dir
19
20
  from .paths import activity_extracted_time_series_dir
20
21
  from .paths import time_series_dir
@@ -82,9 +83,9 @@ def populate_database_from_extracted(config: Config) -> None:
82
83
  distance_km=0,
83
84
  equipment=equipment,
84
85
  kind=kind,
85
- calories=extracted_metadata.get("calories", None),
86
- elevation_gain=extracted_metadata.get("elevation_gain", None),
87
- steps=extracted_metadata.get("steps", None),
86
+ calories=some(extracted_metadata.get("calories", None)),
87
+ elevation_gain=some(extracted_metadata.get("elevation_gain", None)),
88
+ steps=some(extracted_metadata.get("steps", None)),
88
89
  path=extracted_metadata.get("path", None),
89
90
  upstream_id=upstream_id,
90
91
  )
@@ -92,7 +93,13 @@ def populate_database_from_extracted(config: Config) -> None:
92
93
  update_via_time_series(activity, time_series)
93
94
 
94
95
  DB.session.add(activity)
95
- DB.session.commit()
96
+ try:
97
+ DB.session.commit()
98
+ except sqlalchemy.exc.StatementError:
99
+ logger.error(
100
+ f"Could not insert the following activity into the database: {vars(activity)=}"
101
+ )
102
+ raise
96
103
 
97
104
  enriched_time_series_path = time_series_dir() / f"{activity.id}.parquet"
98
105
  time_series.to_parquet(enriched_time_series_path)
@@ -0,0 +1,13 @@
1
+ from typing import Optional
2
+ from typing import Union
3
+
4
+ import numpy as np
5
+
6
+
7
+ def some(value) -> Optional[Union[float, int]]:
8
+ if value is None:
9
+ return None
10
+ elif np.isnan(value):
11
+ return None
12
+ else:
13
+ return value
@@ -0,0 +1,19 @@
1
+ import numpy as np
2
+
3
+ from .missing_values import some
4
+
5
+
6
+ def test_none() -> None:
7
+ assert some(None) == None
8
+
9
+
10
+ def test_nan() -> None:
11
+ assert some(np.nan) == None
12
+
13
+
14
+ def test_float() -> None:
15
+ assert some(1.0) == 1.0
16
+
17
+
18
+ def test_integer() -> None:
19
+ assert some(1) == 1
@@ -176,7 +176,7 @@ def _process_activity(
176
176
  activity_tiles["time"],
177
177
  zip(activity_tiles["tile_x"], activity_tiles["tile_y"]),
178
178
  ):
179
- if activity["consider_for_achievements"]:
179
+ if activity.kind.consider_for_achievements:
180
180
  if tile not in tile_state["tile_visits"][zoom]:
181
181
  new_tile_history_soa["activity_id"].append(activity_id)
182
182
  new_tile_history_soa["time"].append(time)
@@ -70,7 +70,7 @@ def web_ui_main(
70
70
 
71
71
  app = Flask(__name__)
72
72
 
73
- database_path = basedir / "database.sqlite"
73
+ database_path = pathlib.Path("database.sqlite")
74
74
  logger.info(f"Using database file at '{database_path.absolute()}'.")
75
75
  app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{database_path.absolute()}"
76
76
  app.config["ALEMBIC"] = {"script_location": "../alembic/versions"}
@@ -29,6 +29,7 @@ from ...core.datamodel import Activity
29
29
  from ...core.datamodel import DB
30
30
  from ...core.datamodel import Equipment
31
31
  from ...core.datamodel import Kind
32
+ from ...core.datamodel import Tag
32
33
  from ...core.enrichment import update_via_time_series
33
34
  from ...core.heart_rate import HeartRateZoneComputer
34
35
  from ...core.privacy_zones import PrivacyZone
@@ -71,14 +72,14 @@ def make_activity_blueprint(
71
72
  )
72
73
  ]
73
74
  for _, group in repository.get_time_series(
74
- activity["id"]
75
+ activity.id
75
76
  ).groupby("segment_id")
76
77
  ]
77
78
  ),
78
79
  properties={
79
80
  "color": matplotlib.colors.to_hex(cmap(i % 8)),
80
- "activity_name": activity["name"],
81
- "activity_id": str(activity["id"]),
81
+ "activity_name": activity.name,
82
+ "activity_id": str(activity.id),
82
83
  },
83
84
  )
84
85
  for i, activity in enumerate(repository.iter_activities())
@@ -99,7 +100,7 @@ def make_activity_blueprint(
99
100
 
100
101
  meta = repository.meta
101
102
  similar_activities = meta.loc[
102
- (meta.name == activity["name"]) & (meta.id != activity["id"])
103
+ (meta.name == activity.name) & (meta.id != activity.id)
103
104
  ]
104
105
  similar_activities = [row for _, row in similar_activities.iterrows()]
105
106
  similar_activities.reverse()
@@ -107,7 +108,7 @@ def make_activity_blueprint(
107
108
  new_tiles = {
108
109
  zoom: sum(
109
110
  tile_visit_accessor.tile_state["tile_history"][zoom]["activity_id"]
110
- == activity["id"]
111
+ == activity.id
111
112
  )
112
113
  for zoom in sorted(config.explorer_zoom_levels)
113
114
  }
@@ -117,7 +118,7 @@ def make_activity_blueprint(
117
118
  for zoom in sorted(config.explorer_zoom_levels):
118
119
  new_tiles = tile_visit_accessor.tile_state["tile_history"][zoom].loc[
119
120
  tile_visit_accessor.tile_state["tile_history"][zoom]["activity_id"]
120
- == activity["id"]
121
+ == activity.id
121
122
  ]
122
123
  if len(new_tiles):
123
124
  points = make_grid_points(
@@ -152,8 +153,8 @@ def make_activity_blueprint(
152
153
  time_series[line_color_column],
153
154
  line_color_columns_avail[line_color_column].format,
154
155
  ),
155
- "date": activity["start"].date(),
156
- "time": activity["start"].time(),
156
+ "date": activity.start.date(),
157
+ "time": activity.start.time(),
157
158
  "new_tiles": new_tiles_per_zoom,
158
159
  "new_tiles_geojson": new_tiles_geojson,
159
160
  "line_color_column": line_color_column,
@@ -318,6 +319,7 @@ def make_activity_blueprint(
318
319
  abort(404)
319
320
  equipments = DB.session.scalars(sqlalchemy.select(Equipment)).all()
320
321
  kinds = DB.session.scalars(sqlalchemy.select(Kind)).all()
322
+ tags = DB.session.scalars(sqlalchemy.select(Tag)).all()
321
323
 
322
324
  if request.method == "POST":
323
325
  activity.name = request.form.get("name")
@@ -326,13 +328,18 @@ def make_activity_blueprint(
326
328
  if form_equipment == "null":
327
329
  activity.equipment = None
328
330
  else:
329
- activity.equipment = DB.session.get(Equipment, int(form_equipment))
331
+ activity.equipment = DB.session.get_one(Equipment, int(form_equipment))
330
332
 
331
333
  form_kind = request.form.get("kind")
332
334
  if form_kind == "null":
333
335
  activity.kind = None
334
336
  else:
335
- activity.kind = DB.session.get(Kind, int(form_kind))
337
+ activity.kind = DB.session.get_one(Kind, int(form_kind))
338
+
339
+ form_tags = request.form.getlist("tag")
340
+ activity.tags = [
341
+ DB.session.get_one(Tag, int(tag_id_str)) for tag_id_str in form_tags
342
+ ]
336
343
 
337
344
  DB.session.commit()
338
345
  return redirect(url_for(".show", id=activity.id))
@@ -342,6 +349,7 @@ def make_activity_blueprint(
342
349
  activity=activity,
343
350
  kinds=kinds,
344
351
  equipments=equipments,
352
+ tags=tags,
345
353
  )
346
354
 
347
355
  @blueprint.route("/trim/<id>", methods=["GET", "POST"])
@@ -18,6 +18,7 @@ from ...core.config import ConfigAccessor
18
18
  from ...core.datamodel import DB
19
19
  from ...core.datamodel import Equipment
20
20
  from ...core.datamodel import Kind
21
+ from ...core.datamodel import Tag
21
22
  from ...core.heart_rate import HeartRateZoneComputer
22
23
  from ...core.paths import _activity_enriched_dir
23
24
  from ..authenticator import Authenticator
@@ -395,6 +396,37 @@ def make_settings_blueprint(
395
396
  strava_login_helper.save_strava_code(code)
396
397
  return redirect(url_for(".strava"))
397
398
 
399
+ @blueprint.route("/tags")
400
+ @needs_authentication(authenticator)
401
+ def tags_list():
402
+ return render_template(
403
+ "settings/tags-list.html.j2",
404
+ tags=DB.session.scalars(sqlalchemy.select(Tag)).all(),
405
+ )
406
+
407
+ @blueprint.route("/tags/new", methods=["GET", "POST"])
408
+ @needs_authentication(authenticator)
409
+ def tags_new():
410
+ if request.method == "POST":
411
+ tag_str = request.form["tag"]
412
+ tag = Tag(tag=tag_str)
413
+ DB.session.add(tag)
414
+ DB.session.commit()
415
+ return redirect(url_for(".tags_list"))
416
+ else:
417
+ return render_template("settings/tags-new.html.j2")
418
+
419
+ @blueprint.route("/tags/edit/<int:id>", methods=["GET", "POST"])
420
+ @needs_authentication(authenticator)
421
+ def tags_edit(id: int):
422
+ tag = DB.session.get_one(Tag, id)
423
+ if request.method == "POST":
424
+ tag.tag = request.form["tag"]
425
+ DB.session.commit()
426
+ return redirect(url_for(".tags_list"))
427
+ else:
428
+ return render_template("settings/tags-edit.html.j2", tag=tag)
429
+
398
430
  return blueprint
399
431
 
400
432
 
@@ -32,6 +32,21 @@
32
32
  </select>
33
33
  </div>
34
34
 
35
+ <div class="mb-3">
36
+ <label for="" class="form-label">Tags</label>
37
+ <div class="form-control">
38
+ {% for tag in tags %}
39
+ <div class="form-check form-check-inline">
40
+ <input class="form-check-input" type="checkbox" name="tag" value="{{ tag.id }}" id="tag_{{ tag.id }}" {%
41
+ if tag in activity.tags %} checked {% endif %}>
42
+ <label class="form-check-label" for="tag_{{ tag.id }}">
43
+ {{ tag.tag }}
44
+ </label>
45
+ </div>
46
+ {% endfor %}
47
+ </div>
48
+ </div>
49
+
35
50
  <button type="submit" class="btn btn-primary">Save</button>
36
51
  </form>
37
52
 
@@ -11,13 +11,22 @@
11
11
  <div class="col-sm-12 col-md-4">
12
12
  <dl>
13
13
  <dt>Name</dt>
14
- <dd>{{ activity["name"] }}</dd>
14
+ <dd>{{ activity.name }}</dd>
15
15
 
16
16
  {% if activity.kind %}
17
17
  <dt>Kind</dt>
18
18
  <dd>{{ activity.kind.name }}</dd>
19
19
  {% endif %}
20
20
 
21
+ {% if activity.tags %}
22
+ <dt>Tags</dt>
23
+ <dd>
24
+ {% for tag in activity.tags %}
25
+ <span class="badge text-bg-primary">{{ tag.tag }}</span>
26
+ {% endfor %}
27
+ </dd>
28
+ {% endif %}
29
+
21
30
  <dt>Distance</dt>
22
31
  <dd>{{ activity.distance_km|round(1) }} km</dd>
23
32
 
@@ -114,9 +123,11 @@
114
123
  <div class="mb-3" style="padding-top: 10px;">
115
124
  <form method="GET">
116
125
  <label class="form-label">Line Color by</label>
117
- <select class="form-select" aria-label="Line Color by" name="line_color_column" onchange="this.form.submit()">
126
+ <select class="form-select" aria-label="Line Color by" name="line_color_column"
127
+ onchange="this.form.submit()">
118
128
  {% for name, column in line_color_columns_avail.items() %}
119
- <option {% if name == line_color_column %} selected {% endif %} value="{{ name }}">{{ column.display_name }}</option>
129
+ <option {% if name==line_color_column %} selected {% endif %} value="{{ name }}">{{
130
+ column.display_name }}</option>
120
131
  {% endfor %}
121
132
  </select>
122
133
  </form>
@@ -103,6 +103,15 @@
103
103
  </div>
104
104
  </div>
105
105
  </div>
106
+ <div class="col">
107
+ <div class="card">
108
+ <div class="card-body">
109
+ <h5 class="card-title">Tags</h5>
110
+ <p class="card-text">You can assign tags to activities. Create and manage them here.</p>
111
+ <a href="{{ url_for('.tags_list') }}" class="btn btn-primary">Manage tags</a>
112
+ </div>
113
+ </div>
114
+ </div>
106
115
  </div>
107
116
  </div>
108
117
  {% endblock %}
@@ -0,0 +1,17 @@
1
+ {% extends "page.html.j2" %}
2
+
3
+ {% block container %}
4
+
5
+ <h1 class="mb-3">Edit Tag</h1>
6
+
7
+ <form method="POST">
8
+ <div class="mb-3">
9
+ <label for="tag" class="form-label">Tag</label>
10
+ <input type="text" class="form-control" id="tag" name="tag" value="{{ tag.tag }}" />
11
+ </div>
12
+
13
+ <button type="submit" class="btn btn-primary">Save</button>
14
+ </form>
15
+
16
+
17
+ {% endblock %}
@@ -0,0 +1,19 @@
1
+ {% extends "page.html.j2" %}
2
+
3
+ {% block container %}
4
+
5
+ <h1 class="mb-3">Tags</h1>
6
+
7
+ <ul>
8
+ {% for tag in tags %}
9
+ <li>
10
+ <b>{{ tag.tag }}</b>
11
+ <a class="btn btn-sm btn-primary" href="{{ url_for('.tags_edit', id=tag.id) }}">Edit</a>
12
+ </li>
13
+ {% endfor %}
14
+ </ul>
15
+
16
+
17
+ <a class="btn btn-primary" href="{{ url_for('.tags_new') }}">New</a>
18
+
19
+ {% endblock %}
@@ -0,0 +1,17 @@
1
+ {% extends "page.html.j2" %}
2
+
3
+ {% block container %}
4
+
5
+ <h1 class="mb-3">Create Tag</h1>
6
+
7
+ <form method="POST">
8
+ <div class="mb-3">
9
+ <label for="tag" class="form-label">Tag</label>
10
+ <input type="text" class="form-control" id="tag" name="tag" />
11
+ </div>
12
+
13
+ <button type="submit" class="btn btn-primary">Save</button>
14
+ </form>
15
+
16
+
17
+ {% endblock %}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: geo-activity-playground
3
- Version: 0.41.0
3
+ Version: 0.42.0
4
4
  Summary: Analysis of geo data activities like rides, runs or hikes.
5
5
  License: MIT
6
6
  Author: Martin Ueding
@@ -11,6 +11,7 @@ Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.10
12
12
  Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
14
15
  Requires-Dist: Pillow (>=11.0.0,<12.0.0)
15
16
  Requires-Dist: alembic (>=1.15.2,<2.0.0)
16
17
  Requires-Dist: altair (>=5.5.0,<6.0.0)
@@ -12,13 +12,14 @@ geo_activity_playground/alembic/versions/b03491c593f6_add_crop_indices.py,sha256
12
12
  geo_activity_playground/alembic/versions/e02e27876deb_add_square_planner_bookmark_name.py,sha256=Y0OMxp5z_-CQ83rww6GEBFRawXu0J0pLrLArgSjJ7wQ,866
13
13
  geo_activity_playground/alembic/versions/script.py.mako,sha256=JADdI7OljvzEw8JTwKoIw589EZWmKBjtqdRsjnpNd_c,578
14
14
  geo_activity_playground/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- geo_activity_playground/core/activities.py,sha256=0dZk_Xa033FwRVXotcac4BxyW0pLNQAymYiWUkVDIYI,4761
15
+ geo_activity_playground/core/activities.py,sha256=R3S5sLsT3Eot2P7TcR2pd35YrkICMjjV38I2v6ewzqE,4597
16
16
  geo_activity_playground/core/config.py,sha256=eGWWbNfHa6H64AHCnFYTsAJ7-pWi-PhyxL4hjZ4u03U,5256
17
17
  geo_activity_playground/core/coordinates.py,sha256=tDfr9mlXhK6E_MMIJ0vYWVCoH0Lq8uyuaqUgaa8i0jg,966
18
- geo_activity_playground/core/datamodel.py,sha256=tOGo5PqcKZ8g3EAqh4vsj8NGBm4WXqBi5ChyqZbmaik,12866
19
- geo_activity_playground/core/enrichment.py,sha256=kc9747ocSs2_3R7oW9Rjs3_lKP37gdvBUbyWpILaqHc,7346
18
+ geo_activity_playground/core/datamodel.py,sha256=IAjgTwUv-kfP1reep3YMyCIuo74dZjrvZt3ZQK6p19g,11823
19
+ geo_activity_playground/core/enrichment.py,sha256=RYfnBZgPsgdwuIv1byMo5Hh3JJjoFiG6CDugsE9WVKk,7614
20
20
  geo_activity_playground/core/heart_rate.py,sha256=-S3WAhS7AOywrw_Lk5jfuo_fu6zvZQ1VtjwEKSycWpU,1542
21
21
  geo_activity_playground/core/meta_search.py,sha256=naErjAC7ZCFhOF6d492kbegZxCdzbpGcJvjQLJTE4xE,5016
22
+ geo_activity_playground/core/missing_values.py,sha256=ByS7EVynohXga9IRlPOlyS8TY2qQ7bve9nJlA7za0KI,242
22
23
  geo_activity_playground/core/parametric_plot.py,sha256=IefPc6lwthxowvjUDA5wu23oBSw9jq399l04gSaNrOQ,3880
23
24
  geo_activity_playground/core/paths.py,sha256=aUXGuNn9hBvGPQWPoUJeImHN0PB0fS1tja1tm2eq8mA,2595
24
25
  geo_activity_playground/core/privacy_zones.py,sha256=4TumHsVUN1uW6RG3ArqTXDykPVipF98DCxVBe7YNdO8,512
@@ -28,6 +29,7 @@ geo_activity_playground/core/summary_stats.py,sha256=v5FtWnE1imDF5axI6asVN55wCrl
28
29
  geo_activity_playground/core/tasks.py,sha256=-_9cxekoHSWzCW4XblNeqrwi2tTqr5AE7_-p8fdqhwc,2886
29
30
  geo_activity_playground/core/test_datamodel.py,sha256=-VrGHgx5Z3MSQPqHGmmm7atRJYbg5y_ukvRHKxk22PI,569
30
31
  geo_activity_playground/core/test_meta_search.py,sha256=zhuD343Xce-4Fkznw81DHQ7pK5eyX5UbcyCHuYRKsr8,3091
32
+ geo_activity_playground/core/test_missing_values.py,sha256=zlyrKHLCzUSBdktiy87oKz7_bySpfohVPxG1HRGRHqw,278
31
33
  geo_activity_playground/core/test_summary_stats.py,sha256=qH_45mPRFD2H-Rr0Ku-RYc67vhC7qKxbPr7J2F36uV8,3081
32
34
  geo_activity_playground/core/test_tiles.py,sha256=zce1FxNfsSpOQt66jMehdQRVoNdl-oiFydx6iVBHZXM,764
33
35
  geo_activity_playground/core/test_time_conversion.py,sha256=Sh6nZA3uCTOdZTZa3yOijtR0m74QtZu2mcWXsDNnyQI,984
@@ -35,7 +37,7 @@ geo_activity_playground/core/tiles.py,sha256=lV6X1Uc9XQecu2LALIvxpnMcLsVtWx7JczJ
35
37
  geo_activity_playground/core/time_conversion.py,sha256=x5mXG6Y4GtdX7CBmwucGNSWBp9JQJDbZ7u0JkdUY1Vs,379
36
38
  geo_activity_playground/explorer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
39
  geo_activity_playground/explorer/grid_file.py,sha256=YNL_c4O1-kxaajATJwj4ZLywCL5Hpj9qy2h-F7rk8Yg,3260
38
- geo_activity_playground/explorer/tile_visits.py,sha256=C8IpAGmrjMGYhyTVK-tl2ptM9-CXF2mwibhJYn7gLf8,13905
40
+ geo_activity_playground/explorer/tile_visits.py,sha256=ESHI67xV53gmhMXAFzoPR0Uz7lc8T2gViyvjckrBo90,13907
39
41
  geo_activity_playground/explorer/video.py,sha256=7j6Qv3HG6On7Tn7xh7Olwrx_fbQnfzS7CeRg3TEApHg,4397
40
42
  geo_activity_playground/heatmap_video.py,sha256=I8i1uVvbbPUXVtvLAROaLy58nQoUPnuMCZkERWNkQjg,3318
41
43
  geo_activity_playground/importers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -48,10 +50,10 @@ geo_activity_playground/importers/test_csv_parser.py,sha256=nOTVTdlzIY0TDcbWp7xN
48
50
  geo_activity_playground/importers/test_directory.py,sha256=_fn_-y98ZyElbG0BRxAmGFdtGobUShPU86SdEOpuv-A,691
49
51
  geo_activity_playground/importers/test_strava_api.py,sha256=7b8bl5Rh2BctCmvTPEhCadxtUOq3mfzuadD6F5XxRio,398
50
52
  geo_activity_playground/webui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
- geo_activity_playground/webui/app.py,sha256=BOaYo3a8U53w7zgWHLDxiYxrWqHQyW5piGSNoMtW7lc,7277
53
+ geo_activity_playground/webui/app.py,sha256=fUbKRpQh558D19cPUOHScSG-Cur61B41rVDsAtxQt-4,7281
52
54
  geo_activity_playground/webui/authenticator.py,sha256=jtQqvpVHa_eLTAulmvvJgDRoCWOEege49G9zn3MfYk8,1394
53
55
  geo_activity_playground/webui/blueprints/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
- geo_activity_playground/webui/blueprints/activity_blueprint.py,sha256=lUCoL5meZy2ovvTjLeam0nhTzNVrRpjKHrjW-vnlHcg,24974
56
+ geo_activity_playground/webui/blueprints/activity_blueprint.py,sha256=NDFw5v765uGpaX2kMIH09EALsZmYYLSJRkrn7bq_Wvo,25258
55
57
  geo_activity_playground/webui/blueprints/auth_blueprint.py,sha256=_VZeP3VN626BoOOZUkNVnuw9v-cEOrkHz5lhFPmxqMY,784
56
58
  geo_activity_playground/webui/blueprints/bubble_chart_blueprint.py,sha256=xESHzYxlbhz4oNDuxV0A70eVKpFwz84pYC3q_YVZZg8,2812
57
59
  geo_activity_playground/webui/blueprints/calendar_blueprint.py,sha256=4EIBZ8rdXEu3tbl1faVlRwHb8Qp0JuMc3eyxwMkq6g8,2848
@@ -62,7 +64,7 @@ geo_activity_playground/webui/blueprints/explorer_blueprint.py,sha256=6w6seOCVf7
62
64
  geo_activity_playground/webui/blueprints/heatmap_blueprint.py,sha256=iHI5YJYhX7ZOlzTgzl2efIRDzt3UMYCx7X4-LVd0MWk,8702
63
65
  geo_activity_playground/webui/blueprints/plot_builder_blueprint.py,sha256=7HrjpBM-608HSOh0i31Lmt7yDNMfWlEn6G7DlYqlV9w,3031
64
66
  geo_activity_playground/webui/blueprints/search_blueprint.py,sha256=Sv_KL1Cdai26y51qVfI-5jZLhtElREsEar1dbR_VAC4,2275
65
- geo_activity_playground/webui/blueprints/settings_blueprint.py,sha256=UUv63BDQFnBPq8fLDdlWHd5mxL5qIgcGUuqQRFemyEA,16108
67
+ geo_activity_playground/webui/blueprints/settings_blueprint.py,sha256=p_17BC2ZQyPu5VSl7evc5YyQmB3-L-WxgzOTv9UJsK0,17245
66
68
  geo_activity_playground/webui/blueprints/square_planner_blueprint.py,sha256=xVaxJxmt8Dysl3UL9f2y__LVLtTH2Np1Ust4OSXKRAk,4746
67
69
  geo_activity_playground/webui/blueprints/summary_blueprint.py,sha256=rK4LGR2Rpioy4wSqNYuyRn4WxaWeBLensJ3PmAd-ouY,11469
68
70
  geo_activity_playground/webui/blueprints/tile_blueprint.py,sha256=YzZf9OrNdjhc1_j4MtO1DMcw1uCv29ueNsYd-mWqgbg,837
@@ -106,10 +108,10 @@ geo_activity_playground/webui/static/vega@5,sha256=5DLHUaY2P0ph2mKSDMfX69E88J2Cl
106
108
  geo_activity_playground/webui/static/web-app-manifest-192x192.png,sha256=eEImN6iWfSv-EnSNPL5WbX84PKakse_8VZMBPWWye3o,13582
107
109
  geo_activity_playground/webui/static/web-app-manifest-512x512.png,sha256=vU9oQ4HnQerFDZVzcAT9twj4_Doc6_9v9wVvoRI-f_E,48318
108
110
  geo_activity_playground/webui/templates/activity/day.html.j2,sha256=CHEvxlZralCm3-kTbZsGn0xj9VdSv9V5zalPSoAluus,2810
109
- geo_activity_playground/webui/templates/activity/edit.html.j2,sha256=9HDFjYfUQBB6HAgeIZppFPlpiJ1vDZWcGyP7uYG_Hnw,1369
111
+ geo_activity_playground/webui/templates/activity/edit.html.j2,sha256=r979JPqaZi_2ymTykxpkjdpw0D2tsB9VJaf7OaGPaME,1961
110
112
  geo_activity_playground/webui/templates/activity/lines.html.j2,sha256=_ZDg1ruW-9UMJfOudy1-uY_-IcSSaagq7tPCih5Bb8g,1079
111
113
  geo_activity_playground/webui/templates/activity/name.html.j2,sha256=7Wbh3IrVL5lMRve467H0P10Shn5FzGpaXLhV0H-X4Hk,2725
112
- geo_activity_playground/webui/templates/activity/show.html.j2,sha256=70zyc-fIr4JeU7VuLp_O4SYuNMkvDDTnsLFq7woYTlo,8964
114
+ geo_activity_playground/webui/templates/activity/show.html.j2,sha256=ciUMJAU56nUVbl61NiVuJ4vAr1f7dPrx2kRJzkNrE9E,9273
113
115
  geo_activity_playground/webui/templates/activity/trim.html.j2,sha256=3oAXQab6QqWjGBC9KCvWNOVn8uRmxoDLj3hx_O63TXc,1836
114
116
  geo_activity_playground/webui/templates/auth/index.html.j2,sha256=ILQ5HvTEYc3OrtOAIFt1VrqWorVD70V9DC342znmP70,579
115
117
  geo_activity_playground/webui/templates/bubble_chart/index.html.j2,sha256=yd7lWjtxxVmJZqCiXb0Y1gMEOQ7LQYJXEdpE7JB1OZY,1616
@@ -131,7 +133,7 @@ geo_activity_playground/webui/templates/search_form.html.j2,sha256=TG9xIql0HnhsX
131
133
  geo_activity_playground/webui/templates/settings/admin-password.html.j2,sha256=VYwddpObD1RpeTH5Dm4y7VtmT7kwURDCIjxyzJeq08c,495
132
134
  geo_activity_playground/webui/templates/settings/color-schemes.html.j2,sha256=iR91Wxd2_TMuIo9dBDZBrWSUGHNwTwzC6O8oNH-XBt4,1653
133
135
  geo_activity_playground/webui/templates/settings/heart-rate.html.j2,sha256=UPT3MegRgSeff36lhCo0l3ZwhqNSIg5gM6h2s32GkCY,4255
134
- geo_activity_playground/webui/templates/settings/index.html.j2,sha256=3a62-F3EWZpY-n0zB8sBgQSx_HUyr_2OWOa5neln94o,4648
136
+ geo_activity_playground/webui/templates/settings/index.html.j2,sha256=sphzznODrgb9I3SdYoV8zsmnh2FHxzt5NhhrVPPO9wY,5062
135
137
  geo_activity_playground/webui/templates/settings/manage-equipments.html.j2,sha256=vPGGlwyG_xMZc4a6JdajwWMJBfN1lBNBtDSt6QPJBiY,1585
136
138
  geo_activity_playground/webui/templates/settings/manage-kinds.html.j2,sha256=382VW-cEe0iPJ8TNL2jrcRtVYb_RFdzDyeC1ncpWZ9M,1616
137
139
  geo_activity_playground/webui/templates/settings/metadata-extraction.html.j2,sha256=0g9RlHFKipN45RaH_FANWnY1lfXUkKjtc_9B-vJ19LQ,2298
@@ -139,12 +141,15 @@ geo_activity_playground/webui/templates/settings/privacy-zones.html.j2,sha256=Kp
139
141
  geo_activity_playground/webui/templates/settings/segmentation.html.j2,sha256=QV72TZcIxqql-vEsq2lKHzo5UxoxeeXkRA9se46GWKU,1187
140
142
  geo_activity_playground/webui/templates/settings/sharepic.html.j2,sha256=qZkfEpd4CtKKMaSSVadqvNEgMRYLV-0X-pw5-nJvukk,678
141
143
  geo_activity_playground/webui/templates/settings/strava.html.j2,sha256=GCE5gskQ6xJ8AM1qGrrUVLDOiuqg510mWzzsZjia0gk,2211
144
+ geo_activity_playground/webui/templates/settings/tags-edit.html.j2,sha256=OAXg_P-ZN7-LLvvzP0xVtadxb0t0TDD8xiWniL4U1FM,378
145
+ geo_activity_playground/webui/templates/settings/tags-list.html.j2,sha256=AsFhWyMejbNMvOgWhqRtAiJGK8eqkHgECZ7mKFpQWkA,366
146
+ geo_activity_playground/webui/templates/settings/tags-new.html.j2,sha256=xi6KbwydDVrUJM4_ty4KbMa74k3QaoyZhZAn2paERnM,358
142
147
  geo_activity_playground/webui/templates/square_planner/index.html.j2,sha256=-OnY2nQCgZCslOzf28ogZwFykwF8tZm7PgFwOE3eBDk,8176
143
148
  geo_activity_playground/webui/templates/summary/index.html.j2,sha256=ozGuoF36RBUN56Cnc8AFFguRdu_c4dP4eAotw8aidE8,9262
144
149
  geo_activity_playground/webui/templates/upload/index.html.j2,sha256=I1Ix8tDS3YBdi-HdaNfjkzYXVVCjfUTe5PFTnap1ydc,775
145
150
  geo_activity_playground/webui/templates/upload/reload.html.j2,sha256=YZWX5eDeNyqKJdQAywDBcU8DZBm22rRBbZqFjrFrCvQ,556
146
- geo_activity_playground-0.41.0.dist-info/LICENSE,sha256=4RpAwKO8bPkfXH2lnpeUW0eLkNWglyG4lbrLDU_MOwY,1070
147
- geo_activity_playground-0.41.0.dist-info/METADATA,sha256=0vXuY0ptWhByXxwVagr3Xlmgf0Frwhatre1ZPSHM6h4,1758
148
- geo_activity_playground-0.41.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
149
- geo_activity_playground-0.41.0.dist-info/entry_points.txt,sha256=pbNlLI6IIZIp7nPYCfAtiSiz2oxJSCl7DODD6SPkLKk,81
150
- geo_activity_playground-0.41.0.dist-info/RECORD,,
151
+ geo_activity_playground-0.42.0.dist-info/LICENSE,sha256=4RpAwKO8bPkfXH2lnpeUW0eLkNWglyG4lbrLDU_MOwY,1070
152
+ geo_activity_playground-0.42.0.dist-info/METADATA,sha256=inIY4eJNLOogHUVXAvLBuBYmxc_Cp1p0FipaGN0SJq8,1809
153
+ geo_activity_playground-0.42.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
154
+ geo_activity_playground-0.42.0.dist-info/entry_points.txt,sha256=pbNlLI6IIZIp7nPYCfAtiSiz2oxJSCl7DODD6SPkLKk,81
155
+ geo_activity_playground-0.42.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.0
2
+ Generator: poetry-core 1.9.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any