From 159fcab17a1f0f81bae78a50dddc301896913014 Mon Sep 17 00:00:00 2001 From: Lars Date: Thu, 26 Mar 2026 13:09:38 +0100 Subject: [PATCH] feat: circ_summary with best-of-each strategy and age annotations - Each circumference point shows most recent value (even from different dates) - Age annotations: heute, gestern, vor X Tagen/Wochen/Monaten - Gives AI better context about measurement freshness - Example: 'Brust 105cm (heute), Nacken 38cm (vor 2 Wochen)' --- backend/placeholder_resolver.py | 71 +++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 21 deletions(-) diff --git a/backend/placeholder_resolver.py b/backend/placeholder_resolver.py index e568f4a..73e0030 100644 --- a/backend/placeholder_resolver.py +++ b/backend/placeholder_resolver.py @@ -123,33 +123,62 @@ def get_caliper_summary(profile_id: str) -> str: def get_circ_summary(profile_id: str) -> str: - """Get latest circumference measurements summary.""" + """Get latest circumference measurements summary with age annotations. + + For each measurement point, fetches the most recent value (even if from different dates). + Annotates each value with measurement age for AI context. + """ with get_db() as conn: cur = get_cursor(conn) - cur.execute( - """SELECT c_neck, c_chest, c_waist, c_belly, c_hip, c_thigh, c_calf, c_arm, date - FROM circumference_log - WHERE profile_id=%s - ORDER BY date DESC LIMIT 1""", - (profile_id,) - ) - row = r2d(cur.fetchone()) if cur.rowcount > 0 else None - if not row: - return "keine Umfangsmessungen" + # Define all circumference points with their labels + fields = [ + ('c_neck', 'Nacken'), + ('c_chest', 'Brust'), + ('c_waist', 'Taille'), + ('c_belly', 'Bauch'), + ('c_hip', 'Hüfte'), + ('c_thigh', 'Oberschenkel'), + ('c_calf', 'Wade'), + ('c_arm', 'Arm') + ] - # Check all 8 circumference points parts = [] - if row.get('c_neck'): parts.append(f"Nacken {row['c_neck']:.1f}cm") - if row.get('c_chest'): parts.append(f"Brust {row['c_chest']:.1f}cm") - if row.get('c_waist'): parts.append(f"Taille {row['c_waist']:.1f}cm") - if row.get('c_belly'): parts.append(f"Bauch {row['c_belly']:.1f}cm") - if row.get('c_hip'): parts.append(f"Hüfte {row['c_hip']:.1f}cm") - if row.get('c_thigh'): parts.append(f"Oberschenkel {row['c_thigh']:.1f}cm") - if row.get('c_calf'): parts.append(f"Wade {row['c_calf']:.1f}cm") - if row.get('c_arm'): parts.append(f"Arm {row['c_arm']:.1f}cm") + today = datetime.now().date() - return f"{', '.join(parts)} ({row['date']})" if parts else "keine Daten" + # Get latest value for each field individually + for field_name, label in fields: + cur.execute( + f"""SELECT {field_name}, date, + CURRENT_DATE - date AS age_days + FROM circumference_log + WHERE profile_id=%s AND {field_name} IS NOT NULL + ORDER BY date DESC LIMIT 1""", + (profile_id,) + ) + row = r2d(cur.fetchone()) if cur.rowcount > 0 else None + + if row: + value = row[field_name] + age_days = row['age_days'] + + # Format age annotation + if age_days == 0: + age_str = "heute" + elif age_days == 1: + age_str = "gestern" + elif age_days <= 7: + age_str = f"vor {age_days} Tagen" + elif age_days <= 30: + weeks = age_days // 7 + age_str = f"vor {weeks} Woche{'n' if weeks > 1 else ''}" + else: + months = age_days // 30 + age_str = f"vor {months} Monat{'en' if months > 1 else ''}" + + parts.append(f"{label} {value:.1f}cm ({age_str})") + + return ', '.join(parts) if parts else "keine Umfangsmessungen" def get_goal_weight(profile_id: str) -> str: