geo-activity-playground 0.40.0__tar.gz → 0.41.0__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.40.0 → geo_activity_playground-0.41.0}/PKG-INFO +1 -1
- geo_activity_playground-0.41.0/geo_activity_playground/alembic/versions/38882503dc7c_add_tags_to_activities.py +70 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/alembic/versions/script.py.mako +0 -6
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/activities.py +17 -30
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/datamodel.py +83 -2
- geo_activity_playground-0.41.0/geo_activity_playground/core/test_datamodel.py +20 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/importers/strava_checkout.py +2 -5
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/app.py +6 -2
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/blueprints/activity_blueprint.py +20 -3
- geo_activity_playground-0.41.0/geo_activity_playground/webui/blueprints/bubble_chart_blueprint.py +86 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/blueprints/calendar_blueprint.py +12 -4
- geo_activity_playground-0.41.0/geo_activity_playground/webui/blueprints/eddington_blueprints.py +253 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/blueprints/entry_views.py +30 -15
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/blueprints/explorer_blueprint.py +83 -9
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/blueprints/summary_blueprint.py +102 -42
- geo_activity_playground-0.41.0/geo_activity_playground/webui/columns.py +37 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/activity/show.html.j2 +15 -4
- geo_activity_playground-0.41.0/geo_activity_playground/webui/templates/bubble_chart/index.html.j2 +42 -0
- geo_activity_playground-0.41.0/geo_activity_playground/webui/templates/eddington/elevation_gain.html.j2 +150 -0
- geo_activity_playground-0.41.0/geo_activity_playground/webui/templates/elevation_eddington/index.html.j2 +150 -0
- geo_activity_playground-0.41.0/geo_activity_playground/webui/templates/explorer/server-side.html.j2 +72 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/home.html.j2 +14 -5
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/page.html.j2 +10 -1
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/summary/index.html.j2 +91 -2
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/pyproject.toml +1 -1
- geo_activity_playground-0.40.0/geo_activity_playground/core/test_datamodel.py +0 -7
- geo_activity_playground-0.40.0/geo_activity_playground/webui/blueprints/bubble_chart_blueprint.py +0 -61
- geo_activity_playground-0.40.0/geo_activity_playground/webui/blueprints/eddington_blueprint.py +0 -194
- geo_activity_playground-0.40.0/geo_activity_playground/webui/templates/bubble_chart/index.html.j2 +0 -26
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/LICENSE +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/__init__.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/__main__.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/alembic/README +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/alembic/env.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/alembic/script.py.mako +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/alembic/versions/451e7836b53d_add_square_planner_bookmark.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/alembic/versions/63d3b7f6f93c_initial_version.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/alembic/versions/93cc82ad1b60_add_parametricplotspec.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/alembic/versions/ab83b9d23127_add_upstream_id.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/alembic/versions/b03491c593f6_add_crop_indices.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/alembic/versions/e02e27876deb_add_square_planner_bookmark_name.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/__init__.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/config.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/coordinates.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/enrichment.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/heart_rate.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/meta_search.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/parametric_plot.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/paths.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/privacy_zones.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/raster_map.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/similarity.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/summary_stats.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/tasks.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/test_meta_search.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/test_summary_stats.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/test_tiles.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/test_time_conversion.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/tiles.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/core/time_conversion.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/explorer/__init__.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/explorer/grid_file.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/explorer/tile_visits.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/explorer/video.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/heatmap_video.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/importers/__init__.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/importers/activity_parsers.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/importers/csv_parser.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/importers/directory.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/importers/strava_api.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/importers/test_csv_parser.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/importers/test_directory.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/importers/test_strava_api.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/__init__.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/authenticator.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/blueprints/__init__.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/blueprints/auth_blueprint.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/blueprints/equipment_blueprint.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/blueprints/heatmap_blueprint.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/blueprints/plot_builder_blueprint.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/blueprints/search_blueprint.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/blueprints/settings_blueprint.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/blueprints/square_planner_blueprint.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/blueprints/tile_blueprint.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/blueprints/upload_blueprint.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/flasher.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/plot_util.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/search_util.py +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/Leaflet.fullscreen.min.js +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/MarkerCluster.Default.css +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/MarkerCluster.css +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/android-chrome-192x192.png +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/android-chrome-512x512.png +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/apple-touch-icon.png +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/bootstrap-dark-mode.js +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/bootstrap.bundle.min.js +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/bootstrap.min.css +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/browserconfig.xml +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/favicon-16x16.png +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/favicon-32x32.png +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/favicon-48x48.png +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/favicon.ico +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/favicon.svg +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/fullscreen.png +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/fullscreen@2x.png +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/images/layers-2x.png +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/images/layers.png +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/images/marker-icon-2x.png +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/images/marker-icon.png +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/images/marker-shadow.png +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/leaflet.css +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/leaflet.fullscreen.css +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/leaflet.js +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/leaflet.markercluster.js +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/mstile-150x150.png +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/site.webmanifest +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/table-sort.min.js +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/vega-embed@6 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/vega-lite@4 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/vega@5 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/web-app-manifest-192x192.png +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/static/web-app-manifest-512x512.png +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/activity/day.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/activity/edit.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/activity/lines.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/activity/name.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/activity/trim.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/auth/index.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/calendar/index.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/calendar/month.html.j2 +0 -0
- /geo_activity_playground-0.40.0/geo_activity_playground/webui/templates/eddington/index.html.j2 → /geo_activity_playground-0.41.0/geo_activity_playground/webui/templates/eddington/distance.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/equipment/index.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/explorer/index.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/heatmap/index.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/plot_builder/edit.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/plot_builder/index.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/search/index.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/search_form.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/settings/admin-password.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/settings/color-schemes.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/settings/heart-rate.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/settings/index.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/settings/manage-equipments.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/settings/manage-kinds.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/settings/metadata-extraction.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/settings/privacy-zones.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/settings/segmentation.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/settings/sharepic.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/settings/strava.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/square_planner/index.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/upload/index.html.j2 +0 -0
- {geo_activity_playground-0.40.0 → geo_activity_playground-0.41.0}/geo_activity_playground/webui/templates/upload/reload.html.j2 +0 -0
@@ -0,0 +1,70 @@
|
|
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 = "38882503dc7c"
|
10
|
+
down_revision: Union[str, None] = "93cc82ad1b60"
|
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
|
+
"tags",
|
19
|
+
sa.Column("id", sa.Integer(), nullable=False),
|
20
|
+
sa.Column("tag", sa.String(), nullable=False),
|
21
|
+
sa.PrimaryKeyConstraint("id"),
|
22
|
+
sa.UniqueConstraint("tag"),
|
23
|
+
sa.UniqueConstraint("tag", name="tags_tag"),
|
24
|
+
)
|
25
|
+
op.create_table(
|
26
|
+
"activity_tag_association_table",
|
27
|
+
sa.Column("left_id", sa.Integer(), nullable=False),
|
28
|
+
sa.Column("right_id", sa.Integer(), nullable=False),
|
29
|
+
sa.ForeignKeyConstraint(
|
30
|
+
["left_id"],
|
31
|
+
["activities.id"],
|
32
|
+
),
|
33
|
+
sa.ForeignKeyConstraint(
|
34
|
+
["right_id"],
|
35
|
+
["tags.id"],
|
36
|
+
),
|
37
|
+
sa.PrimaryKeyConstraint("left_id", "right_id"),
|
38
|
+
)
|
39
|
+
with op.batch_alter_table("plot_specs", schema=None) as batch_op:
|
40
|
+
batch_op.alter_column("mark", existing_type=sa.VARCHAR(), nullable=False)
|
41
|
+
batch_op.alter_column("x", existing_type=sa.VARCHAR(), nullable=False)
|
42
|
+
batch_op.alter_column("y", existing_type=sa.VARCHAR(), nullable=False)
|
43
|
+
batch_op.alter_column("color", existing_type=sa.VARCHAR(), nullable=False)
|
44
|
+
batch_op.alter_column("shape", existing_type=sa.VARCHAR(), nullable=False)
|
45
|
+
batch_op.alter_column("size", existing_type=sa.VARCHAR(), nullable=False)
|
46
|
+
batch_op.alter_column("row", existing_type=sa.VARCHAR(), nullable=False)
|
47
|
+
batch_op.alter_column("opacity", existing_type=sa.VARCHAR(), nullable=False)
|
48
|
+
batch_op.alter_column("column", existing_type=sa.VARCHAR(), nullable=False)
|
49
|
+
batch_op.alter_column("facet", existing_type=sa.VARCHAR(), nullable=False)
|
50
|
+
|
51
|
+
# ### end Alembic commands ###
|
52
|
+
|
53
|
+
|
54
|
+
def downgrade() -> None:
|
55
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
56
|
+
with op.batch_alter_table("plot_specs", schema=None) as batch_op:
|
57
|
+
batch_op.alter_column("facet", existing_type=sa.VARCHAR(), nullable=True)
|
58
|
+
batch_op.alter_column("column", existing_type=sa.VARCHAR(), nullable=True)
|
59
|
+
batch_op.alter_column("opacity", existing_type=sa.VARCHAR(), nullable=True)
|
60
|
+
batch_op.alter_column("row", existing_type=sa.VARCHAR(), nullable=True)
|
61
|
+
batch_op.alter_column("size", existing_type=sa.VARCHAR(), nullable=True)
|
62
|
+
batch_op.alter_column("shape", existing_type=sa.VARCHAR(), nullable=True)
|
63
|
+
batch_op.alter_column("color", existing_type=sa.VARCHAR(), nullable=True)
|
64
|
+
batch_op.alter_column("y", existing_type=sa.VARCHAR(), nullable=True)
|
65
|
+
batch_op.alter_column("x", existing_type=sa.VARCHAR(), nullable=True)
|
66
|
+
batch_op.alter_column("mark", existing_type=sa.VARCHAR(), nullable=True)
|
67
|
+
|
68
|
+
op.drop_table("activity_tag_association_table")
|
69
|
+
op.drop_table("tags")
|
70
|
+
# ### end Alembic commands ###
|
@@ -16,6 +16,7 @@ from geo_activity_playground.core.datamodel import Activity
|
|
16
16
|
from geo_activity_playground.core.datamodel import ActivityMeta
|
17
17
|
from geo_activity_playground.core.datamodel import DB
|
18
18
|
from geo_activity_playground.core.datamodel import Kind
|
19
|
+
from geo_activity_playground.core.datamodel import query_activity_meta
|
19
20
|
|
20
21
|
logger = logging.getLogger(__name__)
|
21
22
|
|
@@ -79,22 +80,8 @@ class ActivityRepository:
|
|
79
80
|
|
80
81
|
@property
|
81
82
|
def meta(self) -> pd.DataFrame:
|
82
|
-
|
83
|
-
|
84
|
-
df["date"] = df["start"].dt.date
|
85
|
-
df["year"] = [start.year for start in df["start"]]
|
86
|
-
df["month"] = [start.month for start in df["start"]]
|
87
|
-
df["day"] = [start.day for start in df["start"]]
|
88
|
-
df["week"] = [start.isocalendar().week for start in df["start"]]
|
89
|
-
df["day_of_week"] = df["start"].dt.day_of_week
|
90
|
-
df["iso_year"] = [start.isocalendar().year for start in df["start"]]
|
91
|
-
df["hours"] = [
|
92
|
-
elapsed_time.total_seconds() / 3600 for elapsed_time in df["elapsed_time"]
|
93
|
-
]
|
94
|
-
df["hours_moving"] = [
|
95
|
-
moving_time.total_seconds() / 3600 for moving_time in df["moving_time"]
|
96
|
-
]
|
97
|
-
df.index = df["id"]
|
83
|
+
df = query_activity_meta()
|
84
|
+
|
98
85
|
return df
|
99
86
|
|
100
87
|
|
@@ -114,8 +101,8 @@ def inter_quartile_range(values):
|
|
114
101
|
return np.quantile(values, 0.75) - np.quantile(values, 0.25)
|
115
102
|
|
116
103
|
|
117
|
-
def make_geojson_color_line(time_series: pd.DataFrame) -> str:
|
118
|
-
low, high,
|
104
|
+
def make_geojson_color_line(time_series: pd.DataFrame, column: str) -> str:
|
105
|
+
low, high, clamp_value = _make_value_clamp(time_series[column])
|
119
106
|
cmap = matplotlib.colormaps["viridis"]
|
120
107
|
features = [
|
121
108
|
geojson.Feature(
|
@@ -126,8 +113,8 @@ def make_geojson_color_line(time_series: pd.DataFrame) -> str:
|
|
126
113
|
]
|
127
114
|
),
|
128
115
|
properties={
|
129
|
-
|
130
|
-
"color": matplotlib.colors.to_hex(cmap(
|
116
|
+
column: (next_row[column] if np.isfinite(next_row[column]) else 0.0),
|
117
|
+
"color": matplotlib.colors.to_hex(cmap(clamp_value(next_row[column]))),
|
131
118
|
},
|
132
119
|
)
|
133
120
|
for _, group in time_series.groupby("segment_id")
|
@@ -137,21 +124,21 @@ def make_geojson_color_line(time_series: pd.DataFrame) -> str:
|
|
137
124
|
return geojson.dumps(feature_collection)
|
138
125
|
|
139
126
|
|
140
|
-
def
|
141
|
-
low, high,
|
127
|
+
def make_color_bar(time_series: pd.Series, format: str) -> dict[str, Any]:
|
128
|
+
low, high, clamp_value = _make_value_clamp(time_series)
|
142
129
|
cmap = matplotlib.colormaps["viridis"]
|
143
130
|
colors = [
|
144
|
-
(f"{
|
145
|
-
for
|
131
|
+
(f"{value:{format}}", matplotlib.colors.to_hex(cmap(clamp_value(value))))
|
132
|
+
for value in np.linspace(low, high, 10)
|
146
133
|
]
|
147
134
|
return {"low": low, "high": high, "colors": colors}
|
148
135
|
|
149
136
|
|
150
|
-
def
|
151
|
-
|
152
|
-
low = min(
|
137
|
+
def _make_value_clamp(values: pd.Series) -> tuple[float, float, Callable]:
|
138
|
+
values_without_na = values.dropna()
|
139
|
+
low = min(values_without_na)
|
153
140
|
high = min(
|
154
|
-
max(
|
155
|
-
np.median(
|
141
|
+
max(values_without_na),
|
142
|
+
np.median(values_without_na) + 1.5 * inter_quartile_range(values_without_na),
|
156
143
|
)
|
157
|
-
return low, high, lambda
|
144
|
+
return low, high, lambda value: min(max((value - low) / (high - low), 0.0), 1.0)
|
@@ -10,8 +10,10 @@ import pandas as pd
|
|
10
10
|
import sqlalchemy
|
11
11
|
import sqlalchemy as sa
|
12
12
|
from flask_sqlalchemy import SQLAlchemy
|
13
|
+
from sqlalchemy import Column
|
13
14
|
from sqlalchemy import ForeignKey
|
14
15
|
from sqlalchemy import String
|
16
|
+
from sqlalchemy import Table
|
15
17
|
from sqlalchemy.orm import DeclarativeBase
|
16
18
|
from sqlalchemy.orm import Mapped
|
17
19
|
from sqlalchemy.orm import mapped_column
|
@@ -53,6 +55,13 @@ class Base(DeclarativeBase):
|
|
53
55
|
|
54
56
|
DB = SQLAlchemy(model_class=Base)
|
55
57
|
|
58
|
+
activity_tag_association_table = Table(
|
59
|
+
"activity_tag_association_table",
|
60
|
+
Base.metadata,
|
61
|
+
Column("left_id", ForeignKey("activities.id"), primary_key=True),
|
62
|
+
Column("right_id", ForeignKey("tags.id"), primary_key=True),
|
63
|
+
)
|
64
|
+
|
56
65
|
|
57
66
|
class Activity(DB.Model):
|
58
67
|
__tablename__ = "activities"
|
@@ -104,6 +113,10 @@ class Activity(DB.Model):
|
|
104
113
|
)
|
105
114
|
kind: Mapped["Kind"] = relationship(back_populates="activities")
|
106
115
|
|
116
|
+
tags: Mapped[list["Tag"]] = relationship(
|
117
|
+
secondary=activity_tag_association_table, back_populates="activities"
|
118
|
+
)
|
119
|
+
|
107
120
|
def __getitem__(self, item) -> Any:
|
108
121
|
return self.to_dict()[item]
|
109
122
|
|
@@ -112,12 +125,12 @@ class Activity(DB.Model):
|
|
112
125
|
|
113
126
|
@property
|
114
127
|
def average_speed_moving_kmh(self) -> Optional[float]:
|
115
|
-
if self.moving_time
|
128
|
+
if self.moving_time:
|
116
129
|
return self.distance_km / (self.moving_time.total_seconds() / 3_600)
|
117
130
|
|
118
131
|
@property
|
119
132
|
def average_speed_elapsed_kmh(self) -> Optional[float]:
|
120
|
-
if self.elapsed_time
|
133
|
+
if self.elapsed_time:
|
121
134
|
return self.distance_km / (self.elapsed_time.total_seconds() / 3_600)
|
122
135
|
|
123
136
|
@property
|
@@ -174,6 +187,74 @@ class Activity(DB.Model):
|
|
174
187
|
)
|
175
188
|
|
176
189
|
|
190
|
+
class Tag(DB.Model):
|
191
|
+
__tablename__ = "tags"
|
192
|
+
__table_args__ = (sa.UniqueConstraint("tag", name="tags_tag"),)
|
193
|
+
|
194
|
+
id: Mapped[int] = mapped_column(primary_key=True)
|
195
|
+
tag: Mapped[str] = mapped_column(String, unique=True)
|
196
|
+
|
197
|
+
activities: Mapped[list[Activity]] = relationship(
|
198
|
+
secondary=activity_tag_association_table, back_populates="tags"
|
199
|
+
)
|
200
|
+
|
201
|
+
|
202
|
+
def query_activity_meta() -> pd.DataFrame:
|
203
|
+
rows = DB.session.execute(
|
204
|
+
sqlalchemy.select(
|
205
|
+
Activity.id,
|
206
|
+
Activity.name,
|
207
|
+
Activity.path,
|
208
|
+
Activity.distance_km,
|
209
|
+
Activity.start,
|
210
|
+
Activity.elapsed_time,
|
211
|
+
Activity.moving_time,
|
212
|
+
Activity.start_latitude,
|
213
|
+
Activity.start_longitude,
|
214
|
+
Activity.end_latitude,
|
215
|
+
Activity.end_longitude,
|
216
|
+
Activity.elevation_gain,
|
217
|
+
Activity.start_elevation,
|
218
|
+
Activity.end_elevation,
|
219
|
+
Activity.calories,
|
220
|
+
Activity.steps,
|
221
|
+
Activity.num_new_tiles_14,
|
222
|
+
Activity.num_new_tiles_17,
|
223
|
+
Kind.consider_for_achievements,
|
224
|
+
Equipment.name.label("equipment"),
|
225
|
+
Kind.name.label("kind"),
|
226
|
+
)
|
227
|
+
.join(Activity.equipment)
|
228
|
+
.join(Activity.kind)
|
229
|
+
.order_by(Activity.start)
|
230
|
+
).all()
|
231
|
+
df = pd.DataFrame(rows)
|
232
|
+
|
233
|
+
for old, new in [
|
234
|
+
("elapsed_time", "average_speed_elapsed_kmh"),
|
235
|
+
("moving_time", "average_speed_moving_kmh"),
|
236
|
+
]:
|
237
|
+
df[new] = pd.NA
|
238
|
+
mask = df[old].dt.total_seconds() > 0
|
239
|
+
df.loc[mask, new] = df.loc[mask, "distance_km"] / (
|
240
|
+
df.loc[mask, old].dt.total_seconds() / 3_600
|
241
|
+
)
|
242
|
+
|
243
|
+
df["date"] = df["start"].dt.date
|
244
|
+
df["year"] = df["start"].dt.year
|
245
|
+
df["month"] = df["start"].dt.month
|
246
|
+
df["day"] = df["start"].dt.day
|
247
|
+
df["week"] = df["start"].dt.isocalendar().week
|
248
|
+
df["day_of_week"] = df["start"].dt.day_of_week
|
249
|
+
df["iso_year"] = df["start"].dt.isocalendar().year
|
250
|
+
df["hours"] = df["elapsed_time"].dt.total_seconds() / 3_600
|
251
|
+
df["hours_moving"] = df["moving_time"].dt.total_seconds() / 3_600
|
252
|
+
|
253
|
+
df.index = df["id"]
|
254
|
+
|
255
|
+
return df
|
256
|
+
|
257
|
+
|
177
258
|
class Equipment(DB.Model):
|
178
259
|
__tablename__ = "equipments"
|
179
260
|
|
@@ -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
|
@@ -271,9 +271,8 @@ def convert_strava_checkout(
|
|
271
271
|
continue
|
272
272
|
|
273
273
|
activity_date = dateutil.parser.parse(row["Activity Date"])
|
274
|
-
activity_name = row["Activity Name"]
|
274
|
+
activity_name: str = row["Activity Name"]
|
275
275
|
activity_kind = row["Activity Type"]
|
276
|
-
is_commute = row["Commute"] == "true" or row["Commute"] == True
|
277
276
|
equipment = (
|
278
277
|
nan_as_none(row["Activity Gear"])
|
279
278
|
or nan_as_none(row["Bike"])
|
@@ -285,8 +284,6 @@ def convert_strava_checkout(
|
|
285
284
|
activity_target = playground_path / "Activities" / str(activity_kind)
|
286
285
|
if equipment:
|
287
286
|
activity_target /= str(equipment)
|
288
|
-
if is_commute:
|
289
|
-
activity_target /= "Commute"
|
290
287
|
|
291
288
|
activity_target /= "".join(
|
292
289
|
[
|
@@ -294,7 +291,7 @@ def convert_strava_checkout(
|
|
294
291
|
"-",
|
295
292
|
f"{activity_date.hour:02d}-{activity_date.minute:02d}-{activity_date.second:02d}",
|
296
293
|
" ",
|
297
|
-
activity_name,
|
294
|
+
activity_name.replace("/", "_"),
|
298
295
|
]
|
299
296
|
+ activity_file.suffixes
|
300
297
|
)
|
@@ -28,7 +28,7 @@ from .blueprints.activity_blueprint import make_activity_blueprint
|
|
28
28
|
from .blueprints.auth_blueprint import make_auth_blueprint
|
29
29
|
from .blueprints.bubble_chart_blueprint import make_bubble_chart_blueprint
|
30
30
|
from .blueprints.calendar_blueprint import make_calendar_blueprint
|
31
|
-
from .blueprints.
|
31
|
+
from .blueprints.eddington_blueprints import register_eddington_blueprint
|
32
32
|
from .blueprints.entry_views import register_entry_views
|
33
33
|
from .blueprints.equipment_blueprint import make_equipment_blueprint
|
34
34
|
from .blueprints.explorer_blueprint import make_explorer_blueprint
|
@@ -137,7 +137,11 @@ def web_ui_main(
|
|
137
137
|
"/eddington": register_eddington_blueprint(repository, search_query_history),
|
138
138
|
"/equipment": make_equipment_blueprint(repository, config),
|
139
139
|
"/explorer": make_explorer_blueprint(
|
140
|
-
authenticator,
|
140
|
+
authenticator,
|
141
|
+
tile_visit_accessor,
|
142
|
+
config_accessor,
|
143
|
+
tile_getter,
|
144
|
+
image_transforms,
|
141
145
|
),
|
142
146
|
"/heatmap": make_heatmap_blueprint(
|
143
147
|
repository, tile_visit_accessor, config_accessor(), search_query_history
|
@@ -21,9 +21,9 @@ from PIL import Image
|
|
21
21
|
from PIL import ImageDraw
|
22
22
|
|
23
23
|
from ...core.activities import ActivityRepository
|
24
|
+
from ...core.activities import make_color_bar
|
24
25
|
from ...core.activities import make_geojson_color_line
|
25
26
|
from ...core.activities import make_geojson_from_time_series
|
26
|
-
from ...core.activities import make_speed_color_bar
|
27
27
|
from ...core.config import Config
|
28
28
|
from ...core.datamodel import Activity
|
29
29
|
from ...core.datamodel import DB
|
@@ -41,6 +41,8 @@ from ...explorer.grid_file import make_grid_points
|
|
41
41
|
from ...explorer.tile_visits import TileVisitAccessor
|
42
42
|
from ..authenticator import Authenticator
|
43
43
|
from ..authenticator import needs_authentication
|
44
|
+
from ..columns import column_elevation
|
45
|
+
from ..columns import column_speed
|
44
46
|
|
45
47
|
logger = logging.getLogger(__name__)
|
46
48
|
|
@@ -128,19 +130,34 @@ def make_activity_blueprint(
|
|
128
130
|
new_tiles_geojson[zoom] = make_grid_file_geojson(points)
|
129
131
|
new_tiles_per_zoom[zoom] = len(new_tiles)
|
130
132
|
|
133
|
+
line_color_columns_avail = dict(
|
134
|
+
[(column.name, column) for column in [column_speed, column_elevation]]
|
135
|
+
)
|
136
|
+
line_color_column = (
|
137
|
+
request.args.get("line_color_column")
|
138
|
+
or next(iter(line_color_columns_avail.values())).name
|
139
|
+
)
|
140
|
+
|
131
141
|
context = {
|
132
142
|
"activity": activity,
|
133
143
|
"line_json": line_json,
|
134
144
|
"distance_time_plot": distance_time_plot(time_series),
|
135
|
-
"color_line_geojson": make_geojson_color_line(
|
145
|
+
"color_line_geojson": make_geojson_color_line(
|
146
|
+
time_series, line_color_column
|
147
|
+
),
|
136
148
|
"speed_time_plot": speed_time_plot(time_series),
|
137
149
|
"speed_distribution_plot": speed_distribution_plot(time_series),
|
138
150
|
"similar_activites": similar_activities,
|
139
|
-
"
|
151
|
+
"line_color_bar": make_color_bar(
|
152
|
+
time_series[line_color_column],
|
153
|
+
line_color_columns_avail[line_color_column].format,
|
154
|
+
),
|
140
155
|
"date": activity["start"].date(),
|
141
156
|
"time": activity["start"].time(),
|
142
157
|
"new_tiles": new_tiles_per_zoom,
|
143
158
|
"new_tiles_geojson": new_tiles_geojson,
|
159
|
+
"line_color_column": line_color_column,
|
160
|
+
"line_color_columns_avail": line_color_columns_avail,
|
144
161
|
}
|
145
162
|
if (
|
146
163
|
heart_zones := _extract_heart_rate_zones(
|
geo_activity_playground-0.41.0/geo_activity_playground/webui/blueprints/bubble_chart_blueprint.py
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
import altair as alt
|
2
|
+
import pandas as pd
|
3
|
+
from flask import Blueprint
|
4
|
+
from flask import render_template
|
5
|
+
|
6
|
+
from ..columns import column_distance
|
7
|
+
from ..columns import column_elevation_gain
|
8
|
+
from ..columns import ColumnDescription
|
9
|
+
|
10
|
+
|
11
|
+
def make_bubble_chart_blueprint(repository) -> Blueprint:
|
12
|
+
blueprint = Blueprint("bubble_chart", __name__, template_folder="templates")
|
13
|
+
|
14
|
+
@blueprint.route("/", endpoint="index")
|
15
|
+
def bubble_chart():
|
16
|
+
activities = repository.meta
|
17
|
+
|
18
|
+
# Ensure 'activity_id' exists in the activities DataFrame
|
19
|
+
if "activity_id" not in activities.columns:
|
20
|
+
activities["activity_id"] = (
|
21
|
+
activities.index
|
22
|
+
) # Use index as fallback if missing
|
23
|
+
|
24
|
+
# Prepare the bubble chart data
|
25
|
+
bubble_data = activities[
|
26
|
+
[
|
27
|
+
"start",
|
28
|
+
"kind",
|
29
|
+
"activity_id",
|
30
|
+
column_distance.name,
|
31
|
+
column_elevation_gain.name,
|
32
|
+
]
|
33
|
+
].rename(
|
34
|
+
columns={
|
35
|
+
"start": "date",
|
36
|
+
"kind": "activity",
|
37
|
+
"activity_id": "id",
|
38
|
+
}
|
39
|
+
)
|
40
|
+
bubble_data["date"] = pd.to_datetime(bubble_data["date"]).dt.date
|
41
|
+
bubble_data["activity_url"] = bubble_data["id"].apply(
|
42
|
+
lambda x: f"/activity/{x}"
|
43
|
+
)
|
44
|
+
|
45
|
+
return render_template(
|
46
|
+
"bubble_chart/index.html.j2",
|
47
|
+
bubble_chart_distance=_make_bubble_chart(bubble_data, column_distance),
|
48
|
+
bubble_chart_elevation_gain=_make_bubble_chart(
|
49
|
+
bubble_data, column_elevation_gain
|
50
|
+
),
|
51
|
+
)
|
52
|
+
|
53
|
+
return blueprint
|
54
|
+
|
55
|
+
|
56
|
+
def _make_bubble_chart(bubble_data, column: ColumnDescription):
|
57
|
+
return (
|
58
|
+
alt.Chart(bubble_data, title=f"{column.display_name} per Day (Bubble Chart)")
|
59
|
+
.mark_circle()
|
60
|
+
.encode(
|
61
|
+
x=alt.X("date:T", title="Date"),
|
62
|
+
y=alt.Y(
|
63
|
+
f"{column.name}:Q",
|
64
|
+
title=f"{column.display_name} ({column.unit})",
|
65
|
+
),
|
66
|
+
size=alt.Size(
|
67
|
+
f"{column.name}:Q",
|
68
|
+
scale=alt.Scale(range=[10, 300]),
|
69
|
+
title=f"{column.display_name}",
|
70
|
+
),
|
71
|
+
color=alt.Color("activity:N", title="Activity"),
|
72
|
+
tooltip=[
|
73
|
+
alt.Tooltip("date:T", title="Date"),
|
74
|
+
alt.Tooltip(
|
75
|
+
f"{column.name}:Q",
|
76
|
+
title=f"{column.display_name} ({column.unit})",
|
77
|
+
format=column.format,
|
78
|
+
),
|
79
|
+
alt.Tooltip("activity:N", title="Activity"),
|
80
|
+
alt.Tooltip("activity_url:N", title="Activity Link"),
|
81
|
+
],
|
82
|
+
)
|
83
|
+
.properties(height=800, width=1200)
|
84
|
+
.interactive()
|
85
|
+
.to_json(format="vega")
|
86
|
+
)
|
@@ -1,10 +1,14 @@
|
|
1
1
|
import collections
|
2
2
|
import datetime
|
3
3
|
|
4
|
+
import pandas as pd
|
5
|
+
import sqlalchemy
|
4
6
|
from flask import Blueprint
|
5
7
|
from flask import render_template
|
6
8
|
|
7
9
|
from ...core.activities import ActivityRepository
|
10
|
+
from ...core.datamodel import Activity
|
11
|
+
from ...core.datamodel import DB
|
8
12
|
|
9
13
|
|
10
14
|
def make_calendar_blueprint(repository: ActivityRepository) -> Blueprint:
|
@@ -12,9 +16,14 @@ def make_calendar_blueprint(repository: ActivityRepository) -> Blueprint:
|
|
12
16
|
|
13
17
|
@blueprint.route("/")
|
14
18
|
def index():
|
15
|
-
|
19
|
+
data = DB.session.execute(
|
20
|
+
sqlalchemy.select(Activity.start, Activity.distance_km)
|
21
|
+
).all()
|
22
|
+
df = pd.DataFrame(data)
|
23
|
+
df["year"] = df["start"].dt.year
|
24
|
+
df["month"] = df["start"].dt.month
|
16
25
|
|
17
|
-
monthly_distance =
|
26
|
+
monthly_distance = df.groupby(
|
18
27
|
["year", "month"],
|
19
28
|
).apply(lambda group: sum(group["distance_km"]), include_groups=False)
|
20
29
|
monthly_distance.name = "total_distance_km"
|
@@ -24,7 +33,7 @@ def make_calendar_blueprint(repository: ActivityRepository) -> Blueprint:
|
|
24
33
|
.fillna(0.0)
|
25
34
|
)
|
26
35
|
|
27
|
-
yearly_distance =
|
36
|
+
yearly_distance = df.groupby(["year"]).apply(
|
28
37
|
lambda group: sum(group["distance_km"]), include_groups=False
|
29
38
|
)
|
30
39
|
yearly_distance.name = "total_distance_km"
|
@@ -34,7 +43,6 @@ def make_calendar_blueprint(repository: ActivityRepository) -> Blueprint:
|
|
34
43
|
}
|
35
44
|
|
36
45
|
context = {
|
37
|
-
"num_activities": len(repository),
|
38
46
|
"monthly_distances": monthly_pivot,
|
39
47
|
"yearly_distances": yearly_distances,
|
40
48
|
}
|