runner-ai-intelligence / src /domain /goals /goal_trajectory_engine.py
avfranco's picture
HF Space deploy snapshot (minimal allow-list)
d64fd55
from domain.goals.goal_trajectory import GoalTrajectory
class GoalTrajectoryEngine:
def compute(
self, goal, weekly_snapshot, weekly_trend=None, runner_profile=None
):
if not goal or not weekly_snapshot:
return None
current_km = weekly_snapshot.total_distance_km or 0
target_val = getattr(goal, "target_value", 0)
unit = getattr(goal, "unit", "km")
goal_type = getattr(goal, "type", "goal")
name = f"{goal_type} {target_val} {unit}"
# ---- Weekly Volume Goal ----
if getattr(goal, "type", None) == "volume":
target_km = goal.target_value
progress_pct = (current_km / target_km) * 100 if target_km else 0
if current_km >= target_km * 1.1:
status = "ahead"
elif current_km >= target_km * 0.9:
status = "on_track"
else:
status = "behind"
milestone = f"Maintain ≥{target_km:.1f} km/week"
return GoalTrajectory(
goal_name=name,
target_date=getattr(goal, "target_date", None),
status=status,
progress_pct=progress_pct,
expected_weekly_km=target_km,
current_weekly_km=current_km,
next_milestone=milestone
)
# ---- Race Goal ----
if getattr(goal, "type", None) == "race":
target_km = goal.target_value
baseline = None
if runner_profile and runner_profile.baseline_weekly_km:
baseline = runner_profile.baseline_weekly_km
else:
baseline = current_km
expected_weekly_km = max(baseline, target_km * 1.5)
progress_pct = (
(current_km / expected_weekly_km) * 100 if expected_weekly_km else 0
)
if current_km >= expected_weekly_km * 1.1:
status = "ahead"
elif current_km >= expected_weekly_km * 0.9:
status = "on_track"
else:
status = "behind"
milestone = f"Reach ~{expected_weekly_km:.1f} km/week"
return GoalTrajectory(
goal_name=name,
target_date=getattr(goal, "target_date", None),
status=status,
progress_pct=progress_pct / 100.0,
expected_weekly_km=expected_weekly_km,
current_weekly_km=current_km,
next_milestone=milestone
)
return None