From 58a5d5b4507cd04eed321c3d956dfecc57d0f0c7 Mon Sep 17 00:00:00 2001 From: Davide Scaini Date: Wed, 13 May 2026 21:52:07 +0200 Subject: [PATCH] Strava sync: skip import when a FIT-file upload already covers the same start time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before importing each Strava activity, build a set of existing timestamp prefixes (YYYY-MM-DDTHHMMSSZ) from the activities directory. If the incoming Strava activity matches an existing prefix, record its Strava ID as done and skip — preventing duplicate entries when a FIT file and a Strava sync both cover the same ride. Also reports skipped-existing count in the summary line. --- bincio/import_/strava.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/bincio/import_/strava.py b/bincio/import_/strava.py index 83cbdce..6cd0e84 100644 --- a/bincio/import_/strava.py +++ b/bincio/import_/strava.py @@ -343,9 +343,24 @@ def sync( owner = index_data.get("owner", {}) summaries: dict[str, dict] = {s["id"]: s for s in index_data.get("activities", [])} + # ── build timestamp-prefix index of existing activities ────────────────── + # Maps "YYYY-MM-DDTHHMMSSZ" → first matching activity filename (stem). + # Used to detect when a FIT-file upload already covers a Strava activity. + acts_dir = output_dir / "activities" + existing_ts: set[str] = set() + if acts_dir.is_dir(): + for p in acts_dir.iterdir(): + if p.suffix == ".json" and not p.name.endswith(".timeseries.json"): + stem = p.stem + # ID format: YYYY-MM-DDTHHMMSSZ[-optional-slug] + z_pos = stem.find("Z") + if z_pos != -1: + existing_ts.add(stem[: z_pos + 1]) + # ── import loop ─────────────────────────────────────────────────────────── errors: list[tuple[str, str]] = [] imported = 0 + skipped_existing = 0 with Progress( TextColumn("[progress.description]{task.description}"), @@ -362,11 +377,20 @@ def sync( try: streams = client.get_streams(act["id"]) parsed = _strava_to_parsed(act, streams) + + # Skip if any activity already exists for the same start time + ts_part = parsed.started_at.astimezone(timezone.utc).strftime("%Y-%m-%dT%H%M%SZ") + if ts_part in existing_ts: + imported_ids.add(strava_id) + skipped_existing += 1 + continue + metrics = compute(parsed) metrics = _patch_from_summary(metrics, act) act_id = make_activity_id(parsed) write_activity(parsed, metrics, output_dir, privacy="public") summaries[act_id] = build_summary(parsed, metrics, act_id, "public") + existing_ts.add(ts_part) imported_ids.add(strava_id) imported += 1 except Exception as exc: @@ -384,9 +408,11 @@ def sync( from bincio.render.merge import merge_all merge_all(output_dir) + skipped_msg = f", skipped [bold]{skipped_existing}[/bold] already covered by local uploads" if skipped_existing else "" console.print( f"\n[green]Done.[/green] " - f"Imported [bold]{imported}[/bold] activities, " + f"Imported [bold]{imported}[/bold] activities" + f"{skipped_msg}, " f"errors [bold]{len(errors)}[/bold]." ) if errors: