v9d Phase 2d: Vitals Module Refactoring (Baseline + Blood Pressure) #22
|
|
@ -77,13 +77,23 @@ def _get_profile_data(pid: str):
|
|||
nutrition = [r2d(r) for r in cur.fetchall()]
|
||||
cur.execute("SELECT * FROM activity_log WHERE profile_id=%s ORDER BY date DESC LIMIT 90", (pid,))
|
||||
activity = [r2d(r) for r in cur.fetchall()]
|
||||
# v9d Phase 2: Sleep, Rest Days, Vitals
|
||||
cur.execute("SELECT * FROM sleep_log WHERE profile_id=%s ORDER BY date DESC LIMIT 30", (pid,))
|
||||
sleep = [r2d(r) for r in cur.fetchall()]
|
||||
cur.execute("SELECT * FROM rest_days WHERE profile_id=%s ORDER BY date DESC LIMIT 30", (pid,))
|
||||
rest_days = [r2d(r) for r in cur.fetchall()]
|
||||
cur.execute("SELECT * FROM vitals_log WHERE profile_id=%s ORDER BY date DESC LIMIT 30", (pid,))
|
||||
vitals = [r2d(r) for r in cur.fetchall()]
|
||||
return {
|
||||
"profile": prof,
|
||||
"weight": weight,
|
||||
"circumference": circ,
|
||||
"caliper": caliper,
|
||||
"nutrition": nutrition,
|
||||
"activity": activity
|
||||
"activity": activity,
|
||||
"sleep": sleep,
|
||||
"rest_days": rest_days,
|
||||
"vitals": vitals
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -103,6 +113,9 @@ def _prepare_template_vars(data: dict) -> dict:
|
|||
caliper = data['caliper']
|
||||
nutrition = data['nutrition']
|
||||
activity = data['activity']
|
||||
sleep = data.get('sleep', [])
|
||||
rest_days = data.get('rest_days', [])
|
||||
vitals = data.get('vitals', [])
|
||||
|
||||
vars = {
|
||||
"name": prof.get('name', 'Nutzer'),
|
||||
|
|
@ -192,6 +205,75 @@ def _prepare_template_vars(data: dict) -> dict:
|
|||
vars['activity_detail'] = "keine Daten"
|
||||
vars['activity_kcal_summary'] = "keine Daten"
|
||||
|
||||
# Sleep summary (v9d Phase 2b)
|
||||
if sleep:
|
||||
n = len(sleep)
|
||||
avg_duration = sum(float(s.get('duration_minutes',0) or 0) for s in sleep) / n
|
||||
avg_quality = sum(int(s.get('quality',0) or 0) for s in sleep if s.get('quality')) / max(sum(1 for s in sleep if s.get('quality')), 1)
|
||||
deep_data = [s for s in sleep if s.get('deep_minutes')]
|
||||
avg_deep = sum(float(s.get('deep_minutes',0)) for s in deep_data) / len(deep_data) if deep_data else 0
|
||||
vars['sleep_summary'] = f"{n} Nächte, Ø {avg_duration/60:.1f}h Schlafdauer, Qualität {avg_quality:.1f}/5"
|
||||
vars['sleep_detail'] = f"Ø {avg_duration:.0f}min gesamt, {avg_deep:.0f}min Tiefschlaf"
|
||||
vars['sleep_avg_duration'] = round(avg_duration)
|
||||
vars['sleep_avg_quality'] = round(avg_quality, 1)
|
||||
vars['sleep_nights'] = n
|
||||
else:
|
||||
vars['sleep_summary'] = "keine Daten"
|
||||
vars['sleep_detail'] = "keine Daten"
|
||||
vars['sleep_avg_duration'] = 0
|
||||
vars['sleep_avg_quality'] = 0
|
||||
vars['sleep_nights'] = 0
|
||||
|
||||
# Rest Days summary (v9d Phase 2a)
|
||||
if rest_days:
|
||||
n = len(rest_days)
|
||||
types = {}
|
||||
for rd in rest_days:
|
||||
rt = rd.get('rest_type', 'unknown')
|
||||
types[rt] = types.get(rt, 0) + 1
|
||||
type_summary = ", ".join([f"{k}: {v}x" for k, v in types.items()])
|
||||
vars['rest_days_summary'] = f"{n} Ruhetage (letzte 30d): {type_summary}"
|
||||
vars['rest_days_count'] = n
|
||||
vars['rest_days_types'] = type_summary
|
||||
else:
|
||||
vars['rest_days_summary'] = "keine Daten"
|
||||
vars['rest_days_count'] = 0
|
||||
vars['rest_days_types'] = "keine"
|
||||
|
||||
# Vitals summary (v9d Phase 2d)
|
||||
if vitals:
|
||||
n = len(vitals)
|
||||
hr_data = [v for v in vitals if v.get('resting_hr')]
|
||||
hrv_data = [v for v in vitals if v.get('hrv')]
|
||||
bp_data = [v for v in vitals if v.get('blood_pressure_systolic') and v.get('blood_pressure_diastolic')]
|
||||
vo2_data = [v for v in vitals if v.get('vo2_max')]
|
||||
|
||||
avg_hr = sum(int(v.get('resting_hr')) for v in hr_data) / len(hr_data) if hr_data else 0
|
||||
avg_hrv = sum(int(v.get('hrv')) for v in hrv_data) / len(hrv_data) if hrv_data else 0
|
||||
avg_bp_sys = sum(int(v.get('blood_pressure_systolic')) for v in bp_data) / len(bp_data) if bp_data else 0
|
||||
avg_bp_dia = sum(int(v.get('blood_pressure_diastolic')) for v in bp_data) / len(bp_data) if bp_data else 0
|
||||
latest_vo2 = float(vo2_data[0].get('vo2_max')) if vo2_data else 0
|
||||
|
||||
parts = []
|
||||
if avg_hr: parts.append(f"Ruhepuls Ø {avg_hr:.0f}bpm")
|
||||
if avg_hrv: parts.append(f"HRV Ø {avg_hrv:.0f}ms")
|
||||
if avg_bp_sys: parts.append(f"Blutdruck Ø {avg_bp_sys:.0f}/{avg_bp_dia:.0f}mmHg")
|
||||
if latest_vo2: parts.append(f"VO2 Max {latest_vo2:.1f}")
|
||||
|
||||
vars['vitals_summary'] = f"{n} Messungen: " + ", ".join(parts) if parts else "keine verwertbaren Daten"
|
||||
vars['vitals_detail'] = vars['vitals_summary']
|
||||
vars['vitals_avg_hr'] = round(avg_hr)
|
||||
vars['vitals_avg_hrv'] = round(avg_hrv)
|
||||
vars['vitals_avg_bp'] = f"{round(avg_bp_sys)}/{round(avg_bp_dia)}" if avg_bp_sys else "k.A."
|
||||
vars['vitals_vo2_max'] = round(latest_vo2, 1) if latest_vo2 else "k.A."
|
||||
else:
|
||||
vars['vitals_summary'] = "keine Daten"
|
||||
vars['vitals_detail'] = "keine Daten"
|
||||
vars['vitals_avg_hr'] = 0
|
||||
vars['vitals_avg_hrv'] = 0
|
||||
vars['vitals_avg_bp'] = "k.A."
|
||||
vars['vitals_vo2_max'] = "k.A."
|
||||
|
||||
return vars
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,11 @@ function PromptEditor({ prompt, onSave, onCancel }) {
|
|||
'{{weight_trend}}','{{weight_aktuell}}','{{kf_aktuell}}','{{caliper_summary}}',
|
||||
'{{circ_summary}}','{{nutrition_summary}}','{{nutrition_detail}}',
|
||||
'{{protein_ziel_low}}','{{protein_ziel_high}}','{{activity_summary}}',
|
||||
'{{activity_kcal_summary}}','{{activity_detail}}']
|
||||
'{{activity_kcal_summary}}','{{activity_detail}}',
|
||||
'{{sleep_summary}}','{{sleep_detail}}','{{sleep_avg_duration}}','{{sleep_avg_quality}}',
|
||||
'{{rest_days_summary}}','{{rest_days_count}}','{{rest_days_types}}',
|
||||
'{{vitals_summary}}','{{vitals_detail}}','{{vitals_avg_hr}}','{{vitals_avg_hrv}}',
|
||||
'{{vitals_avg_bp}}','{{vitals_vo2_max}}']
|
||||
|
||||
return (
|
||||
<div className="card section-gap">
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user