mitai-jinkendo/backend/data_layer/nutrition_body_merge.py

65 lines
2.5 KiB
Python
Raw Normal View History

"""
Layer 1 Hilfslogik: Ernährung + Gewicht + Caliper (forward-filled Magermasse).
Genutzt von Layer 2b (nutrition_viz) und vom Router GET /api/nutrition/correlations.
"""
from __future__ import annotations
from typing import Any, Dict, List, Optional
from db import get_db, get_cursor, r2d
from caliper_composition import compute_lean_fat_kg, nearest_weight_kg_from_map
def build_merged_daily_nutrition_body_rows(profile_id: str) -> List[Dict[str, Any]]:
"""
Pro Kalendertag: Makros aus nutrition_log, Gewicht, forward-filled Caliper (lean_mass, bf%).
Gleiche Semantik wie bisher ``GET /api/nutrition/correlations``.
"""
with get_db() as conn:
cur = get_cursor(conn)
cur.execute("SELECT * FROM nutrition_log WHERE profile_id=%s ORDER BY date", (profile_id,))
nutr = {r["date"]: r2d(r) for r in cur.fetchall()}
cur.execute("SELECT date, weight FROM weight_log WHERE profile_id=%s ORDER BY date", (profile_id,))
wlog = {r["date"]: r["weight"] for r in cur.fetchall()}
cur.execute(
"SELECT date, lean_mass, body_fat_pct FROM caliper_log WHERE profile_id=%s ORDER BY date",
(profile_id,),
)
cals = sorted([r2d(r) for r in cur.fetchall()], key=lambda x: x["date"])
all_dates = sorted(set(list(nutr.keys()) + list(wlog.keys())))
mi = 0
last_cal: Dict[str, Any] = {}
cal_by_date: Dict[Any, Dict[str, Any]] = {}
for d in all_dates:
while mi < len(cals) and cals[mi]["date"] <= d:
last_cal = cals[mi]
mi += 1
if last_cal:
cal_by_date[d] = last_cal
result: List[Dict[str, Any]] = []
for d in all_dates:
if d not in nutr and d not in wlog:
continue
row: Dict[str, Any] = {"date": d}
if d in nutr:
for k in ("kcal", "protein_g", "fat_g", "carbs_g"):
v = nutr[d].get(k)
row[k] = float(v) if v is not None else None
if d in wlog:
row["weight"] = float(wlog[d])
if d in cal_by_date:
lm = cal_by_date[d].get("lean_mass")
bf = cal_by_date[d].get("body_fat_pct")
if bf is not None and lm is None:
wkg = nearest_weight_kg_from_map(wlog, d)
if wkg is not None:
lm, _fat = compute_lean_fat_kg(wkg, float(bf))
row["lean_mass"] = float(lm) if lm is not None else None
row["body_fat_pct"] = float(bf) if bf is not None else None
result.append(row)
return result