feat: add AI evaluation placeholders for v9d Phase 2 modules
**Backend (insights.py):**
- Extended _get_profile_data() to fetch sleep, rest_days, vitals
- Added template variables for Sleep Module:
{{sleep_summary}}, {{sleep_detail}}, {{sleep_avg_duration}}, {{sleep_avg_quality}}
- Added template variables for Rest Days:
{{rest_days_summary}}, {{rest_days_count}}, {{rest_days_types}}
- Added template variables for Vitals:
{{vitals_summary}}, {{vitals_detail}}, {{vitals_avg_hr}}, {{vitals_avg_hrv}},
{{vitals_avg_bp}}, {{vitals_vo2_max}}
**Frontend (Analysis.jsx):**
- Added 12 new template variables to VARS list in PromptEditor
- Enables AI prompt creation for Sleep, Rest Days, and Vitals analysis
All modules now have AI evaluation support for future prompt creation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
bf87e03100
commit
37fd28ec5a
|
|
@ -77,13 +77,23 @@ def _get_profile_data(pid: str):
|
||||||
nutrition = [r2d(r) for r in cur.fetchall()]
|
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,))
|
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()]
|
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 {
|
return {
|
||||||
"profile": prof,
|
"profile": prof,
|
||||||
"weight": weight,
|
"weight": weight,
|
||||||
"circumference": circ,
|
"circumference": circ,
|
||||||
"caliper": caliper,
|
"caliper": caliper,
|
||||||
"nutrition": nutrition,
|
"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']
|
caliper = data['caliper']
|
||||||
nutrition = data['nutrition']
|
nutrition = data['nutrition']
|
||||||
activity = data['activity']
|
activity = data['activity']
|
||||||
|
sleep = data.get('sleep', [])
|
||||||
|
rest_days = data.get('rest_days', [])
|
||||||
|
vitals = data.get('vitals', [])
|
||||||
|
|
||||||
vars = {
|
vars = {
|
||||||
"name": prof.get('name', 'Nutzer'),
|
"name": prof.get('name', 'Nutzer'),
|
||||||
|
|
@ -192,6 +205,75 @@ def _prepare_template_vars(data: dict) -> dict:
|
||||||
vars['activity_detail'] = "keine Daten"
|
vars['activity_detail'] = "keine Daten"
|
||||||
vars['activity_kcal_summary'] = "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
|
return vars
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,11 @@ function PromptEditor({ prompt, onSave, onCancel }) {
|
||||||
'{{weight_trend}}','{{weight_aktuell}}','{{kf_aktuell}}','{{caliper_summary}}',
|
'{{weight_trend}}','{{weight_aktuell}}','{{kf_aktuell}}','{{caliper_summary}}',
|
||||||
'{{circ_summary}}','{{nutrition_summary}}','{{nutrition_detail}}',
|
'{{circ_summary}}','{{nutrition_summary}}','{{nutrition_detail}}',
|
||||||
'{{protein_ziel_low}}','{{protein_ziel_high}}','{{activity_summary}}',
|
'{{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 (
|
return (
|
||||||
<div className="card section-gap">
|
<div className="card section-gap">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user