smart tdee
This commit is contained in:
@@ -14,13 +14,14 @@ from app.db.models import (
|
||||
WaterLog,
|
||||
WorkoutLog,
|
||||
)
|
||||
from app.fitness.activity_budget import (
|
||||
build_base_targets,
|
||||
compute_activity_bonus,
|
||||
estimate_workout_active_kcal,
|
||||
scale_targets,
|
||||
from app.fitness.activity_budget import estimate_workout_active_kcal
|
||||
from app.fitness.calculators import (
|
||||
compute_daily_targets,
|
||||
compute_targets,
|
||||
one_rep_max,
|
||||
targets_to_api,
|
||||
tdee_breakdown_to_api,
|
||||
)
|
||||
from app.fitness.calculators import compute_targets, one_rep_max
|
||||
from app.fitness.body_composition import compute_body_composition
|
||||
|
||||
DEFAULT_REMINDERS = [
|
||||
@@ -45,28 +46,26 @@ class FitnessService:
|
||||
return None
|
||||
return self._profile_to_dict(row)
|
||||
|
||||
def _profile_to_dict(self, row: FitnessProfile) -> dict[str, Any]:
|
||||
targets = compute_targets(
|
||||
{
|
||||
"sex": row.sex,
|
||||
"age": row.age,
|
||||
"height_cm": row.height_cm,
|
||||
"weight_kg": row.weight_kg,
|
||||
"activity_level": row.activity_level,
|
||||
"goal": row.goal,
|
||||
}
|
||||
)
|
||||
def _profile_params(self, row: FitnessProfile) -> dict[str, Any]:
|
||||
return {
|
||||
"sex": row.sex,
|
||||
"age": row.age,
|
||||
"height_cm": row.height_cm,
|
||||
"weight_kg": row.weight_kg,
|
||||
"goal": row.goal,
|
||||
"neat_base_kcal": row.neat_base_kcal,
|
||||
}
|
||||
|
||||
def _profile_to_dict(self, row: FitnessProfile) -> dict[str, Any]:
|
||||
targets = compute_targets(self._profile_params(row))
|
||||
return {
|
||||
"sex": row.sex,
|
||||
"age": row.age,
|
||||
"height_cm": row.height_cm,
|
||||
"weight_kg": row.weight_kg,
|
||||
"activity_level": row.activity_level,
|
||||
"goal": row.goal,
|
||||
"target_weight_kg": row.target_weight_kg,
|
||||
"weekly_workouts": row.weekly_workouts,
|
||||
"baseline_steps": row.baseline_steps,
|
||||
"baseline_workout_kcal": row.baseline_workout_kcal,
|
||||
"neat_base_kcal": row.neat_base_kcal,
|
||||
"calorie_target": row.calorie_target,
|
||||
"protein_g": row.protein_g,
|
||||
"fat_g": row.fat_g,
|
||||
@@ -85,23 +84,13 @@ class FitnessService:
|
||||
self.db.flush()
|
||||
|
||||
for key in (
|
||||
"sex", "age", "height_cm", "weight_kg", "activity_level",
|
||||
"goal", "target_weight_kg", "weekly_workouts",
|
||||
"baseline_steps", "baseline_workout_kcal",
|
||||
"sex", "age", "height_cm", "weight_kg",
|
||||
"goal", "target_weight_kg", "neat_base_kcal",
|
||||
):
|
||||
if key in updates and updates[key] is not None:
|
||||
setattr(row, key, updates[key])
|
||||
|
||||
targets = compute_targets(
|
||||
{
|
||||
"sex": row.sex,
|
||||
"age": row.age,
|
||||
"height_cm": row.height_cm,
|
||||
"weight_kg": row.weight_kg,
|
||||
"activity_level": row.activity_level,
|
||||
"goal": row.goal,
|
||||
}
|
||||
)
|
||||
targets = compute_targets(self._profile_params(row))
|
||||
row.calorie_target = targets["calorie_target"]
|
||||
row.protein_g = targets["protein_g"]
|
||||
row.fat_g = targets["fat_g"]
|
||||
@@ -193,14 +182,12 @@ class FitnessService:
|
||||
if profile:
|
||||
return profile
|
||||
return {
|
||||
"calorie_target": 2000,
|
||||
"protein_g": 140,
|
||||
"fat_g": 65,
|
||||
"carbs_g": 200,
|
||||
"water_l": 2.5,
|
||||
"weight_kg": 70,
|
||||
"activity_level": "moderate",
|
||||
"weekly_workouts": 3,
|
||||
"height_cm": 170,
|
||||
"age": 30,
|
||||
"sex": "male",
|
||||
"goal": "maintain",
|
||||
"neat_base_kcal": 200,
|
||||
}
|
||||
|
||||
|
||||
@@ -248,24 +235,19 @@ class FitnessService:
|
||||
"steps": steps_total,
|
||||
}
|
||||
|
||||
base_targets = build_base_targets(profile)
|
||||
activity = compute_activity_bonus(
|
||||
daily = compute_daily_targets(
|
||||
profile,
|
||||
steps_total=steps_total,
|
||||
workouts=workouts,
|
||||
)
|
||||
effective_targets, targets_base = scale_targets(
|
||||
base_targets,
|
||||
activity.total_bonus_kcal,
|
||||
)
|
||||
targets = targets_to_api(daily)
|
||||
|
||||
return {
|
||||
"date": (day or datetime.now(timezone.utc).date()).isoformat(),
|
||||
"profile_configured": profile_row is not None,
|
||||
"totals": totals,
|
||||
"targets": effective_targets,
|
||||
"targets_base": targets_base,
|
||||
"activity": activity.to_dict(),
|
||||
"targets": targets,
|
||||
"tdee_breakdown": tdee_breakdown_to_api(daily),
|
||||
"meals": [self._food_to_dict(f) for f in foods],
|
||||
"water": [self._water_to_dict(w) for w in waters],
|
||||
"workouts": workouts,
|
||||
@@ -393,8 +375,8 @@ class FitnessService:
|
||||
"age": profile_row.age,
|
||||
"height_cm": profile_row.height_cm,
|
||||
"weight_kg": weight_kg,
|
||||
"activity_level": profile_row.activity_level,
|
||||
"goal": profile_row.goal,
|
||||
"neat_base_kcal": profile_row.neat_base_kcal,
|
||||
}
|
||||
)
|
||||
profile_row.calorie_target = targets["calorie_target"]
|
||||
@@ -428,10 +410,27 @@ class FitnessService:
|
||||
active_calories: float | None = None,
|
||||
total_calories: float | None = None,
|
||||
steps: int | None = None,
|
||||
activity_type: str | None = None,
|
||||
met: float | None = None,
|
||||
logged_at: datetime | str | None = None,
|
||||
day: date | None = None,
|
||||
days_ago: int | None = None,
|
||||
) -> dict[str, Any]:
|
||||
profile = self.get_profile() or {}
|
||||
weight_kg = float(profile.get("weight_kg") or 70)
|
||||
|
||||
if active_calories is None and duration_min and met is not None:
|
||||
active_calories = round(met * weight_kg * (float(duration_min) / 60.0), 1)
|
||||
elif active_calories is None and duration_min:
|
||||
draft = {
|
||||
"title": title,
|
||||
"notes": notes,
|
||||
"activity_type": activity_type,
|
||||
"met": met,
|
||||
"duration_min": duration_min,
|
||||
}
|
||||
active_calories = estimate_workout_active_kcal(draft, weight_kg=weight_kg) or None
|
||||
|
||||
row = WorkoutLog(
|
||||
user_id=self.user_id,
|
||||
title=title[:255],
|
||||
@@ -471,12 +470,16 @@ class FitnessService:
|
||||
).all()
|
||||
|
||||
profile = self.get_profile() or {}
|
||||
weekly_target = int(profile.get("weekly_workouts") or 3)
|
||||
weight_kg = float(profile.get("weight_kg") or 70)
|
||||
weekly_target = 3
|
||||
|
||||
count = len(rows)
|
||||
duration_min = sum(r.duration_min or 0 for r in rows)
|
||||
active_kcal = round(
|
||||
sum(estimate_workout_active_kcal(self._workout_to_dict(r)) for r in rows),
|
||||
sum(
|
||||
estimate_workout_active_kcal(self._workout_to_dict(r), weight_kg=weight_kg)
|
||||
for r in rows
|
||||
),
|
||||
1,
|
||||
)
|
||||
|
||||
@@ -583,7 +586,7 @@ class FitnessService:
|
||||
*,
|
||||
days: int = 7,
|
||||
end_day: date | None = None,
|
||||
include_targets_base: bool = True,
|
||||
include_tdee_breakdown: bool = True,
|
||||
) -> dict[str, Any]:
|
||||
days = max(1, min(days, 90))
|
||||
end = end_day or datetime.now(timezone.utc).date()
|
||||
@@ -603,8 +606,8 @@ class FitnessService:
|
||||
"meal_count": len(full["meals"]),
|
||||
"workout_count": len(full["workouts"]),
|
||||
}
|
||||
if include_targets_base:
|
||||
item["targets_base"] = full.get("targets_base")
|
||||
if include_tdee_breakdown:
|
||||
item["tdee_breakdown"] = full.get("tdee_breakdown")
|
||||
summaries.append(item)
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user