feat: Phase 0c - body_metrics.py module complete
All checks were successful
Deploy Development / deploy (push) Successful in 46s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s

Data Layer:
- get_latest_weight_data() - most recent weight with date
- get_weight_trend_data() - already existed (PoC)
- get_body_composition_data() - already existed (PoC)
- get_circumference_summary_data() - already existed (PoC)

Placeholder Layer:
- get_latest_weight() - refactored to use data layer
- get_caliper_summary() - refactored to use get_body_composition_data
- get_weight_trend() - already refactored (PoC)
- get_latest_bf() - already refactored (PoC)
- get_circ_summary() - already refactored (PoC)

body_metrics.py now complete with all 4 functions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lars 2026-03-28 19:17:02 +01:00
parent b4558b0582
commit 6c23973c5d
3 changed files with 71 additions and 23 deletions

View File

@ -45,6 +45,7 @@ __all__ = [
'serialize_dates',
# Body Metrics
'get_latest_weight_data',
'get_weight_trend_data',
'get_body_composition_data',
'get_circumference_summary_data',

View File

@ -4,6 +4,7 @@ Body Metrics Data Layer
Provides structured data for body composition and measurements.
Functions:
- get_latest_weight_data(): Most recent weight entry
- get_weight_trend_data(): Weight trend with slope and direction
- get_body_composition_data(): Body fat percentage and lean mass
- get_circumference_summary_data(): Latest circumference measurements
@ -21,6 +22,51 @@ from db import get_db, get_cursor, r2d
from data_layer.utils import calculate_confidence, safe_float
def get_latest_weight_data(
profile_id: str
) -> Dict:
"""
Get most recent weight entry.
Args:
profile_id: User profile ID
Returns:
{
"weight": float, # kg
"date": date,
"confidence": str
}
Migration from Phase 0b:
OLD: get_latest_weight() returned formatted string "85.0 kg"
NEW: Returns structured data {"weight": 85.0, "date": ...}
"""
with get_db() as conn:
cur = get_cursor(conn)
cur.execute(
"""SELECT weight, date FROM weight_log
WHERE profile_id=%s
ORDER BY date DESC
LIMIT 1""",
(profile_id,)
)
row = cur.fetchone()
if not row:
return {
"weight": 0.0,
"date": None,
"confidence": "insufficient"
}
return {
"weight": safe_float(row['weight']),
"date": row['date'],
"confidence": "high"
}
def get_weight_trend_data(
profile_id: str,
days: int = 28

View File

@ -14,6 +14,7 @@ from db import get_db, get_cursor, r2d
# Phase 0c: Import data layer
from data_layer.body_metrics import (
get_latest_weight_data,
get_weight_trend_data,
get_body_composition_data,
get_circumference_summary_data
@ -51,15 +52,18 @@ def get_profile_data(profile_id: str) -> Dict:
def get_latest_weight(profile_id: str) -> Optional[str]:
"""Get latest weight entry."""
with get_db() as conn:
cur = get_cursor(conn)
cur.execute(
"SELECT weight FROM weight_log WHERE profile_id=%s ORDER BY date DESC LIMIT 1",
(profile_id,)
)
row = cur.fetchone()
return f"{row['weight']:.1f} kg" if row else "nicht verfügbar"
"""
Get latest weight entry.
Phase 0c: Refactored to use data_layer.body_metrics.get_latest_weight_data()
This function now only FORMATS the data for AI consumption.
"""
data = get_latest_weight_data(profile_id)
if data['confidence'] == 'insufficient':
return "nicht verfügbar"
return f"{data['weight']:.1f} kg"
def get_weight_trend(profile_id: str, days: int = 28) -> str:
@ -129,22 +133,19 @@ def get_nutrition_avg(profile_id: str, field: str, days: int = 30) -> str:
def get_caliper_summary(profile_id: str) -> str:
"""Get latest caliper measurements summary."""
with get_db() as conn:
cur = get_cursor(conn)
cur.execute(
"""SELECT body_fat_pct, sf_method, date FROM caliper_log
WHERE profile_id=%s AND body_fat_pct IS NOT NULL
ORDER BY date DESC LIMIT 1""",
(profile_id,)
)
row = r2d(cur.fetchone()) if cur.rowcount > 0 else None
"""
Get latest caliper measurements summary.
if not row:
Phase 0c: Refactored to use data_layer.body_metrics.get_body_composition_data()
This function now only FORMATS the data for AI consumption.
"""
data = get_body_composition_data(profile_id)
if data['confidence'] == 'insufficient':
return "keine Caliper-Messungen"
method = row.get('sf_method', 'unbekannt')
return f"{row['body_fat_pct']:.1f}% ({method} am {row['date']})"
method = data.get('method', 'unbekannt')
return f"{data['body_fat_pct']:.1f}% ({method} am {data['date']})"
def get_circ_summary(profile_id: str) -> str: