refactor: extract/ingest facade, merge_one, deduplicate ops constants
- Add bincio/extract/ingest.py as a facade over the extract internals (ingest_parsed, strava_sync), reducing coupling from 6+ imports to one - Add merge_one() to merge.py — fast single-activity path for interactive edits (rewrites one file + index, skips full directory rebuild) - Rewrite edit/ops.py to delegate to the new facade; fix broken run_strava_sync return (was referencing undefined locals) - Remove duplicated SPORTS, STAT_PANELS, VALID_ACTIVITY_ID from edit/server.py — now imported from ops.py
This commit is contained in:
+10
-68
@@ -7,12 +7,15 @@ No FastAPI, no globals — all context is passed as explicit arguments.
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import time
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
# ── Shared constants (imported by edit/server.py and serve/server.py) ─────────
|
||||
|
||||
SPORTS = ["cycling", "running", "hiking", "walking", "swimming", "skiing", "other"]
|
||||
STAT_PANELS = ["elevation", "speed", "heart_rate", "cadence", "power"]
|
||||
VALID_ACTIVITY_ID = re.compile(r'^[a-zA-Z0-9][a-zA-Z0-9\-]{0,250}$')
|
||||
|
||||
|
||||
def apply_sidecar_edit(activity_id: str, payload: dict[str, Any], data_dir: Path) -> None:
|
||||
@@ -51,8 +54,8 @@ def apply_sidecar_edit(activity_id: str, payload: dict[str, Any], data_dir: Path
|
||||
|
||||
sidecar_path.write_text(content, encoding="utf-8")
|
||||
|
||||
from bincio.render.merge import merge_all
|
||||
merge_all(data_dir)
|
||||
from bincio.render.merge import merge_one
|
||||
merge_one(data_dir, activity_id)
|
||||
|
||||
|
||||
def run_strava_sync(data_dir: Path, client_id: str, client_secret: str) -> dict[str, Any]:
|
||||
@@ -69,72 +72,11 @@ def run_strava_sync(data_dir: Path, client_id: str, client_secret: str) -> dict[
|
||||
Raises:
|
||||
RuntimeError: If Strava credentials are missing or API calls fail.
|
||||
"""
|
||||
if not client_id or not client_secret:
|
||||
raise RuntimeError("Strava not configured (missing client_id or client_secret)")
|
||||
|
||||
from bincio.extract.strava_api import (
|
||||
StravaError,
|
||||
ensure_fresh,
|
||||
fetch_activities,
|
||||
fetch_streams,
|
||||
save_token,
|
||||
strava_meta_to_partial,
|
||||
strava_to_parsed,
|
||||
)
|
||||
|
||||
try:
|
||||
token = ensure_fresh(data_dir, client_id, client_secret)
|
||||
except StravaError as e:
|
||||
raise RuntimeError(str(e)) from e
|
||||
|
||||
after: int | None = token.get("last_sync_at")
|
||||
try:
|
||||
activities = fetch_activities(token["access_token"], after=after)
|
||||
except StravaError as e:
|
||||
raise RuntimeError(str(e)) from e
|
||||
|
||||
from bincio.extract.metrics import compute
|
||||
from bincio.extract.writer import build_summary, make_activity_id, write_activity, write_index
|
||||
from bincio.extract.ingest import strava_sync as _strava_sync
|
||||
from bincio.render.merge import merge_all
|
||||
|
||||
index_path = data_dir / "index.json"
|
||||
if index_path.exists():
|
||||
index_data = json.loads(index_path.read_text(encoding="utf-8"))
|
||||
else:
|
||||
index_data = {"owner": {"handle": "unknown"}, "activities": []}
|
||||
owner = index_data.get("owner", {})
|
||||
summaries: dict[str, dict] = {s["id"]: s for s in index_data.get("activities", [])}
|
||||
|
||||
imported = 0
|
||||
skipped = 0
|
||||
errors: list[str] = []
|
||||
|
||||
for meta in activities:
|
||||
try:
|
||||
activity_id = make_activity_id(strava_meta_to_partial(meta))
|
||||
if (data_dir / "activities" / f"{activity_id}.json").exists():
|
||||
skipped += 1
|
||||
continue
|
||||
streams = fetch_streams(token["access_token"], meta["id"])
|
||||
parsed = strava_to_parsed(meta, streams)
|
||||
metrics = compute(parsed)
|
||||
write_activity(parsed, metrics, data_dir, privacy="public", rdp_epsilon=0.0001)
|
||||
summaries[activity_id] = build_summary(parsed, metrics, activity_id, "public")
|
||||
imported += 1
|
||||
except Exception as exc:
|
||||
errors.append(f"{meta.get('id')}: {type(exc).__name__}")
|
||||
|
||||
if imported:
|
||||
write_index(list(summaries.values()), data_dir, owner)
|
||||
result = _strava_sync(data_dir, client_id, client_secret)
|
||||
if result["imported"]:
|
||||
merge_all(data_dir)
|
||||
|
||||
token["last_sync_at"] = int(time.time())
|
||||
save_token(data_dir, token)
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"imported": imported,
|
||||
"skipped": skipped,
|
||||
"error_count": len(errors),
|
||||
"errors": errors[:5],
|
||||
}
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user