Spaces:
Runtime error
Runtime error
File size: 4,986 Bytes
d64fd55 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | from datetime import date, timedelta
from typing import Optional, Dict, Any
from _app.presentation.ui_text import get_text
def _get_val(obj, key, default=None):
if obj is None:
return default
# Mapping for new DTO keys against domain models
ATTR_MAP = {
"week_start": "week_start_date",
"num_runs": "run_count",
"weekly_distance_km": "total_distance_km"
}
def _extract(target, k):
if target is None:
return None, False
if isinstance(target, dict):
if k in target:
return target[k], True
return None, False
from unittest.mock import MagicMock
if isinstance(target, MagicMock):
# Only return if specifically set (in __dict__)
if k in target.__dict__:
return getattr(target, k), True
return None, False
# Regular object
if hasattr(target, k):
return getattr(target, k), True
return None, False
# 1. Try exact key
val, found = _extract(obj, key)
if found:
return val
# 2. Try mapped key
mapped = ATTR_MAP.get(key)
if mapped:
val, found = _extract(obj, mapped)
if found:
return val
# 3. Final fallback
if isinstance(obj, dict):
return obj.get(key, default)
# For non-mocks, try getattr one last time
from unittest.mock import MagicMock
if not isinstance(obj, MagicMock):
return getattr(obj, key, default)
return default
def is_current_week(week_start) -> bool:
"""Detect if the given week start date corresponds to the current week."""
if not week_start:
return False
from datetime import date
if isinstance(week_start, str):
try:
week_start = date.fromisoformat(week_start)
except:
return False
today = date.today()
# Monday of the current week
current_monday = today - timedelta(days=today.weekday())
return week_start == current_monday
def format_positioning_metrics(snapshot, language: str = "en") -> Dict[str, Any]:
"""
Returns absolute metrics for the current week to avoid misleading percentages.
Returns delta metrics for previous weeks.
"""
if not snapshot:
return {}
w_start = _get_val(snapshot, "week_start")
is_current = is_current_week(w_start)
if is_current:
dist_val = _get_val(snapshot, "weekly_distance_km", 0.0)
dist_str = get_text("so_far_this_week", language).format(val=f"{dist_val:.1f} km")
runs_val = _get_val(snapshot, "num_runs", 0)
runs_str = f"{runs_val} " + (get_text("unit_runs", language) if runs_val != 1 else get_text("lbl_runs", language).lower()[:-1] if language == "en" else "corrida")
# Add "building consistency" message if only 1 run
consistency_msg = ""
if runs_val <= 1:
consistency_msg = get_text("building_consistency", language)
return {
"distance": dist_str,
"runs": runs_str,
"consistency_msg": consistency_msg,
"mode": "absolute"
}
trend_val = _get_val(snapshot, "trend")
if trend_val:
dist_delta = _get_val(trend_val, "distance_delta_pct", 0.0)
run_delta = _get_val(trend_val, "frequency_delta", 0)
else:
# Fallback to direct attributes for domain models (WeeklySnapshot doesn't have trend attached usually)
dist_delta = _get_val(snapshot, "distance_delta_pct", 0.0)
run_delta = _get_val(snapshot, "run_delta", 0)
return {
"distance": f"{dist_delta:+.1f}%",
"runs": f"{run_delta:+d}",
"consistency_msg": "",
"mode": "delta"
}
def get_baseline_aware_target(current_km: float, baseline: float) -> float:
"""
Computes a safe target volume based on historical baseline.
Rule:
- If current < 60% of baseline -> Rebuild to 60%
- If current < 80% of baseline -> Rebuild to 80%
- Otherwise -> target baseline
- Minimum safety floor: 8.0 km
"""
if not baseline or baseline <= 0:
return max(current_km, 8.0)
drop_ratio = current_km / baseline
if drop_ratio < 0.6:
target_km = baseline * 0.6
elif drop_ratio < 0.8:
target_km = baseline * 0.8
else:
target_km = baseline
return max(target_km, 8.0)
def interpret_week(snapshot, baseline: Optional[float], language: str = "en") -> Optional[str]:
"""Add simple interpretation rules for the narrative layer."""
if not snapshot:
return None
run_count = _get_val(snapshot, "num_runs", 0)
if run_count <= 1:
return get_text("early_week_building", language)
dist_km = _get_val(snapshot, "weekly_distance_km", 0.0)
if baseline and dist_km < baseline * 0.6:
return get_text("rebuild_phase", language)
return None
|