INITIAL COMMIT

This commit is contained in:
2026-06-16 15:14:37 +02:00
commit 1477ec36fd
49 changed files with 6835 additions and 0 deletions

110
scripts/analyze_dump.py Executable file
View File

@ -0,0 +1,110 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from typing import Any
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from garmin_coach_clone.coach import (
best_workout_for_date,
find_date_matches,
find_workout_like_objects,
)
from garmin_coach_clone.dates import parse_date
from garmin_coach_clone.io import dump_redacted_json, print_json
from garmin_coach_clone.workouts import (
clone_workout_payload,
summarize_workout,
validate_workout_payload,
)
def main() -> int:
parser = argparse.ArgumentParser(description="Analyze saved Garmin JSON without logging in.")
parser.add_argument("json_file", type=Path, help="Path to a saved JSON dump.")
parser.add_argument("--date", default="today", help="today, tomorrow, or YYYY-MM-DD")
parser.add_argument(
"--clone-dry-run",
action="store_true",
help="Build a local clone payload from the matched workout.",
)
parser.add_argument("--prefix", default="GCClone", help="Clone workout name prefix.")
parser.add_argument(
"--dump-json", action="store_true", help="Write analysis JSON under debug/."
)
args = parser.parse_args()
target_date = parse_date(args.date)
data = _load_json(args.json_file)
date_matches = find_date_matches(data, target_date)
workout_like = find_workout_like_objects(data)
matched = best_workout_for_date(data, target_date)
analysis: dict[str, Any] = {
"source": str(args.json_file),
"target_date": target_date.isoformat(),
"date_match_count": len(date_matches),
"workout_like_count": len(workout_like),
"matched_workout": matched is not None,
}
print(f"Source: {args.json_file}")
print(f"Target date: {target_date.isoformat()}")
print(f"Date-containing objects: {len(date_matches)}")
print(f"Workout-like objects with workoutSegments/workoutSteps: {len(workout_like)}")
if matched is None:
print("No dated workout-like object was found.")
if args.dump_json:
out = _analysis_path(args.json_file)
dump_redacted_json(out, analysis)
print(f"Analysis JSON written to {out}")
return 1
print("\nMatched workout summary:")
print(summarize_workout(matched))
if args.clone_dry_run:
payload = clone_workout_payload(matched, target_date, args.prefix)
errors = validate_workout_payload(payload)
analysis["clone_validation_errors"] = errors
analysis["clone_payload"] = payload
if errors:
print("\nLocal clone payload failed validation:")
for error in errors:
print(f" - {error}")
return_code = 1
else:
print("\nLocal clone payload passed validation.")
print_json(payload)
return_code = 0
else:
return_code = 0
if args.dump_json:
out = _analysis_path(args.json_file)
dump_redacted_json(out, analysis)
print(f"Analysis JSON written to {out}")
return return_code
def _load_json(path: Path) -> Any:
try:
return json.loads(path.read_text(encoding="utf-8"))
except FileNotFoundError:
raise SystemExit(f"File not found: {path}") from None
except json.JSONDecodeError as exc:
raise SystemExit(f"Invalid JSON in {path}: {exc}") from None
def _analysis_path(source: Path) -> Path:
stem = source.stem.replace(".", "_")
return Path("debug") / f"analysis_{stem}.json"
if __name__ == "__main__":
raise SystemExit(main())