"""Import metadata from Strava's activities.csv bulk export. Strava export columns we care about: Activity ID, Activity Date, Activity Name, Activity Type, Activity Description, Filename """ import csv import re from pathlib import Path from typing import Optional _STRAVA_DATE_FMTS = ( "%b %d, %Y, %I:%M:%S %p", # "Jun 1, 2024, 7:30:12 AM" "%Y-%m-%d %H:%M:%S", ) class StravaMetadata: """Maps original filename → Strava metadata.""" def __init__(self, csv_path: Path) -> None: self._by_filename: dict[str, dict] = {} self._load(csv_path) def _load(self, path: Path) -> None: with path.open(newline="", encoding="utf-8-sig") as f: reader = csv.DictReader(f) for row in reader: filename = row.get("Filename", "").strip() if not filename: continue # Strava stores paths like "activities/12345.fit.gz" basename = Path(filename).name self._by_filename[basename] = row def lookup(self, source_file: str) -> Optional[dict]: """Return the Strava CSV row for a given source filename, or None.""" return self._by_filename.get(source_file) def enrich(self, source_file: str, activity: object) -> None: """Mutate a ParsedActivity with Strava metadata if found.""" row = self.lookup(source_file) if row is None: return if not activity.title and row.get("Activity Name"): # type: ignore[attr-defined] activity.title = row["Activity Name"].strip() # type: ignore[attr-defined] if not activity.description and row.get("Activity Description"): # type: ignore[attr-defined] activity.description = row["Activity Description"].strip() # type: ignore[attr-defined] if not activity.strava_id and row.get("Activity ID"): # type: ignore[attr-defined] activity.strava_id = row["Activity ID"].strip() # type: ignore[attr-defined]