Spaces:
Runtime error
Runtime error
Download src/application/positioning_service.py from avfranco/runner-ai-intelligence: direct link, hf CLI and curl.
- Browser
- Download file 7.46 kB
-
https://huggingface.co/spaces/avfranco/runner-ai-intelligence/resolve/main/src/application/positioning_service.py
- Command line
-
hf download hf://spaces/avfranco/runner-ai-intelligence/src/application/positioning_service.py
-
curl -L -o positioning_service.py https://huggingface.co/spaces/avfranco/runner-ai-intelligence/resolve/main/src/application/positioning_service.py
7.46 kB
| from typing import Optional, Dict, List, Literal | |
| from pydantic import BaseModel, Field | |
| from datetime import date | |
| from domain.training.weekly_snapshot import WeeklySnapshot | |
| from domain.training.weekly_trend import WeeklyTrend | |
| from domain.runner_positioning import TrainingPhase | |
| from _app.presentation.ui_text import get_text | |
| class WeeklyPositioning(BaseModel): | |
| """ | |
| Application-layer model for Positioning Intelligence v1. | |
| Narrative-focused assessment of the runner's current standing. | |
| """ | |
| status: Literal["CONSTRUCTIVE_ADAPTATION", "PRODUCTIVE_LOAD", "STRAIN", "PLATEAU", "BASELINE_BUILDING"] | |
| signal_strength: float = 1.0 # 0.0 to 1.0 | |
| rationale: str | |
| training_phase: TrainingPhase = TrainingPhase.BASE | |
| def status_value(self) -> str: | |
| return self.status | |
| class PositioningEngine: | |
| """ | |
| Engine to compute high-level positioning status from snapshots and trends. | |
| """ | |
| def detect_training_phase(self, snapshot: WeeklySnapshot, trend: WeeklyTrend) -> TrainingPhase: | |
| """ | |
| Infer training phase from workload and trend signals. | |
| """ | |
| distance_delta = trend.distance_delta_pct or 0 | |
| pace_delta = trend.pace_delta_s_per_km or 0 | |
| run_count = snapshot.run_count | |
| # consistency = snapshot.consistency_score or 0 | |
| # Recovery phase (very low activity and sharp drop in load) | |
| if run_count <= 1 and distance_delta < -80: | |
| return TrainingPhase.RECOVERY | |
| # Plateau phase (load dropped or adaptation stalled) | |
| if distance_delta < -20: | |
| return TrainingPhase.PLATEAU | |
| # Build phase (clear volume increase) | |
| if distance_delta > 5: | |
| return TrainingPhase.BUILD | |
| # Peak phase (performance improving without load reduction) | |
| if pace_delta < -5 and distance_delta >= 0: | |
| return TrainingPhase.PEAK | |
| # Default: base training | |
| return TrainingPhase.BASE | |
| def compute(self, snapshot: WeeklySnapshot, trend: WeeklyTrend) -> WeeklyPositioning: | |
| if not trend or not trend.comparison_available: | |
| return WeeklyPositioning( | |
| status="BASELINE_BUILDING", | |
| rationale="positioning_rationale_building", | |
| signal_strength=0.1, | |
| training_phase=TrainingPhase.BASE | |
| ) | |
| # 1. Logic to determine status | |
| # This is a v1 heuristic mapping | |
| status = "PLATEAU" | |
| rationale_key = "positioning_rationale_plateau" | |
| if trend.comparison_available: | |
| # Constructive Adaptation: Increasing load with positive/stable consistency | |
| if trend.distance_delta_pct > 5.0 and trend.consistency_delta >= -5.0: | |
| status = "CONSTRUCTIVE_ADAPTATION" | |
| rationale_key = "positioning_rationale_constructive_adaptation" | |
| # Strain: High load increase or high HR increase | |
| elif trend.distance_delta_pct > 20.0 or (trend.hr_delta and trend.hr_delta > 5.0): | |
| status = "STRAIN" | |
| rationale_key = "positioning_rationale_strain" | |
| # Productive Load: Stable or slightly increasing load | |
| elif trend.distance_delta_pct >= -5.0: | |
| status = "PRODUCTIVE_LOAD" | |
| rationale_key = "positioning_rationale_productive_load" | |
| else: | |
| status = "BASELINE_BUILDING" | |
| rationale_key = "positioning_rationale_building" | |
| training_phase = self.detect_training_phase(snapshot, trend) | |
| return WeeklyPositioning( | |
| status=status, | |
| rationale=rationale_key, # Store key here | |
| signal_strength=1.0 if trend.comparison_available else 0.5, | |
| training_phase=training_phase | |
| ) | |
| def build_positioning_view( | |
| snapshot: WeeklySnapshot, | |
| trend: WeeklyTrend, | |
| positioning: WeeklyPositioning, | |
| goal_progress: Optional[dict] = None, | |
| language: str = "en" | |
| ) -> dict: | |
| """ | |
| Builds a UI-ready positioning view using BOTH absolute state (snapshot) | |
| and relative changes (trend). This restores coaching-quality output. | |
| """ | |
| positioning = PositioningEngine().compute(snapshot, trend) | |
| # --- Absolute context (snapshot) --- | |
| total_distance = snapshot.total_distance_km or 0 | |
| run_count = snapshot.run_count or 0 | |
| avg_pace = snapshot.avg_pace_sec_per_km | |
| avg_hr = snapshot.avg_hr | |
| # --- Relative context (trend) --- | |
| distance_delta = trend.distance_delta_pct or 0 | |
| pace_delta = trend.pace_delta_s_per_km or 0 | |
| hr_delta = trend.hr_delta or 0 | |
| freq_delta = int(trend.frequency_delta or 0) | |
| consistency_delta = trend.consistency_delta or 0 | |
| # --- Headline (context-aware) --- | |
| if run_count <= 1: | |
| headline = get_text("positioning_headline_building", language) | |
| elif total_distance < 5: | |
| headline = f"Your training volume is currently low at {total_distance:.1f} km." | |
| else: | |
| status_lower = positioning.status.lower() | |
| headline = get_text(f"positioning_headline_{status_lower}", language) | |
| # --- State --- | |
| status_icons = { | |
| "CONSTRUCTIVE_ADAPTATION": "π’", | |
| "PRODUCTIVE_LOAD": "π‘", | |
| "STRAIN": "π΄", | |
| "PLATEAU": "βͺ", | |
| "BASELINE_BUILDING": "βͺ", | |
| } | |
| status_icon = status_icons.get(positioning.status, "βͺ") | |
| status_name = get_text(f"positioning_status_{positioning.status.lower()}", language) | |
| # --- Health --- | |
| if hr_delta > 5: | |
| health_signal = f"π΄ {get_text('health_strain', language)}" | |
| else: | |
| health_signal = f"π’ {get_text('health_stable', language)}" | |
| # --- Goal trajectory --- | |
| if goal_progress: | |
| if positioning.status == "BASELINE_BUILDING": | |
| goal_trajectory = get_text("trajectory_establishing", language) | |
| elif positioning.status == "CONSTRUCTIVE_ADAPTATION": | |
| goal_trajectory = f"π― {get_text('trajectory_improving', language)}" | |
| else: | |
| goal_trajectory = f"π― {get_text('trajectory_maintaining', language)}" | |
| else: | |
| goal_trajectory = get_text("trajectory_no_goal", language) | |
| # --- Forward focus --- | |
| forward_focus = get_text(f"positioning_forward_focus_{positioning.status.lower()}", language) | |
| # --- Narrative insight --- | |
| insight = get_text(positioning.rationale, language) | |
| # --- Evidence (enriched with absolute + relative) --- | |
| evidence = { | |
| "distance": f"{total_distance:.1f} km ({distance_delta:+.1f}%)", | |
| "pace": f"{pace_delta:+.1f} sec/km" if pace_delta else None, | |
| "hr": f"{hr_delta:+.1f} bpm" if hr_delta else None, | |
| "frequency": f"{run_count} runs ({freq_delta:+d})", | |
| "consistency": f"{int(consistency_delta):+d}", | |
| } | |
| # Remove empty evidence fields | |
| evidence = {k: v for k, v in evidence.items() if v is not None} | |
| return { | |
| "headline": headline, | |
| "state": f"{status_icon} {status_name}", | |
| "health_signal": health_signal, | |
| "goal_trajectory": goal_trajectory, | |
| "training_phase": positioning.training_phase.value if hasattr(positioning.training_phase, "value") else positioning.training_phase, | |
| "forward_focus": forward_focus, | |
| "trajectory": get_text(f"positioning_trajectory_{positioning.status.lower()}", language), | |
| "insight": insight, | |
| "evidence": evidence if positioning.status != "BASELINE_BUILDING" else None, | |
| } | |