feat: consolidate datastore into sleep, nutrition, mila databases

This commit is contained in:
Moritz Graf
2026-10-13 09:28:07 +02:00
parent fe4edcc217
commit 7b5d039323
5 changed files with 896 additions and 161 deletions
+76 -14
View File
@@ -197,10 +197,18 @@ def generate_dashboard_html(engine: DatastoreEngine) -> str:
today_night_h, today_night_m = divmod(today_night_min, 60)
today_night_dur_str = f"{today_night_h} h {today_night_m:02d} min" if today_night_h > 0 else f"{today_night_m} min"
# 3. Today's Feedings (conditional)
# 3. Today's Feedings & Nutrition (Bottles + Brei/Beikost)
today_feedings = con.execute(
f"""
SELECT strftime(local_time, '%H:%M'), consumed_ml, milk_type, note
SELECT
strftime(local_time, '%H:%M') AS time_str,
consumed_ml,
milk_type,
item,
solid_g,
kcal,
kind,
note
FROM v_feeding
WHERE subject = 'mila' AND log_date = '{today_str}'
ORDER BY start ASC
@@ -209,7 +217,12 @@ def generate_dashboard_html(engine: DatastoreEngine) -> str:
today_feeding_agg = con.execute(
f"""
SELECT bottle_count, total_consumed_ml
SELECT
bottle_count,
total_consumed_ml,
solid_count,
total_solid_g,
total_kcal
FROM v_feeding_daily
WHERE subject = 'mila' AND log_date = '{today_str}'
"""
@@ -218,6 +231,9 @@ def generate_dashboard_html(engine: DatastoreEngine) -> str:
has_feeding_today = bool(today_feedings and len(today_feedings) > 0)
today_bottle_count = today_feeding_agg[0] if today_feeding_agg else 0
today_bottle_ml = int(today_feeding_agg[1]) if today_feeding_agg else 0
today_solid_count = today_feeding_agg[2] if today_feeding_agg else 0
today_solid_g = int(today_feeding_agg[3]) if today_feeding_agg else 0
today_feeding_kcal = int(today_feeding_agg[4]) if today_feeding_agg else 0
# 4. Today's Diapers (conditional)
today_diapers = con.execute(
@@ -272,6 +288,8 @@ def generate_dashboard_html(engine: DatastoreEngine) -> str:
s.nap_sleep_minutes,
COALESCE(f.total_consumed_ml, 0) AS feed_ml,
COALESCE(f.bottle_count, 0) AS feed_bottles,
COALESCE(f.total_solid_g, 0) AS feed_solid_g,
COALESCE(f.solid_count, 0) AS feed_solid_count,
COALESCE(d.total_changes, 0) AS diaper_cnt,
COALESCE(d.dirty_count, 0) AS diaper_dirty
FROM v_sleep_daily s
@@ -311,8 +329,15 @@ def generate_dashboard_html(engine: DatastoreEngine) -> str:
night_h, night_m = divmod(r[2], 60)
night_str = f"{night_h} h {night_m:02d} min" if night_h > 0 else f"{night_m} min"
nap_str = f"{r[3]} ({r[4]} min)"
feed_str = f"{int(r[5])} ml ({r[6]}x)" if r[6] > 0 else "—"
diaper_str = f"{r[7]} ({r[8]} Stuhl)" if r[7] > 0 else "—"
feed_parts = []
if r[6] > 0: # feed_bottles
feed_parts.append(f"{int(r[5])} ml ({r[6]}x)")
if r[8] > 0: # feed_solid_count
feed_parts.append(f"{int(r[7])} g Brei ({r[8]}x)")
feed_str = " + ".join(feed_parts) if feed_parts else "—"
diaper_str = f"{r[9]} ({r[10]} Stuhl)" if r[9] > 0 else "—"
# Detailed breakdown of sessions for this day
day_sess = sessions_by_date.get(d_str, [])
@@ -339,7 +364,7 @@ def generate_dashboard_html(engine: DatastoreEngine) -> str:
<td class="py-3.5 px-4 font-bold text-purple-300">{tot_h}</td>
<td class="py-3.5 px-4 text-sky-400 font-semibold">{night_str}</td>
<td class="py-3.5 px-4 text-emerald-400">{nap_str}</td>
<td class="py-3.5 px-4 text-zinc-400">{feed_str}</td>
<td class="py-3.5 px-4 text-amber-300 font-medium">{feed_str}</td>
<td class="py-3.5 px-4 text-zinc-400">{diaper_str}</td>
</tr>
<tr class="border-b border-zinc-800/30 bg-zinc-950/30 text-xs">
@@ -421,25 +446,62 @@ def generate_dashboard_html(engine: DatastoreEngine) -> str:
# Conditional feeding card HTML
feeding_section_html = ""
if has_feeding_today:
bottles_pills = []
feeding_pills = []
for f in today_feedings:
ml_val = f"{int(f[1])} ml" if f[1] else "—"
bottles_pills.append(f'<span class="px-2 py-1 rounded bg-amber-500/10 border border-amber-500/20 text-amber-300 text-xs">{f[0]} Uhr ({ml_val})</span>')
time_str = f[0]
ml_val = f[1]
milk_type = f[2]
item_name = f[3]
solid_g = f[4]
kcal_val = f[5]
kind = f[6]
if kind == "bottle" or (ml_val is not None and ml_val > 0):
ml_str = f"{int(ml_val)} ml"
milk_desc = f" · {milk_type}" if milk_type else ""
feeding_pills.append(
f'<span class="px-2.5 py-1 rounded-lg bg-amber-500/10 border border-amber-500/20 text-amber-300 text-xs flex items-center gap-1.5 font-medium">'
f'<span>🍼</span> <span>{time_str} Uhr: <strong>{ml_str}</strong>{milk_desc}</span></span>'
)
else:
food_title = item_name or "Brei"
details = []
if solid_g:
details.append(f"{int(solid_g)} g")
if kcal_val:
details.append(f"~{int(kcal_val)} kcal")
detail_str = f" ({', '.join(details)})" if details else ""
feeding_pills.append(
f'<span class="px-2.5 py-1 rounded-lg bg-orange-500/10 border border-orange-500/20 text-orange-300 text-xs flex items-center gap-1.5 font-medium">'
f'<span>🥣</span> <span>{time_str} Uhr: <strong>{food_title}</strong>{detail_str}</span></span>'
)
feed_summary_parts = []
if today_bottle_count > 0:
feed_summary_parts.append(
f'<span class="font-mono font-bold text-amber-400 text-base">{today_bottle_ml} ml <span class="text-xs text-zinc-400 font-normal">({today_bottle_count}x Flasche)</span></span>'
)
if today_solid_count > 0:
feed_summary_parts.append(
f'<span class="font-mono font-bold text-orange-400 text-base">{today_solid_g} g <span class="text-xs text-zinc-400 font-normal">({today_solid_count}x Brei)</span></span>'
)
feed_summary_html = " · ".join(feed_summary_parts) if feed_summary_parts else '<span class="text-zinc-500 text-sm">—</span>'
feeding_section_html = f"""
<div class="bg-zinc-900/80 border border-amber-500/20 rounded-2xl p-5 shadow-lg space-y-3">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<span class="text-xl">🍼</span>
<h3 class="font-semibold text-zinc-200">Fläschchen heute</h3>
<span class="text-xl">🍼🥣</span>
<h3 class="font-semibold text-zinc-200">Ernährung heute</h3>
</div>
<span class="font-mono font-bold text-amber-400 text-lg">{today_bottle_ml} ml <span class="text-xs text-zinc-400 font-normal">({today_bottle_count}x)</span></span>
<div>{feed_summary_html}</div>
</div>
<div class="flex flex-wrap gap-2 pt-1">
{' '.join(bottles_pills)}
{' '.join(feeding_pills)}
</div>
</div>
"""
# Conditional diaper card HTML
diaper_section_html = ""
if has_diapers_today:
@@ -999,7 +1061,7 @@ def generate_dashboard_html(engine: DatastoreEngine) -> str:
<th class="py-2.5 px-4">24h Gesamt</th>
<th class="py-2.5 px-4">Nacht</th>
<th class="py-2.5 px-4">Schläfchen</th>
<th class="py-2.5 px-4">Fläschchen</th>
<th class="py-2.5 px-4">Ernährung (Milch & Brei)</th>
<th class="py-2.5 px-4 rounded-r-lg">Windeln</th>
</tr>
</thead>