fix high priority issues

This commit is contained in:
Davide Scaini
2026-03-31 22:53:50 +02:00
parent e2870c3344
commit f8abab2c23
3 changed files with 40 additions and 16 deletions
+26 -11
View File
@@ -112,10 +112,11 @@ def compute_mmp(pts: list[DataPoint], started_at: datetime) -> Optional[list[lis
[duration_s, avg_watts] pairs (integers), or None when the activity has no
power data. Only durations shorter than the total activity are included.
"""
# 1 Hz downsample: at most one sample per second, skip sub-second duplicates.
# Seconds without a recorded sample are omitted (not zero-filled) so that
# paused-recording gaps don't silently lower power averages.
power_1hz: list[int] = []
# Build a dense 1 Hz power array with gaps zero-filled.
# Zero-filling is the standard approach (matches GoldenCheetah / WKO):
# a recording gap counts as 0 W so windows cannot silently span pauses
# and inflate MMP values.
sparse: dict[int, int] = {}
last_t = -1
for p in pts:
t = int((p.timestamp - started_at).total_seconds())
@@ -123,11 +124,15 @@ def compute_mmp(pts: list[DataPoint], started_at: datetime) -> Optional[list[lis
continue
last_t = t
if p.power_w is not None:
power_1hz.append(p.power_w)
sparse[t] = p.power_w
if len(power_1hz) < 2:
if len(sparse) < 2:
return None
t_min = min(sparse)
t_max = max(sparse)
power_1hz: list[int] = [sparse.get(t, 0) for t in range(t_min, t_max + 1)]
n = len(power_1hz)
results: list[list[int]] = []
@@ -166,17 +171,27 @@ def compute_best_efforts(
"""
targets = BEST_EFFORT_DISTANCES.get(sport, [])
# Build 1 Hz speed (km/h) and elevation (m) arrays — same downsampling as timeseries.py
speed_1hz: list[float] = []
ele_1hz: list[Optional[float]] = []
# Build dense 1 Hz speed (km/h) and elevation (m) arrays with gap zero-filling.
# Zero-filling speed gaps (0 km/h) prevents best-effort windows from spanning
# recording pauses and producing artificially fast times.
sparse_speed: dict[int, float] = {}
sparse_ele: dict[int, Optional[float]] = {}
last_t = -1
for p in pts:
t = int((p.timestamp - started_at).total_seconds())
if t < 0 or t == last_t:
continue
last_t = t
speed_1hz.append(p.speed_kmh if p.speed_kmh is not None else 0.0)
ele_1hz.append(p.elevation_m)
sparse_speed[t] = p.speed_kmh if p.speed_kmh is not None else 0.0
sparse_ele[t] = p.elevation_m
if not sparse_speed:
return None, None
t_min = min(sparse_speed)
t_max = max(sparse_speed)
speed_1hz: list[float] = [sparse_speed.get(t, 0.0) for t in range(t_min, t_max + 1)]
ele_1hz: list[Optional[float]] = [sparse_ele.get(t) for t in range(t_min, t_max + 1)]
best_efforts: Optional[list[list[float]]] = None
if targets and speed_1hz:
+7
View File
@@ -83,6 +83,13 @@ def write_activity(
}
json_path = acts_dir / f"{activity_id}.json"
# Collision guard: if a *different* activity already has this ID, append a
# short hash suffix to disambiguate (same hash = idempotent re-extract).
if json_path.exists():
existing = json.loads(json_path.read_text(encoding="utf-8"))
if existing.get("source_hash") != activity.source_hash:
activity_id = f"{activity_id}-{activity.source_hash[-6:]}"
json_path = acts_dir / f"{activity_id}.json"
json_path.write_text(json.dumps(detail, indent=2, ensure_ascii=False))
# ── GeoJSON track ────────────────────────────────────────────────────────