Precompute run analysis and serve compact route previews

This commit is contained in:
Peter Stockings
2026-10-02 14:19:47 +10:00
parent 2d7069c12d
commit 6bfab483d4
13 changed files with 518 additions and 56 deletions
+19 -40
View File
@@ -6,6 +6,7 @@ from pathlib import Path
from garmin_sync.service import SyncError, complete, decode_fit
from .display import ANALYSIS_VERSION, persist_analysis
from .extensions import db
from .models import Activity
from .records import persist_efforts
@@ -112,6 +113,7 @@ def activity_from_fit(owner_id, source_key, documents, metadata=None, refresh=Fa
if existing:
existing.samples, existing.laps, existing.metrics = samples, laps, metrics
persist_efforts(existing)
persist_analysis(existing)
return existing
row = Activity(
owner_id=owner_id,
@@ -128,6 +130,7 @@ def activity_from_fit(owner_id, source_key, documents, metadata=None, refresh=Fa
laps=laps,
)
persist_efforts(row)
persist_analysis(row)
db.session.add(row)
return row
@@ -159,44 +162,20 @@ def import_upload(user, payload):
def activity_data(activity, owner_view=False):
"""Call only after access checks. Owners and accepted friends see the full route."""
from .analytics import analyse
"""Read compact persisted data only, after the caller's access checks."""
if activity.analysis_version != ANALYSIS_VERSION:
return {"segments": [], "route_segments": [], "chart": []}
data = activity.display_data
routes = data.get("route_segments", [])
return {
"segments": [[sample["point"] for sample in segment] for segment in routes],
"route_segments": routes,
"chart": data.get("chart", []),
}
analysis = analyse(activity)
segments, segment = [], []
route_segments, route = [], []
for sample in analysis["samples"]:
point = sample.get("point")
if point:
segment.append(point)
route.append(sample)
elif segment:
segments.append(segment)
segment = []
route_segments.append(route)
route = []
if segment:
segments.append(segment)
route_segments.append(route)
step = max(1, math.ceil(len(activity.samples) / 750))
chart = [
{
k: sample.get(k)
for k in (
"distance",
"heart_rate",
"speed",
"elevation",
"grade",
"gap",
"pace",
"cadence",
"power",
"stride_length",
"vertical_oscillation",
"ground_contact",
)
}
for sample in analysis["samples"][::step]
]
return {"segments": segments, "route_segments": route_segments, "chart": chart}
def route_data(activity):
"""Dashboard preview: no measurements, charts or full-resolution samples."""
return {
"segments": activity.route_geometry if activity.analysis_version == ANALYSIS_VERSION else []
}