geo-activity-playground 0.39.1__tar.gz → 0.40.1__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.
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/PKG-INFO +1 -1
- geo_activity_playground-0.40.1/geo_activity_playground/__init__.py +0 -0
- geo_activity_playground-0.40.1/geo_activity_playground/alembic/versions/93cc82ad1b60_add_parametricplotspec.py +39 -0
- geo_activity_playground-0.40.1/geo_activity_playground/core/__init__.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/core/datamodel.py +54 -7
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/core/enrichment.py +7 -5
- geo_activity_playground-0.40.1/geo_activity_playground/core/parametric_plot.py +108 -0
- geo_activity_playground-0.40.1/geo_activity_playground/core/test_datamodel.py +20 -0
- geo_activity_playground-0.40.1/geo_activity_playground/explorer/__init__.py +0 -0
- geo_activity_playground-0.40.1/geo_activity_playground/importers/__init__.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/importers/activity_parsers.py +11 -11
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/importers/strava_checkout.py +9 -5
- geo_activity_playground-0.40.1/geo_activity_playground/webui/__init__.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/app.py +10 -4
- geo_activity_playground-0.40.1/geo_activity_playground/webui/blueprints/__init__.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/blueprints/activity_blueprint.py +7 -7
- geo_activity_playground-0.40.1/geo_activity_playground/webui/blueprints/plot_builder_blueprint.py +89 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/blueprints/summary_blueprint.py +8 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/activity/show.html.j2 +3 -3
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/page.html.j2 +5 -1
- geo_activity_playground-0.40.1/geo_activity_playground/webui/templates/plot_builder/edit.html.j2 +63 -0
- geo_activity_playground-0.40.1/geo_activity_playground/webui/templates/plot_builder/index.html.j2 +32 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/summary/index.html.j2 +12 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/pyproject.toml +1 -1
- geo_activity_playground-0.39.1/geo_activity_playground/core/parametric_plot.py +0 -101
- geo_activity_playground-0.39.1/geo_activity_playground/webui/blueprints/plot_builder_blueprint.py +0 -43
- geo_activity_playground-0.39.1/geo_activity_playground/webui/templates/plot_builder/index.html.j2 +0 -44
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/LICENSE +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/__main__.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/alembic/README +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/alembic/env.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/alembic/script.py.mako +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/alembic/versions/451e7836b53d_add_square_planner_bookmark.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/alembic/versions/63d3b7f6f93c_initial_version.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/alembic/versions/ab83b9d23127_add_upstream_id.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/alembic/versions/b03491c593f6_add_crop_indices.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/alembic/versions/e02e27876deb_add_square_planner_bookmark_name.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/alembic/versions/script.py.mako +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/core/activities.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/core/config.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/core/coordinates.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/core/heart_rate.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/core/meta_search.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/core/paths.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/core/privacy_zones.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/core/raster_map.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/core/similarity.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/core/summary_stats.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/core/tasks.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/core/test_meta_search.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/core/test_summary_stats.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/core/test_tiles.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/core/test_time_conversion.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/core/tiles.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/core/time_conversion.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/explorer/grid_file.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/explorer/tile_visits.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/explorer/video.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/heatmap_video.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/importers/csv_parser.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/importers/directory.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/importers/strava_api.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/importers/test_csv_parser.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/importers/test_directory.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/importers/test_strava_api.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/authenticator.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/blueprints/auth_blueprint.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/blueprints/bubble_chart_blueprint.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/blueprints/calendar_blueprint.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/blueprints/eddington_blueprint.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/blueprints/entry_views.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/blueprints/equipment_blueprint.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/blueprints/explorer_blueprint.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/blueprints/heatmap_blueprint.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/blueprints/search_blueprint.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/blueprints/settings_blueprint.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/blueprints/square_planner_blueprint.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/blueprints/tile_blueprint.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/blueprints/upload_blueprint.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/flasher.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/plot_util.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/search_util.py +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/Leaflet.fullscreen.min.js +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/MarkerCluster.Default.css +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/MarkerCluster.css +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/android-chrome-192x192.png +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/android-chrome-512x512.png +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/apple-touch-icon.png +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/bootstrap-dark-mode.js +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/bootstrap.bundle.min.js +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/bootstrap.min.css +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/browserconfig.xml +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/favicon-16x16.png +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/favicon-32x32.png +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/favicon-48x48.png +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/favicon.ico +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/favicon.svg +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/fullscreen.png +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/fullscreen@2x.png +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/images/layers-2x.png +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/images/layers.png +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/images/marker-icon-2x.png +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/images/marker-icon.png +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/images/marker-shadow.png +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/leaflet.css +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/leaflet.fullscreen.css +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/leaflet.js +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/leaflet.markercluster.js +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/mstile-150x150.png +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/site.webmanifest +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/table-sort.min.js +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/vega-embed@6 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/vega-lite@4 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/vega@5 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/web-app-manifest-192x192.png +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/static/web-app-manifest-512x512.png +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/activity/day.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/activity/edit.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/activity/lines.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/activity/name.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/activity/trim.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/auth/index.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/bubble_chart/index.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/calendar/index.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/calendar/month.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/eddington/index.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/equipment/index.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/explorer/index.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/heatmap/index.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/home.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/search/index.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/search_form.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/settings/admin-password.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/settings/color-schemes.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/settings/heart-rate.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/settings/index.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/settings/manage-equipments.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/settings/manage-kinds.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/settings/metadata-extraction.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/settings/privacy-zones.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/settings/segmentation.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/settings/sharepic.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/settings/strava.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/square_planner/index.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/upload/index.html.j2 +0 -0
- {geo_activity_playground-0.39.1 → geo_activity_playground-0.40.1}/geo_activity_playground/webui/templates/upload/reload.html.j2 +0 -0
File without changes
|
@@ -0,0 +1,39 @@
|
|
1
|
+
from typing import Sequence
|
2
|
+
from typing import Union
|
3
|
+
|
4
|
+
import sqlalchemy as sa
|
5
|
+
from alembic import op
|
6
|
+
|
7
|
+
|
8
|
+
# revision identifiers, used by Alembic.
|
9
|
+
revision: str = "93cc82ad1b60"
|
10
|
+
down_revision: Union[str, None] = "e02e27876deb"
|
11
|
+
branch_labels: Union[str, Sequence[str], None] = None
|
12
|
+
depends_on: Union[str, Sequence[str], None] = None
|
13
|
+
|
14
|
+
|
15
|
+
def upgrade() -> None:
|
16
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
17
|
+
op.create_table(
|
18
|
+
"plot_specs",
|
19
|
+
sa.Column("id", sa.Integer(), nullable=False),
|
20
|
+
sa.Column("name", sa.String(), nullable=False),
|
21
|
+
sa.Column("mark", sa.String(), nullable=True),
|
22
|
+
sa.Column("x", sa.String(), nullable=True),
|
23
|
+
sa.Column("y", sa.String(), nullable=True),
|
24
|
+
sa.Column("color", sa.String(), nullable=True),
|
25
|
+
sa.Column("shape", sa.String(), nullable=True),
|
26
|
+
sa.Column("size", sa.String(), nullable=True),
|
27
|
+
sa.Column("row", sa.String(), nullable=True),
|
28
|
+
sa.Column("opacity", sa.String(), nullable=True),
|
29
|
+
sa.Column("column", sa.String(), nullable=True),
|
30
|
+
sa.Column("facet", sa.String(), nullable=True),
|
31
|
+
sa.PrimaryKeyConstraint("id"),
|
32
|
+
)
|
33
|
+
# ### end Alembic commands ###
|
34
|
+
|
35
|
+
|
36
|
+
def downgrade() -> None:
|
37
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
38
|
+
op.drop_table("plot_specs")
|
39
|
+
# ### end Alembic commands ###
|
File without changes
|
@@ -1,6 +1,8 @@
|
|
1
1
|
import datetime
|
2
|
+
import json
|
2
3
|
import logging
|
3
4
|
from typing import Any
|
5
|
+
from typing import Optional
|
4
6
|
from typing import TypedDict
|
5
7
|
|
6
8
|
import numpy as np
|
@@ -109,21 +111,25 @@ class Activity(DB.Model):
|
|
109
111
|
return f"{self.start} {self.name}"
|
110
112
|
|
111
113
|
@property
|
112
|
-
def average_speed_moving_kmh(self) -> float:
|
113
|
-
|
114
|
+
def average_speed_moving_kmh(self) -> Optional[float]:
|
115
|
+
if self.moving_time:
|
116
|
+
return self.distance_km / (self.moving_time.total_seconds() / 3_600)
|
114
117
|
|
115
118
|
@property
|
116
|
-
def average_speed_elapsed_kmh(self) -> float:
|
117
|
-
|
119
|
+
def average_speed_elapsed_kmh(self) -> Optional[float]:
|
120
|
+
if self.elapsed_time:
|
121
|
+
return self.distance_km / (self.elapsed_time.total_seconds() / 3_600)
|
118
122
|
|
119
123
|
@property
|
120
124
|
def raw_time_series(self) -> pd.DataFrame:
|
121
125
|
path = time_series_dir() / f"{self.id}.parquet"
|
122
126
|
try:
|
123
|
-
|
127
|
+
time_series = pd.read_parquet(path)
|
128
|
+
if "altitude" in time_series.columns:
|
129
|
+
time_series.rename(columns={"altitude": "elevation"}, inplace=True)
|
130
|
+
return time_series
|
124
131
|
except OSError as e:
|
125
|
-
logger.error(f"Error while reading {path}
|
126
|
-
path.unlink(missing_ok=True)
|
132
|
+
logger.error(f"Error while reading {path}.")
|
127
133
|
raise
|
128
134
|
|
129
135
|
@property
|
@@ -255,3 +261,44 @@ def get_or_make_equipment(name: str, config: Config) -> Equipment:
|
|
255
261
|
)
|
256
262
|
DB.session.add(equipment)
|
257
263
|
return equipment
|
264
|
+
|
265
|
+
|
266
|
+
class PlotSpec(DB.Model):
|
267
|
+
__tablename__ = "plot_specs"
|
268
|
+
|
269
|
+
id: Mapped[int] = mapped_column(primary_key=True)
|
270
|
+
|
271
|
+
name: Mapped[str] = mapped_column(sa.String, nullable=False)
|
272
|
+
|
273
|
+
mark: Mapped[str] = mapped_column(sa.String, nullable=False, default="")
|
274
|
+
x: Mapped[str] = mapped_column(sa.String, nullable=False, default="")
|
275
|
+
y: Mapped[str] = mapped_column(sa.String, nullable=False, default="")
|
276
|
+
color: Mapped[str] = mapped_column(sa.String, nullable=False, default="")
|
277
|
+
shape: Mapped[str] = mapped_column(sa.String, nullable=False, default="")
|
278
|
+
size: Mapped[str] = mapped_column(sa.String, nullable=False, default="")
|
279
|
+
row: Mapped[str] = mapped_column(sa.String, nullable=False, default="")
|
280
|
+
opacity: Mapped[str] = mapped_column(sa.String, nullable=False, default="")
|
281
|
+
column: Mapped[str] = mapped_column(sa.String, nullable=False, default="")
|
282
|
+
facet: Mapped[str] = mapped_column(sa.String, nullable=False, default="")
|
283
|
+
|
284
|
+
FIELDS = [
|
285
|
+
"name",
|
286
|
+
"mark",
|
287
|
+
"x",
|
288
|
+
"y",
|
289
|
+
"color",
|
290
|
+
"shape",
|
291
|
+
"size",
|
292
|
+
"row",
|
293
|
+
"opacity",
|
294
|
+
"column",
|
295
|
+
"facet",
|
296
|
+
]
|
297
|
+
|
298
|
+
def __str__(self) -> str:
|
299
|
+
return self.name
|
300
|
+
|
301
|
+
def to_json(self) -> str:
|
302
|
+
return json.dumps(
|
303
|
+
{key: getattr(self, key) for key in self.FIELDS if getattr(self, key)}
|
304
|
+
)
|
@@ -192,10 +192,12 @@ def _embellish_single_time_series(
|
|
192
192
|
timeseries["y"] = y
|
193
193
|
|
194
194
|
if "altitude" in timeseries.columns:
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
195
|
+
timeseries.rename(columns={"altitude": "elevation"}, inplace=True)
|
196
|
+
if "elevation" in timeseries.columns:
|
197
|
+
elevation_diff = timeseries["elevation"].diff()
|
198
|
+
elevation_diff = elevation_diff.ewm(span=5, min_periods=5).mean()
|
199
|
+
elevation_diff.loc[elevation_diff.abs() > 30] = 0
|
200
|
+
elevation_diff.loc[elevation_diff < 0] = 0
|
201
|
+
timeseries["elevation_gain_cum"] = elevation_diff.cumsum()
|
200
202
|
|
201
203
|
return timeseries
|
@@ -0,0 +1,108 @@
|
|
1
|
+
import altair as alt
|
2
|
+
import pandas as pd
|
3
|
+
|
4
|
+
from .datamodel import PlotSpec
|
5
|
+
|
6
|
+
|
7
|
+
MARKS = {
|
8
|
+
"point": "Point",
|
9
|
+
"circle": "Circle",
|
10
|
+
"area": "Area",
|
11
|
+
"bar": "Bar",
|
12
|
+
"rect": "Rectangle",
|
13
|
+
}
|
14
|
+
CONTINUOUS_VARIABLES = {
|
15
|
+
"distance_km": "Distance / km",
|
16
|
+
"sum(distance_km)": "Total distance / km",
|
17
|
+
"mean(distance_km)": "Average distance / km",
|
18
|
+
"start": "Date",
|
19
|
+
"hours": "Elapsed time / h",
|
20
|
+
"hours_moving": "Moving time / h",
|
21
|
+
"calories": "Energy / kcal",
|
22
|
+
"steps": "Steps",
|
23
|
+
"elevation_gain": "Elevation gain / m",
|
24
|
+
"start_elevation": "Start elevation / m",
|
25
|
+
"end_elevation": "End elevation / m",
|
26
|
+
"sum(elevation_gain)": "Total elevation gain / m",
|
27
|
+
"mean(elevation_gain)": "Average elevation gain / m",
|
28
|
+
"num_new_tiles_14": "New tiles 14",
|
29
|
+
"num_new_tiles_14": "New tiles 17",
|
30
|
+
"average_speed_moving_kmh": "Average moving speed / km/h",
|
31
|
+
"average_speed_elapsed_kmh": "Average elapsed speed / km/h",
|
32
|
+
"start_latitude": "Start latitude / °",
|
33
|
+
"start_longitude": "Start longitude / °",
|
34
|
+
"end_latitude": "End latitude / °",
|
35
|
+
"end_longitude": "End longitude / °",
|
36
|
+
}
|
37
|
+
DISCRETE_VARIABLES = {
|
38
|
+
"equipment": "Equipment",
|
39
|
+
"kind": "Activity kind",
|
40
|
+
"consider_for_achievements": "Consider for achievements",
|
41
|
+
"year(start):O": "Year",
|
42
|
+
"iso_year:O": "ISO Year",
|
43
|
+
"yearquarter(start)": "Year, Quarter",
|
44
|
+
"yearquartermonth(start)": "Year, Quarter, Month",
|
45
|
+
"yearmonth(start)": "Year, Month",
|
46
|
+
"quarter(start)": "Quarter",
|
47
|
+
"quartermonth(start)": "Quarter, Month",
|
48
|
+
"month(start)": "Month",
|
49
|
+
"week:O": "ISO Week",
|
50
|
+
"date(start)": "Day of month",
|
51
|
+
"weekday(start)": "Day of week",
|
52
|
+
}
|
53
|
+
|
54
|
+
VARIABLES_1 = {"": "", **DISCRETE_VARIABLES}
|
55
|
+
VARIABLES_2 = {"": "", **DISCRETE_VARIABLES, **CONTINUOUS_VARIABLES}
|
56
|
+
|
57
|
+
|
58
|
+
def make_parametric_plot(df: pd.DataFrame, spec: PlotSpec) -> str:
|
59
|
+
chart = alt.Chart(df)
|
60
|
+
|
61
|
+
match spec.mark:
|
62
|
+
case "point":
|
63
|
+
chart = chart.mark_point()
|
64
|
+
case "circle":
|
65
|
+
chart = chart.mark_circle()
|
66
|
+
case "area":
|
67
|
+
chart = chart.mark_area()
|
68
|
+
case "bar":
|
69
|
+
chart = chart.mark_bar()
|
70
|
+
case "rect":
|
71
|
+
chart = chart.mark_rect()
|
72
|
+
case _:
|
73
|
+
raise ValueError()
|
74
|
+
|
75
|
+
encodings = [
|
76
|
+
alt.X(spec.x, title=VARIABLES_2[spec.x]),
|
77
|
+
alt.Y(spec.y, title=VARIABLES_2[spec.y]),
|
78
|
+
]
|
79
|
+
tooltips = [
|
80
|
+
alt.Tooltip(spec.x, title=VARIABLES_2[spec.x]),
|
81
|
+
alt.Tooltip(spec.y, title=VARIABLES_2[spec.y]),
|
82
|
+
]
|
83
|
+
|
84
|
+
if spec.color:
|
85
|
+
encodings.append(alt.Color(spec.color, title=VARIABLES_2[spec.color]))
|
86
|
+
tooltips.append(alt.Tooltip(spec.color, title=VARIABLES_2[spec.color]))
|
87
|
+
if spec.shape:
|
88
|
+
encodings.append(alt.Shape(spec.shape, title=VARIABLES_2[spec.shape]))
|
89
|
+
tooltips.append(alt.Tooltip(spec.shape, title=VARIABLES_2[spec.shape]))
|
90
|
+
if spec.size:
|
91
|
+
encodings.append(alt.Size(spec.size, title=VARIABLES_2[spec.size]))
|
92
|
+
tooltips.append(alt.Tooltip(spec.size, title=VARIABLES_2[spec.size]))
|
93
|
+
if spec.opacity:
|
94
|
+
encodings.append(alt.Size(spec.opacity, title=VARIABLES_2[spec.opacity]))
|
95
|
+
tooltips.append(alt.Opacity(spec.opacity, title=VARIABLES_2[spec.opacity]))
|
96
|
+
if spec.row:
|
97
|
+
encodings.append(alt.Row(spec.row, title=VARIABLES_2[spec.row]))
|
98
|
+
tooltips.append(alt.Tooltip(spec.row, title=VARIABLES_2[spec.row]))
|
99
|
+
if spec.column:
|
100
|
+
encodings.append(alt.Column(spec.column, title=VARIABLES_2[spec.column]))
|
101
|
+
tooltips.append(alt.Tooltip(spec.column, title=VARIABLES_2[spec.column]))
|
102
|
+
if spec.facet:
|
103
|
+
encodings.append(
|
104
|
+
alt.Facet(spec.facet, columns=3, title=VARIABLES_2[spec.facet])
|
105
|
+
)
|
106
|
+
tooltips.append(alt.Tooltip(spec.facet, title=VARIABLES_2[spec.facet]))
|
107
|
+
|
108
|
+
return chart.encode(*encodings, tooltips).interactive().to_json(format="vega")
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import datetime
|
2
|
+
|
3
|
+
from .datamodel import Activity
|
4
|
+
|
5
|
+
|
6
|
+
def test_no_duration() -> None:
|
7
|
+
activity = Activity(name="Test", distance_km=10.0)
|
8
|
+
assert activity.average_speed_elapsed_kmh is None
|
9
|
+
assert activity.average_speed_moving_kmh is None
|
10
|
+
|
11
|
+
|
12
|
+
def test_zero_duration() -> None:
|
13
|
+
activity = Activity(
|
14
|
+
name="Test",
|
15
|
+
distance_km=10.0,
|
16
|
+
elapsed_time=datetime.timedelta(seconds=0),
|
17
|
+
moving_time=datetime.timedelta(seconds=0),
|
18
|
+
)
|
19
|
+
assert activity.average_speed_elapsed_kmh is None
|
20
|
+
assert activity.average_speed_moving_kmh is None
|
File without changes
|
File without changes
|
@@ -121,9 +121,9 @@ def read_fit_activity(path: pathlib.Path, open) -> tuple[ActivityMeta, pd.DataFr
|
|
121
121
|
if "distance" in fields:
|
122
122
|
row["distance"] = values["distance"]
|
123
123
|
if "altitude" in fields:
|
124
|
-
row["
|
124
|
+
row["elevation"] = values["altitude"]
|
125
125
|
if "enhanced_altitude" in fields:
|
126
|
-
row["
|
126
|
+
row["elevation"] = values["enhanced_altitude"]
|
127
127
|
if "speed" in fields:
|
128
128
|
factor = _fit_speed_unit_factor(fields["speed"].units)
|
129
129
|
row["speed"] = values["speed"] * factor
|
@@ -188,10 +188,10 @@ def read_gpx_activity(path: pathlib.Path, open) -> pd.DataFrame:
|
|
188
188
|
time = convert_to_datetime_ns(time)
|
189
189
|
points.append((time, point.latitude, point.longitude, point.elevation))
|
190
190
|
|
191
|
-
df = pd.DataFrame(points, columns=["time", "latitude", "longitude", "
|
192
|
-
# Some files don't have
|
193
|
-
if not df["
|
194
|
-
del df["
|
191
|
+
df = pd.DataFrame(points, columns=["time", "latitude", "longitude", "elevation"])
|
192
|
+
# Some files don't have elevation information. In these cases we remove the column.
|
193
|
+
if not df["elevation"].any():
|
194
|
+
del df["elevation"]
|
195
195
|
return df
|
196
196
|
|
197
197
|
|
@@ -230,7 +230,7 @@ def read_tcx_activity(path: pathlib.Path, opener) -> pd.DataFrame:
|
|
230
230
|
"longitude": trackpoint.longitude,
|
231
231
|
}
|
232
232
|
if trackpoint.elevation:
|
233
|
-
row["
|
233
|
+
row["elevation"] = trackpoint.elevation
|
234
234
|
if trackpoint.hr_value:
|
235
235
|
row["heartrate"] = trackpoint.hr_value
|
236
236
|
if trackpoint.cadence:
|
@@ -256,16 +256,16 @@ def read_kml_activity(path: pathlib.Path, opener) -> pd.DataFrame:
|
|
256
256
|
parts = where.split(" ")
|
257
257
|
if len(parts) == 2:
|
258
258
|
lon, lat = parts
|
259
|
-
|
259
|
+
elevation = None
|
260
260
|
if len(parts) == 3:
|
261
|
-
lon, lat,
|
261
|
+
lon, lat, elevation = parts
|
262
262
|
row = {
|
263
263
|
"time": time,
|
264
264
|
"latitude": float(lat),
|
265
265
|
"longitude": float(lon),
|
266
266
|
}
|
267
|
-
if
|
268
|
-
row["
|
267
|
+
if elevation is not None:
|
268
|
+
row["elevation"] = float(elevation)
|
269
269
|
rows.append(row)
|
270
270
|
return pd.DataFrame(rows)
|
271
271
|
|
@@ -258,15 +258,21 @@ def convert_strava_checkout(
|
|
258
258
|
activities = pd.read_csv(checkout_path / "activities.csv")
|
259
259
|
print(activities)
|
260
260
|
|
261
|
+
# Handle German localization.
|
262
|
+
if activities.columns[0] == "Aktivitäts-ID":
|
263
|
+
assert len(activities.columns) == len(
|
264
|
+
EXPECTED_COLUMNS
|
265
|
+
), "Strava seems to have changed for format again. Please file a bug report at https://github.com/martin-ueding/geo-activity-playground/issues and include the first line of the 'activities.csv'."
|
266
|
+
activities.columns = EXPECTED_COLUMNS
|
267
|
+
|
261
268
|
for _, row in tqdm(activities.iterrows(), desc="Import activity files"):
|
262
269
|
# Some people have manually added activities without position data. These don't have a file there. We'll skip these.
|
263
270
|
if not isinstance(row["Filename"], str):
|
264
271
|
continue
|
265
272
|
|
266
273
|
activity_date = dateutil.parser.parse(row["Activity Date"])
|
267
|
-
activity_name = row["Activity Name"]
|
274
|
+
activity_name: str = row["Activity Name"]
|
268
275
|
activity_kind = row["Activity Type"]
|
269
|
-
is_commute = row["Commute"] == "true" or row["Commute"] == True
|
270
276
|
equipment = (
|
271
277
|
nan_as_none(row["Activity Gear"])
|
272
278
|
or nan_as_none(row["Bike"])
|
@@ -278,8 +284,6 @@ def convert_strava_checkout(
|
|
278
284
|
activity_target = playground_path / "Activities" / str(activity_kind)
|
279
285
|
if equipment:
|
280
286
|
activity_target /= str(equipment)
|
281
|
-
if is_commute:
|
282
|
-
activity_target /= "Commute"
|
283
287
|
|
284
288
|
activity_target /= "".join(
|
285
289
|
[
|
@@ -287,7 +291,7 @@ def convert_strava_checkout(
|
|
287
291
|
"-",
|
288
292
|
f"{activity_date.hour:02d}-{activity_date.minute:02d}-{activity_date.second:02d}",
|
289
293
|
" ",
|
290
|
-
activity_name,
|
294
|
+
activity_name.replace("/", "_"),
|
291
295
|
]
|
292
296
|
+ activity_file.suffixes
|
293
297
|
)
|
File without changes
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import datetime
|
2
2
|
import importlib
|
3
3
|
import json
|
4
|
+
import logging
|
4
5
|
import os
|
5
6
|
import pathlib
|
6
7
|
import secrets
|
@@ -44,6 +45,9 @@ from .flasher import FlaskFlasher
|
|
44
45
|
from .search_util import SearchQueryHistory
|
45
46
|
|
46
47
|
|
48
|
+
logger = logging.getLogger(__name__)
|
49
|
+
|
50
|
+
|
47
51
|
def get_secret_key():
|
48
52
|
secret_file = pathlib.Path("Cache/flask-secret.json")
|
49
53
|
if secret_file.exists():
|
@@ -66,9 +70,9 @@ def web_ui_main(
|
|
66
70
|
|
67
71
|
app = Flask(__name__)
|
68
72
|
|
69
|
-
|
70
|
-
|
71
|
-
)
|
73
|
+
database_path = basedir / "database.sqlite"
|
74
|
+
logger.info(f"Using database file at '{database_path.absolute()}'.")
|
75
|
+
app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{database_path.absolute()}"
|
72
76
|
app.config["ALEMBIC"] = {"script_location": "../alembic/versions"}
|
73
77
|
DB.init_app(app)
|
74
78
|
|
@@ -138,7 +142,9 @@ def web_ui_main(
|
|
138
142
|
"/heatmap": make_heatmap_blueprint(
|
139
143
|
repository, tile_visit_accessor, config_accessor(), search_query_history
|
140
144
|
),
|
141
|
-
"/plot-builder": make_plot_builder_blueprint(
|
145
|
+
"/plot-builder": make_plot_builder_blueprint(
|
146
|
+
repository, flasher, authenticator
|
147
|
+
),
|
142
148
|
"/settings": make_settings_blueprint(config_accessor, authenticator, flasher),
|
143
149
|
"/square-planner": make_square_planner_blueprint(tile_visit_accessor),
|
144
150
|
"/search": make_search_blueprint(
|
File without changes
|
@@ -148,8 +148,8 @@ def make_activity_blueprint(
|
|
148
148
|
)
|
149
149
|
) is not None:
|
150
150
|
context["heart_zones_plot"] = heart_rate_zone_plot(heart_zones)
|
151
|
-
if "
|
152
|
-
context["
|
151
|
+
if "elevation" in time_series.columns:
|
152
|
+
context["elevation_time_plot"] = elevation_time_plot(time_series)
|
153
153
|
if "elevation_gain_cum" in time_series.columns:
|
154
154
|
context["elevation_gain_cum_plot"] = elevation_gain_cum_plot(time_series)
|
155
155
|
if "heartrate" in time_series.columns:
|
@@ -445,13 +445,13 @@ def distance_time_plot(time_series: pd.DataFrame) -> str:
|
|
445
445
|
)
|
446
446
|
|
447
447
|
|
448
|
-
def
|
448
|
+
def elevation_time_plot(time_series: pd.DataFrame) -> str:
|
449
449
|
return (
|
450
|
-
alt.Chart(time_series, title="
|
450
|
+
alt.Chart(time_series, title="Elevation")
|
451
451
|
.mark_line()
|
452
452
|
.encode(
|
453
453
|
alt.X("time", title="Time"),
|
454
|
-
alt.Y("
|
454
|
+
alt.Y("elevation", scale=alt.Scale(zero=False), title="Elevation / m"),
|
455
455
|
alt.Color("segment_id:N", title="Segment"),
|
456
456
|
)
|
457
457
|
.interactive(bind_y=False)
|
@@ -461,14 +461,14 @@ def altitude_time_plot(time_series: pd.DataFrame) -> str:
|
|
461
461
|
|
462
462
|
def elevation_gain_cum_plot(time_series: pd.DataFrame) -> str:
|
463
463
|
return (
|
464
|
-
alt.Chart(time_series, title="
|
464
|
+
alt.Chart(time_series, title="Elevation Gain")
|
465
465
|
.mark_line()
|
466
466
|
.encode(
|
467
467
|
alt.X("time", title="Time"),
|
468
468
|
alt.Y(
|
469
469
|
"elevation_gain_cum",
|
470
470
|
scale=alt.Scale(zero=False),
|
471
|
-
title="
|
471
|
+
title="Elevation gain / m",
|
472
472
|
),
|
473
473
|
alt.Color("segment_id:N", title="Segment"),
|
474
474
|
)
|
geo_activity_playground-0.40.1/geo_activity_playground/webui/blueprints/plot_builder_blueprint.py
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
import sqlalchemy
|
2
|
+
from flask import Blueprint
|
3
|
+
from flask import redirect
|
4
|
+
from flask import render_template
|
5
|
+
from flask import request
|
6
|
+
from flask import Response
|
7
|
+
from flask import url_for
|
8
|
+
|
9
|
+
from ...core.activities import ActivityRepository
|
10
|
+
from ...core.datamodel import DB
|
11
|
+
from ...core.parametric_plot import make_parametric_plot
|
12
|
+
from ...core.parametric_plot import MARKS
|
13
|
+
from ...core.parametric_plot import PlotSpec
|
14
|
+
from ...core.parametric_plot import VARIABLES_1
|
15
|
+
from ...core.parametric_plot import VARIABLES_2
|
16
|
+
from ..authenticator import Authenticator
|
17
|
+
from ..authenticator import needs_authentication
|
18
|
+
from ..flasher import Flasher
|
19
|
+
from ..flasher import FlashTypes
|
20
|
+
|
21
|
+
|
22
|
+
def make_plot_builder_blueprint(
|
23
|
+
repository: ActivityRepository, flasher: Flasher, authenticator: Authenticator
|
24
|
+
) -> Blueprint:
|
25
|
+
blueprint = Blueprint("plot_builder", __name__, template_folder="templates")
|
26
|
+
|
27
|
+
@blueprint.route("/")
|
28
|
+
def index() -> Response:
|
29
|
+
return render_template(
|
30
|
+
"plot_builder/index.html.j2",
|
31
|
+
specs=DB.session.scalars(sqlalchemy.select(PlotSpec)).all(),
|
32
|
+
)
|
33
|
+
|
34
|
+
@blueprint.route("/new")
|
35
|
+
@needs_authentication(authenticator)
|
36
|
+
def new() -> Response:
|
37
|
+
spec = PlotSpec(
|
38
|
+
name="My New Plot",
|
39
|
+
mark="bar",
|
40
|
+
x="year(start):O",
|
41
|
+
y="sum(distance_km)",
|
42
|
+
color="kind",
|
43
|
+
)
|
44
|
+
DB.session.add(spec)
|
45
|
+
DB.session.commit()
|
46
|
+
return redirect(url_for(".edit", id=spec.id))
|
47
|
+
|
48
|
+
@blueprint.route("/edit/<int:id>")
|
49
|
+
@needs_authentication(authenticator)
|
50
|
+
def edit(id: int) -> Response:
|
51
|
+
spec = DB.session.get(PlotSpec, id)
|
52
|
+
if request.args:
|
53
|
+
spec.name = request.args["name"]
|
54
|
+
spec.mark = request.args["mark"]
|
55
|
+
spec.x = request.args["x"]
|
56
|
+
spec.y = request.args["y"]
|
57
|
+
spec.color = request.args["color"]
|
58
|
+
spec.shape = request.args["shape"]
|
59
|
+
spec.size = request.args["size"]
|
60
|
+
spec.size = request.args["size"]
|
61
|
+
spec.row = request.args["row"]
|
62
|
+
spec.column = request.args["column"]
|
63
|
+
spec.facet = request.args["facet"]
|
64
|
+
spec.opacity = request.args["opacity"]
|
65
|
+
try:
|
66
|
+
plot = make_parametric_plot(repository.meta, spec)
|
67
|
+
DB.session.commit()
|
68
|
+
except ValueError as e:
|
69
|
+
plot = None
|
70
|
+
flasher.flash_message(str(e), FlashTypes.WARNING)
|
71
|
+
return render_template(
|
72
|
+
"plot_builder/edit.html.j2",
|
73
|
+
marks=MARKS,
|
74
|
+
discrete=VARIABLES_1,
|
75
|
+
continuous=VARIABLES_2,
|
76
|
+
plot=plot,
|
77
|
+
spec=spec,
|
78
|
+
)
|
79
|
+
|
80
|
+
@blueprint.route("/delete/<int:id>")
|
81
|
+
@needs_authentication(authenticator)
|
82
|
+
def delete(id: int) -> Response:
|
83
|
+
spec = DB.session.get(PlotSpec, id)
|
84
|
+
DB.session.delete(spec)
|
85
|
+
flasher.flash_message(f"Deleted plot '{spec.name}'.", FlashTypes.SUCCESS)
|
86
|
+
DB.session.commit()
|
87
|
+
return redirect(url_for(".index"))
|
88
|
+
|
89
|
+
return blueprint
|
@@ -3,6 +3,7 @@ import datetime
|
|
3
3
|
|
4
4
|
import altair as alt
|
5
5
|
import pandas as pd
|
6
|
+
import sqlalchemy
|
6
7
|
from flask import Blueprint
|
7
8
|
from flask import render_template
|
8
9
|
from flask import request
|
@@ -10,7 +11,10 @@ from flask import request
|
|
10
11
|
from ...core.activities import ActivityRepository
|
11
12
|
from ...core.activities import make_geojson_from_time_series
|
12
13
|
from ...core.config import Config
|
14
|
+
from ...core.datamodel import DB
|
15
|
+
from ...core.datamodel import PlotSpec
|
13
16
|
from ...core.meta_search import apply_search_query
|
17
|
+
from ...core.parametric_plot import make_parametric_plot
|
14
18
|
from ..plot_util import make_kind_scale
|
15
19
|
from ..search_util import search_query_from_form
|
16
20
|
from ..search_util import SearchQueryHistory
|
@@ -62,6 +66,10 @@ def make_summary_blueprint(
|
|
62
66
|
for activity_id, reasons in nominations.items()
|
63
67
|
],
|
64
68
|
query=query.to_jinja(),
|
69
|
+
custom_plots=[
|
70
|
+
(spec, make_parametric_plot(repository.meta, spec))
|
71
|
+
for spec in DB.session.scalars(sqlalchemy.select(PlotSpec)).all()
|
72
|
+
],
|
65
73
|
)
|
66
74
|
|
67
75
|
return blueprint
|
@@ -132,16 +132,16 @@
|
|
132
132
|
</div>
|
133
133
|
</div>
|
134
134
|
|
135
|
-
{% if
|
135
|
+
{% if elevation_time_plot is defined %}
|
136
136
|
<div class="row mb-3">
|
137
137
|
<div class="col">
|
138
|
-
<h2>
|
138
|
+
<h2>Elevation</h2>
|
139
139
|
</div>
|
140
140
|
</div>
|
141
141
|
|
142
142
|
<div class="row mb-3">
|
143
143
|
<div class="col-md-4">
|
144
|
-
{{ vega_direct("
|
144
|
+
{{ vega_direct("elevation_time_plot", elevation_time_plot) }}
|
145
145
|
</div>
|
146
146
|
{% if elevation_gain_cum_plot is defined %}
|
147
147
|
<div class="col-md-4">
|
@@ -130,6 +130,8 @@
|
|
130
130
|
<hr class="dropdown-divider">
|
131
131
|
</li>
|
132
132
|
|
133
|
+
<li><a class="dropdown-item" href="{{ url_for('plot_builder.index') }}">Plot Builder</a>
|
134
|
+
</li>
|
133
135
|
<li><a class="dropdown-item" href="{{ url_for('settings.index') }}">Settings</a></li>
|
134
136
|
</ul>
|
135
137
|
</li>
|
@@ -196,7 +198,9 @@
|
|
196
198
|
|
197
199
|
<div class="row border-top py-3 my-4">
|
198
200
|
<ul class="nav col-4">
|
199
|
-
<li class="nav-item px-2 nav-link"><a
|
201
|
+
<li class="nav-item px-2 nav-link"><a
|
202
|
+
href="https://github.com/martin-ueding/geo-activity-playground/blob/main/docs/changelog.md"
|
203
|
+
class="nav-link px-2 text-muted" target="_blank">Version {{ version }}</a></li>
|
200
204
|
</ul>
|
201
205
|
<ul class="nav col-8 justify-content-end">
|
202
206
|
<li class="nav-item"><a href="https://github.com/martin-ueding/geo-activity-playground"
|