from core.intelligence.runner_intelligence_snapshot import RunnerIntelligenceSnapshot def build_intelligence_snapshot(context) -> RunnerIntelligenceSnapshot: """ Builds a RunnerIntelligenceSnapshot from a PipelineContext. This is an aggregation layer only. It uses safe accessors (`getattr`) to extract already-computed values without introducing new business logic. """ summary = context.summary # Helper to safely extract depending on whether summary is a dict, WeeklySnapshot, or WeeklySummary def _extract_summary_val(dict_key, attr_names, default, transform=None): if not summary: return default val = default if isinstance(summary, dict): val = summary.get(dict_key, default) else: for attr in attr_names: if hasattr(summary, attr): val = getattr(summary, attr, default) break return transform(val) if transform and val != default else val return RunnerIntelligenceSnapshot( week_start=_extract_summary_val("week_start", ["week_start_date", "week_start"], None), training_state=getattr(context, "training_state", None), health_signal=getattr(context, "health_signal", None), positioning_status=getattr(context, "positioning_status", None), positioning_change=getattr(context, "positioning_change", None), goal_trajectory=getattr(context, "goal_trajectory", None), goal_progress_pct=getattr(context, "goal_progress_pct", None), next_run=getattr(context, "next_run", None), training_focus=getattr(context, "training_focus", None), key_insight=getattr(context, "key_insight", None), forward_focus=getattr(context, "forward_focus", None), weekly_distance_km=_extract_summary_val( "total_distance_m", ["total_distance_km", "total_distance_m"], 0.0, transform=lambda x: x / 1000.0 if not hasattr(summary, "total_distance_km") else x ), run_count=_extract_summary_val("num_runs", ["run_count", "num_runs"], 0), consistency_score=_extract_summary_val("consistency_score", ["consistency_score"], 0), )