fixed dynamic TDEE
This commit is contained in:
@@ -21,6 +21,17 @@ PROTEIN_G_PER_KG = {
|
||||
}
|
||||
FAT_G_PER_KG = 1.0
|
||||
|
||||
EXPECTED_LOOKBACK_DAYS = 7
|
||||
EXPECTED_MIN_DAYS_WITH_DATA = 3
|
||||
DEFAULT_SESSION_KCAL = 350.0
|
||||
|
||||
ACTIVITY_LEVEL_STEPS: dict[str, int] = {
|
||||
"sedentary": 5000,
|
||||
"moderate": 8000,
|
||||
"active": 10000,
|
||||
"very_active": 12000,
|
||||
}
|
||||
|
||||
|
||||
def bmr_mifflin(*, sex: str, weight_kg: float, height_cm: float, age: int) -> float:
|
||||
base = 10 * weight_kg + 6.25 * height_cm - 5 * age
|
||||
@@ -166,3 +177,99 @@ def compute_targets(profile: dict[str, Any]) -> dict[str, Any]:
|
||||
"carbs_g": daily["carbs_g"],
|
||||
"water_l": daily["water_l"],
|
||||
}
|
||||
|
||||
|
||||
def _activity_level_steps(activity_level: str | None) -> int:
|
||||
key = (activity_level or "moderate").lower().replace("-", "_")
|
||||
return ACTIVITY_LEVEL_STEPS.get(key, ACTIVITY_LEVEL_STEPS["moderate"])
|
||||
|
||||
|
||||
def _history_days_with_data(history: list[dict[str, Any]]) -> int:
|
||||
return sum(
|
||||
1
|
||||
for row in history
|
||||
if int(row.get("steps") or 0) > 0 or float(row.get("workout_kcal") or 0) > 0
|
||||
)
|
||||
|
||||
|
||||
def resolve_expected_activity(
|
||||
profile: dict[str, Any],
|
||||
*,
|
||||
history: list[dict[str, Any]],
|
||||
lookback_days: int = EXPECTED_LOOKBACK_DAYS,
|
||||
) -> tuple[int, float, str, int]:
|
||||
"""Return expected daily steps, workout kcal, source, and days_with_data."""
|
||||
days_with_data = _history_days_with_data(history)
|
||||
|
||||
if days_with_data >= EXPECTED_MIN_DAYS_WITH_DATA:
|
||||
steps_vals = [int(row.get("steps") or 0) for row in history]
|
||||
workout_vals = [float(row.get("workout_kcal") or 0) for row in history]
|
||||
expected_steps = round(sum(steps_vals) / len(steps_vals))
|
||||
expected_workout_kcal = round(sum(workout_vals) / len(workout_vals), 1)
|
||||
return expected_steps, expected_workout_kcal, "weekly_avg", days_with_data
|
||||
|
||||
baseline_steps = profile.get("baseline_steps")
|
||||
baseline_workout_kcal = profile.get("baseline_workout_kcal")
|
||||
if baseline_steps is not None or baseline_workout_kcal is not None:
|
||||
steps = int(baseline_steps) if baseline_steps is not None else _activity_level_steps(
|
||||
profile.get("activity_level")
|
||||
)
|
||||
workout_daily = (
|
||||
round(float(baseline_workout_kcal) / 7, 1)
|
||||
if baseline_workout_kcal is not None
|
||||
else round(
|
||||
int(profile.get("weekly_workouts") or 3) * DEFAULT_SESSION_KCAL / 7,
|
||||
1,
|
||||
)
|
||||
)
|
||||
return steps, workout_daily, "baseline", days_with_data
|
||||
|
||||
weekly_workouts = int(profile.get("weekly_workouts") or 3)
|
||||
return (
|
||||
_activity_level_steps(profile.get("activity_level")),
|
||||
round(weekly_workouts * DEFAULT_SESSION_KCAL / 7, 1),
|
||||
"defaults",
|
||||
days_with_data,
|
||||
)
|
||||
|
||||
|
||||
def compute_expected_targets(
|
||||
profile: dict[str, Any],
|
||||
*,
|
||||
history: list[dict[str, Any]],
|
||||
lookback_days: int = EXPECTED_LOOKBACK_DAYS,
|
||||
) -> dict[str, Any]:
|
||||
expected_steps, expected_workout_kcal, source, days_with_data = resolve_expected_activity(
|
||||
profile,
|
||||
history=history,
|
||||
lookback_days=lookback_days,
|
||||
)
|
||||
workouts = [{"active_calories": expected_workout_kcal}] if expected_workout_kcal > 0 else []
|
||||
daily = compute_daily_targets(
|
||||
profile,
|
||||
steps_total=expected_steps,
|
||||
workouts=workouts,
|
||||
)
|
||||
return {
|
||||
**daily,
|
||||
"source": source,
|
||||
"lookback_days": lookback_days,
|
||||
"days_with_data": days_with_data,
|
||||
"expected_steps": expected_steps,
|
||||
"expected_workout_kcal": expected_workout_kcal,
|
||||
}
|
||||
|
||||
|
||||
def tdee_expected_to_api(daily: dict[str, Any]) -> dict[str, Any]:
|
||||
return {
|
||||
"bmr": daily["bmr"],
|
||||
"neat_kcal": daily["neat_kcal"],
|
||||
"steps_kcal": daily["steps_kcal"],
|
||||
"workout_kcal": daily["workout_kcal"],
|
||||
"tdee": daily["tdee"],
|
||||
"calorie_target": daily["calorie_target"],
|
||||
"steps": daily.get("expected_steps", daily.get("steps", 0)),
|
||||
"source": daily.get("source", "defaults"),
|
||||
"lookback_days": daily.get("lookback_days", EXPECTED_LOOKBACK_DAYS),
|
||||
"days_with_data": daily.get("days_with_data", 0),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user