Strava sync: skip import when a FIT-file upload already covers the same start time
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.
This commit is contained in:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user