mitai-jinkendo/backend/routers/nutrition.py

290 lines
13 KiB
Python
Raw Normal View History

refactor: modular backend architecture with 14 router modules Phase 2 Complete - Backend Refactoring: - Extracted all endpoints to dedicated router modules - main.py: 1878 → 75 lines (-96% reduction) - Created modular structure for maintainability Router Structure (60 endpoints total): ├── auth.py - 7 endpoints (login, logout, password reset) ├── profiles.py - 7 endpoints (CRUD + current user) ├── weight.py - 5 endpoints (tracking + stats) ├── circumference.py - 4 endpoints (body measurements) ├── caliper.py - 4 endpoints (skinfold tracking) ├── activity.py - 6 endpoints (workouts + Apple Health import) ├── nutrition.py - 4 endpoints (diet + FDDB import) ├── photos.py - 3 endpoints (progress photos) ├── insights.py - 8 endpoints (AI analysis + pipeline) ├── prompts.py - 2 endpoints (AI prompt management) ├── admin.py - 7 endpoints (user management) ├── stats.py - 1 endpoint (dashboard stats) ├── exportdata.py - 3 endpoints (CSV/JSON/ZIP export) └── importdata.py - 1 endpoint (ZIP import) Core modules maintained: - db.py: PostgreSQL connection + helpers - auth.py: Auth functions (hash, verify, sessions) - models.py: 11 Pydantic models Benefits: - Self-contained modules with clear responsibilities - Easier to navigate and modify specific features - Improved code organization and readability - 100% functional compatibility maintained - All syntax checks passed Updated CLAUDE.md with new architecture documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 11:15:35 +01:00
"""
Nutrition Tracking Endpoints for Mitai Jinkendo
Handles nutrition data, FDDB CSV import, correlations, and weekly aggregates.
"""
import csv
import io
import uuid
import logging
refactor: modular backend architecture with 14 router modules Phase 2 Complete - Backend Refactoring: - Extracted all endpoints to dedicated router modules - main.py: 1878 → 75 lines (-96% reduction) - Created modular structure for maintainability Router Structure (60 endpoints total): ├── auth.py - 7 endpoints (login, logout, password reset) ├── profiles.py - 7 endpoints (CRUD + current user) ├── weight.py - 5 endpoints (tracking + stats) ├── circumference.py - 4 endpoints (body measurements) ├── caliper.py - 4 endpoints (skinfold tracking) ├── activity.py - 6 endpoints (workouts + Apple Health import) ├── nutrition.py - 4 endpoints (diet + FDDB import) ├── photos.py - 3 endpoints (progress photos) ├── insights.py - 8 endpoints (AI analysis + pipeline) ├── prompts.py - 2 endpoints (AI prompt management) ├── admin.py - 7 endpoints (user management) ├── stats.py - 1 endpoint (dashboard stats) ├── exportdata.py - 3 endpoints (CSV/JSON/ZIP export) └── importdata.py - 1 endpoint (ZIP import) Core modules maintained: - db.py: PostgreSQL connection + helpers - auth.py: Auth functions (hash, verify, sessions) - models.py: 11 Pydantic models Benefits: - Self-contained modules with clear responsibilities - Easier to navigate and modify specific features - Improved code organization and readability - 100% functional compatibility maintained - All syntax checks passed Updated CLAUDE.md with new architecture documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 11:15:35 +01:00
from typing import Optional
from datetime import datetime
from fastapi import APIRouter, HTTPException, UploadFile, File, Header, Depends
from db import get_db, get_cursor, r2d
from auth import require_auth, check_feature_access, increment_feature_usage
refactor: modular backend architecture with 14 router modules Phase 2 Complete - Backend Refactoring: - Extracted all endpoints to dedicated router modules - main.py: 1878 → 75 lines (-96% reduction) - Created modular structure for maintainability Router Structure (60 endpoints total): ├── auth.py - 7 endpoints (login, logout, password reset) ├── profiles.py - 7 endpoints (CRUD + current user) ├── weight.py - 5 endpoints (tracking + stats) ├── circumference.py - 4 endpoints (body measurements) ├── caliper.py - 4 endpoints (skinfold tracking) ├── activity.py - 6 endpoints (workouts + Apple Health import) ├── nutrition.py - 4 endpoints (diet + FDDB import) ├── photos.py - 3 endpoints (progress photos) ├── insights.py - 8 endpoints (AI analysis + pipeline) ├── prompts.py - 2 endpoints (AI prompt management) ├── admin.py - 7 endpoints (user management) ├── stats.py - 1 endpoint (dashboard stats) ├── exportdata.py - 3 endpoints (CSV/JSON/ZIP export) └── importdata.py - 1 endpoint (ZIP import) Core modules maintained: - db.py: PostgreSQL connection + helpers - auth.py: Auth functions (hash, verify, sessions) - models.py: 11 Pydantic models Benefits: - Self-contained modules with clear responsibilities - Easier to navigate and modify specific features - Improved code organization and readability - 100% functional compatibility maintained - All syntax checks passed Updated CLAUDE.md with new architecture documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 11:15:35 +01:00
from routers.profiles import get_pid
from feature_logger import log_feature_usage
refactor: modular backend architecture with 14 router modules Phase 2 Complete - Backend Refactoring: - Extracted all endpoints to dedicated router modules - main.py: 1878 → 75 lines (-96% reduction) - Created modular structure for maintainability Router Structure (60 endpoints total): ├── auth.py - 7 endpoints (login, logout, password reset) ├── profiles.py - 7 endpoints (CRUD + current user) ├── weight.py - 5 endpoints (tracking + stats) ├── circumference.py - 4 endpoints (body measurements) ├── caliper.py - 4 endpoints (skinfold tracking) ├── activity.py - 6 endpoints (workouts + Apple Health import) ├── nutrition.py - 4 endpoints (diet + FDDB import) ├── photos.py - 3 endpoints (progress photos) ├── insights.py - 8 endpoints (AI analysis + pipeline) ├── prompts.py - 2 endpoints (AI prompt management) ├── admin.py - 7 endpoints (user management) ├── stats.py - 1 endpoint (dashboard stats) ├── exportdata.py - 3 endpoints (CSV/JSON/ZIP export) └── importdata.py - 1 endpoint (ZIP import) Core modules maintained: - db.py: PostgreSQL connection + helpers - auth.py: Auth functions (hash, verify, sessions) - models.py: 11 Pydantic models Benefits: - Self-contained modules with clear responsibilities - Easier to navigate and modify specific features - Improved code organization and readability - 100% functional compatibility maintained - All syntax checks passed Updated CLAUDE.md with new architecture documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 11:15:35 +01:00
router = APIRouter(prefix="/api/nutrition", tags=["nutrition"])
logger = logging.getLogger(__name__)
refactor: modular backend architecture with 14 router modules Phase 2 Complete - Backend Refactoring: - Extracted all endpoints to dedicated router modules - main.py: 1878 → 75 lines (-96% reduction) - Created modular structure for maintainability Router Structure (60 endpoints total): ├── auth.py - 7 endpoints (login, logout, password reset) ├── profiles.py - 7 endpoints (CRUD + current user) ├── weight.py - 5 endpoints (tracking + stats) ├── circumference.py - 4 endpoints (body measurements) ├── caliper.py - 4 endpoints (skinfold tracking) ├── activity.py - 6 endpoints (workouts + Apple Health import) ├── nutrition.py - 4 endpoints (diet + FDDB import) ├── photos.py - 3 endpoints (progress photos) ├── insights.py - 8 endpoints (AI analysis + pipeline) ├── prompts.py - 2 endpoints (AI prompt management) ├── admin.py - 7 endpoints (user management) ├── stats.py - 1 endpoint (dashboard stats) ├── exportdata.py - 3 endpoints (CSV/JSON/ZIP export) └── importdata.py - 1 endpoint (ZIP import) Core modules maintained: - db.py: PostgreSQL connection + helpers - auth.py: Auth functions (hash, verify, sessions) - models.py: 11 Pydantic models Benefits: - Self-contained modules with clear responsibilities - Easier to navigate and modify specific features - Improved code organization and readability - 100% functional compatibility maintained - All syntax checks passed Updated CLAUDE.md with new architecture documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 11:15:35 +01:00
# ── Helper ────────────────────────────────────────────────────────────────────
def _pf(s):
"""Parse float from string (handles comma decimal separator)."""
try: return float(str(s).replace(',','.').strip())
except: return 0.0
# ── Endpoints ─────────────────────────────────────────────────────────────────
@router.post("/import-csv")
async def import_nutrition_csv(file: UploadFile=File(...), x_profile_id: Optional[str]=Header(default=None), session: dict=Depends(require_auth)):
"""Import FDDB nutrition CSV."""
pid = get_pid(x_profile_id)
# Phase 4: Check feature access and ENFORCE
# Note: CSV import can create many entries - we check once before import
access = check_feature_access(pid, 'nutrition_entries')
log_feature_usage(pid, 'nutrition_entries', access, 'import_csv')
if not access['allowed']:
logger.warning(
f"[FEATURE-LIMIT] User {pid} blocked: "
f"nutrition_entries {access['reason']} (used: {access['used']}, limit: {access['limit']})"
)
raise HTTPException(
status_code=403,
detail=f"Limit erreicht: Du hast das Kontingent für Ernährungseinträge überschritten ({access['used']}/{access['limit']}). "
f"Bitte kontaktiere den Admin oder warte bis zum nächsten Reset."
)
refactor: modular backend architecture with 14 router modules Phase 2 Complete - Backend Refactoring: - Extracted all endpoints to dedicated router modules - main.py: 1878 → 75 lines (-96% reduction) - Created modular structure for maintainability Router Structure (60 endpoints total): ├── auth.py - 7 endpoints (login, logout, password reset) ├── profiles.py - 7 endpoints (CRUD + current user) ├── weight.py - 5 endpoints (tracking + stats) ├── circumference.py - 4 endpoints (body measurements) ├── caliper.py - 4 endpoints (skinfold tracking) ├── activity.py - 6 endpoints (workouts + Apple Health import) ├── nutrition.py - 4 endpoints (diet + FDDB import) ├── photos.py - 3 endpoints (progress photos) ├── insights.py - 8 endpoints (AI analysis + pipeline) ├── prompts.py - 2 endpoints (AI prompt management) ├── admin.py - 7 endpoints (user management) ├── stats.py - 1 endpoint (dashboard stats) ├── exportdata.py - 3 endpoints (CSV/JSON/ZIP export) └── importdata.py - 1 endpoint (ZIP import) Core modules maintained: - db.py: PostgreSQL connection + helpers - auth.py: Auth functions (hash, verify, sessions) - models.py: 11 Pydantic models Benefits: - Self-contained modules with clear responsibilities - Easier to navigate and modify specific features - Improved code organization and readability - 100% functional compatibility maintained - All syntax checks passed Updated CLAUDE.md with new architecture documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 11:15:35 +01:00
raw = await file.read()
try: text = raw.decode('utf-8')
except: text = raw.decode('latin-1')
if text.startswith('\ufeff'): text = text[1:]
if not text.strip(): raise HTTPException(400,"Leere Datei")
reader = csv.DictReader(io.StringIO(text), delimiter=';')
days: dict = {}
count = 0
for row in reader:
rd = row.get('datum_tag_monat_jahr_stunde_minute','').strip().strip('"')
if not rd: continue
try:
p = rd.split(' ')[0].split('.')
iso = f"{p[2]}-{p[1]}-{p[0]}"
except: continue
days.setdefault(iso,{'kcal':0,'fat_g':0,'carbs_g':0,'protein_g':0})
days[iso]['kcal'] += _pf(row.get('kj',0))/4.184
days[iso]['fat_g'] += _pf(row.get('fett_g',0))
days[iso]['carbs_g'] += _pf(row.get('kh_g',0))
days[iso]['protein_g'] += _pf(row.get('protein_g',0))
count+=1
inserted=0
new_entries=0
refactor: modular backend architecture with 14 router modules Phase 2 Complete - Backend Refactoring: - Extracted all endpoints to dedicated router modules - main.py: 1878 → 75 lines (-96% reduction) - Created modular structure for maintainability Router Structure (60 endpoints total): ├── auth.py - 7 endpoints (login, logout, password reset) ├── profiles.py - 7 endpoints (CRUD + current user) ├── weight.py - 5 endpoints (tracking + stats) ├── circumference.py - 4 endpoints (body measurements) ├── caliper.py - 4 endpoints (skinfold tracking) ├── activity.py - 6 endpoints (workouts + Apple Health import) ├── nutrition.py - 4 endpoints (diet + FDDB import) ├── photos.py - 3 endpoints (progress photos) ├── insights.py - 8 endpoints (AI analysis + pipeline) ├── prompts.py - 2 endpoints (AI prompt management) ├── admin.py - 7 endpoints (user management) ├── stats.py - 1 endpoint (dashboard stats) ├── exportdata.py - 3 endpoints (CSV/JSON/ZIP export) └── importdata.py - 1 endpoint (ZIP import) Core modules maintained: - db.py: PostgreSQL connection + helpers - auth.py: Auth functions (hash, verify, sessions) - models.py: 11 Pydantic models Benefits: - Self-contained modules with clear responsibilities - Easier to navigate and modify specific features - Improved code organization and readability - 100% functional compatibility maintained - All syntax checks passed Updated CLAUDE.md with new architecture documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 11:15:35 +01:00
with get_db() as conn:
cur = get_cursor(conn)
for iso,vals in days.items():
kcal=round(vals['kcal'],1); fat=round(vals['fat_g'],1)
carbs=round(vals['carbs_g'],1); prot=round(vals['protein_g'],1)
cur.execute("SELECT id FROM nutrition_log WHERE profile_id=%s AND date=%s",(pid,iso))
is_new = not cur.fetchone()
if not is_new:
# UPDATE existing
refactor: modular backend architecture with 14 router modules Phase 2 Complete - Backend Refactoring: - Extracted all endpoints to dedicated router modules - main.py: 1878 → 75 lines (-96% reduction) - Created modular structure for maintainability Router Structure (60 endpoints total): ├── auth.py - 7 endpoints (login, logout, password reset) ├── profiles.py - 7 endpoints (CRUD + current user) ├── weight.py - 5 endpoints (tracking + stats) ├── circumference.py - 4 endpoints (body measurements) ├── caliper.py - 4 endpoints (skinfold tracking) ├── activity.py - 6 endpoints (workouts + Apple Health import) ├── nutrition.py - 4 endpoints (diet + FDDB import) ├── photos.py - 3 endpoints (progress photos) ├── insights.py - 8 endpoints (AI analysis + pipeline) ├── prompts.py - 2 endpoints (AI prompt management) ├── admin.py - 7 endpoints (user management) ├── stats.py - 1 endpoint (dashboard stats) ├── exportdata.py - 3 endpoints (CSV/JSON/ZIP export) └── importdata.py - 1 endpoint (ZIP import) Core modules maintained: - db.py: PostgreSQL connection + helpers - auth.py: Auth functions (hash, verify, sessions) - models.py: 11 Pydantic models Benefits: - Self-contained modules with clear responsibilities - Easier to navigate and modify specific features - Improved code organization and readability - 100% functional compatibility maintained - All syntax checks passed Updated CLAUDE.md with new architecture documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 11:15:35 +01:00
cur.execute("UPDATE nutrition_log SET kcal=%s,protein_g=%s,fat_g=%s,carbs_g=%s WHERE profile_id=%s AND date=%s",
(kcal,prot,fat,carbs,pid,iso))
else:
# INSERT new
refactor: modular backend architecture with 14 router modules Phase 2 Complete - Backend Refactoring: - Extracted all endpoints to dedicated router modules - main.py: 1878 → 75 lines (-96% reduction) - Created modular structure for maintainability Router Structure (60 endpoints total): ├── auth.py - 7 endpoints (login, logout, password reset) ├── profiles.py - 7 endpoints (CRUD + current user) ├── weight.py - 5 endpoints (tracking + stats) ├── circumference.py - 4 endpoints (body measurements) ├── caliper.py - 4 endpoints (skinfold tracking) ├── activity.py - 6 endpoints (workouts + Apple Health import) ├── nutrition.py - 4 endpoints (diet + FDDB import) ├── photos.py - 3 endpoints (progress photos) ├── insights.py - 8 endpoints (AI analysis + pipeline) ├── prompts.py - 2 endpoints (AI prompt management) ├── admin.py - 7 endpoints (user management) ├── stats.py - 1 endpoint (dashboard stats) ├── exportdata.py - 3 endpoints (CSV/JSON/ZIP export) └── importdata.py - 1 endpoint (ZIP import) Core modules maintained: - db.py: PostgreSQL connection + helpers - auth.py: Auth functions (hash, verify, sessions) - models.py: 11 Pydantic models Benefits: - Self-contained modules with clear responsibilities - Easier to navigate and modify specific features - Improved code organization and readability - 100% functional compatibility maintained - All syntax checks passed Updated CLAUDE.md with new architecture documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 11:15:35 +01:00
cur.execute("INSERT INTO nutrition_log (id,profile_id,date,kcal,protein_g,fat_g,carbs_g,source,created) VALUES (%s,%s,%s,%s,%s,%s,%s,'csv',CURRENT_TIMESTAMP)",
(str(uuid.uuid4()),pid,iso,kcal,prot,fat,carbs))
new_entries += 1
refactor: modular backend architecture with 14 router modules Phase 2 Complete - Backend Refactoring: - Extracted all endpoints to dedicated router modules - main.py: 1878 → 75 lines (-96% reduction) - Created modular structure for maintainability Router Structure (60 endpoints total): ├── auth.py - 7 endpoints (login, logout, password reset) ├── profiles.py - 7 endpoints (CRUD + current user) ├── weight.py - 5 endpoints (tracking + stats) ├── circumference.py - 4 endpoints (body measurements) ├── caliper.py - 4 endpoints (skinfold tracking) ├── activity.py - 6 endpoints (workouts + Apple Health import) ├── nutrition.py - 4 endpoints (diet + FDDB import) ├── photos.py - 3 endpoints (progress photos) ├── insights.py - 8 endpoints (AI analysis + pipeline) ├── prompts.py - 2 endpoints (AI prompt management) ├── admin.py - 7 endpoints (user management) ├── stats.py - 1 endpoint (dashboard stats) ├── exportdata.py - 3 endpoints (CSV/JSON/ZIP export) └── importdata.py - 1 endpoint (ZIP import) Core modules maintained: - db.py: PostgreSQL connection + helpers - auth.py: Auth functions (hash, verify, sessions) - models.py: 11 Pydantic models Benefits: - Self-contained modules with clear responsibilities - Easier to navigate and modify specific features - Improved code organization and readability - 100% functional compatibility maintained - All syntax checks passed Updated CLAUDE.md with new architecture documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 11:15:35 +01:00
inserted+=1
# Phase 2: Increment usage counter for each new entry created
for _ in range(new_entries):
increment_feature_usage(pid, 'nutrition_entries')
return {"rows_parsed":count,"days_imported":inserted,"new_entries":new_entries,
refactor: modular backend architecture with 14 router modules Phase 2 Complete - Backend Refactoring: - Extracted all endpoints to dedicated router modules - main.py: 1878 → 75 lines (-96% reduction) - Created modular structure for maintainability Router Structure (60 endpoints total): ├── auth.py - 7 endpoints (login, logout, password reset) ├── profiles.py - 7 endpoints (CRUD + current user) ├── weight.py - 5 endpoints (tracking + stats) ├── circumference.py - 4 endpoints (body measurements) ├── caliper.py - 4 endpoints (skinfold tracking) ├── activity.py - 6 endpoints (workouts + Apple Health import) ├── nutrition.py - 4 endpoints (diet + FDDB import) ├── photos.py - 3 endpoints (progress photos) ├── insights.py - 8 endpoints (AI analysis + pipeline) ├── prompts.py - 2 endpoints (AI prompt management) ├── admin.py - 7 endpoints (user management) ├── stats.py - 1 endpoint (dashboard stats) ├── exportdata.py - 3 endpoints (CSV/JSON/ZIP export) └── importdata.py - 1 endpoint (ZIP import) Core modules maintained: - db.py: PostgreSQL connection + helpers - auth.py: Auth functions (hash, verify, sessions) - models.py: 11 Pydantic models Benefits: - Self-contained modules with clear responsibilities - Easier to navigate and modify specific features - Improved code organization and readability - 100% functional compatibility maintained - All syntax checks passed Updated CLAUDE.md with new architecture documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 11:15:35 +01:00
"date_range":{"from":min(days) if days else None,"to":max(days) if days else None}}
@router.post("")
def create_nutrition(date: str, kcal: float, protein_g: float, fat_g: float, carbs_g: float,
x_profile_id: Optional[str]=Header(default=None), session: dict=Depends(require_auth)):
"""Create or update nutrition entry for a specific date."""
pid = get_pid(x_profile_id)
# Validate date format
try:
datetime.strptime(date, '%Y-%m-%d')
except ValueError:
raise HTTPException(400, "Ungültiges Datumsformat. Erwartet: YYYY-MM-DD")
with get_db() as conn:
cur = get_cursor(conn)
# Check if entry exists
cur.execute("SELECT id FROM nutrition_log WHERE profile_id=%s AND date=%s", (pid, date))
existing = cur.fetchone()
if existing:
# UPDATE existing entry
cur.execute("""
UPDATE nutrition_log
SET kcal=%s, protein_g=%s, fat_g=%s, carbs_g=%s, source='manual'
WHERE id=%s AND profile_id=%s
""", (round(kcal,1), round(protein_g,1), round(fat_g,1), round(carbs_g,1), existing['id'], pid))
return {"success": True, "mode": "updated", "id": existing['id']}
else:
# Phase 4: Check feature access before INSERT
access = check_feature_access(pid, 'nutrition_entries')
log_feature_usage(pid, 'nutrition_entries', access, 'create')
if not access['allowed']:
logger.warning(
f"[FEATURE-LIMIT] User {pid} blocked: "
f"nutrition_entries {access['reason']} (used: {access['used']}, limit: {access['limit']})"
)
raise HTTPException(
status_code=403,
detail=f"Limit erreicht: Du hast das Kontingent für Ernährungseinträge überschritten ({access['used']}/{access['limit']}). "
f"Bitte kontaktiere den Admin oder warte bis zum nächsten Reset."
)
# INSERT new entry
new_id = str(uuid.uuid4())
cur.execute("""
INSERT INTO nutrition_log (id, profile_id, date, kcal, protein_g, fat_g, carbs_g, source, created)
VALUES (%s, %s, %s, %s, %s, %s, %s, 'manual', CURRENT_TIMESTAMP)
""", (new_id, pid, date, round(kcal,1), round(protein_g,1), round(fat_g,1), round(carbs_g,1)))
# Phase 2: Increment usage counter
increment_feature_usage(pid, 'nutrition_entries')
return {"success": True, "mode": "created", "id": new_id}
refactor: modular backend architecture with 14 router modules Phase 2 Complete - Backend Refactoring: - Extracted all endpoints to dedicated router modules - main.py: 1878 → 75 lines (-96% reduction) - Created modular structure for maintainability Router Structure (60 endpoints total): ├── auth.py - 7 endpoints (login, logout, password reset) ├── profiles.py - 7 endpoints (CRUD + current user) ├── weight.py - 5 endpoints (tracking + stats) ├── circumference.py - 4 endpoints (body measurements) ├── caliper.py - 4 endpoints (skinfold tracking) ├── activity.py - 6 endpoints (workouts + Apple Health import) ├── nutrition.py - 4 endpoints (diet + FDDB import) ├── photos.py - 3 endpoints (progress photos) ├── insights.py - 8 endpoints (AI analysis + pipeline) ├── prompts.py - 2 endpoints (AI prompt management) ├── admin.py - 7 endpoints (user management) ├── stats.py - 1 endpoint (dashboard stats) ├── exportdata.py - 3 endpoints (CSV/JSON/ZIP export) └── importdata.py - 1 endpoint (ZIP import) Core modules maintained: - db.py: PostgreSQL connection + helpers - auth.py: Auth functions (hash, verify, sessions) - models.py: 11 Pydantic models Benefits: - Self-contained modules with clear responsibilities - Easier to navigate and modify specific features - Improved code organization and readability - 100% functional compatibility maintained - All syntax checks passed Updated CLAUDE.md with new architecture documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 11:15:35 +01:00
@router.get("")
def list_nutrition(limit: int=365, x_profile_id: Optional[str]=Header(default=None), session: dict=Depends(require_auth)):
"""Get nutrition entries for current profile."""
pid = get_pid(x_profile_id)
with get_db() as conn:
cur = get_cursor(conn)
cur.execute(
"SELECT * FROM nutrition_log WHERE profile_id=%s ORDER BY date DESC LIMIT %s", (pid,limit))
return [r2d(r) for r in cur.fetchall()]
@router.get("/by-date/{date}")
def get_nutrition_by_date(date: str, x_profile_id: Optional[str]=Header(default=None), session: dict=Depends(require_auth)):
"""Get nutrition entry for a specific date."""
pid = get_pid(x_profile_id)
with get_db() as conn:
cur = get_cursor(conn)
cur.execute("SELECT * FROM nutrition_log WHERE profile_id=%s AND date=%s", (pid, date))
row = cur.fetchone()
return r2d(row) if row else None
refactor: modular backend architecture with 14 router modules Phase 2 Complete - Backend Refactoring: - Extracted all endpoints to dedicated router modules - main.py: 1878 → 75 lines (-96% reduction) - Created modular structure for maintainability Router Structure (60 endpoints total): ├── auth.py - 7 endpoints (login, logout, password reset) ├── profiles.py - 7 endpoints (CRUD + current user) ├── weight.py - 5 endpoints (tracking + stats) ├── circumference.py - 4 endpoints (body measurements) ├── caliper.py - 4 endpoints (skinfold tracking) ├── activity.py - 6 endpoints (workouts + Apple Health import) ├── nutrition.py - 4 endpoints (diet + FDDB import) ├── photos.py - 3 endpoints (progress photos) ├── insights.py - 8 endpoints (AI analysis + pipeline) ├── prompts.py - 2 endpoints (AI prompt management) ├── admin.py - 7 endpoints (user management) ├── stats.py - 1 endpoint (dashboard stats) ├── exportdata.py - 3 endpoints (CSV/JSON/ZIP export) └── importdata.py - 1 endpoint (ZIP import) Core modules maintained: - db.py: PostgreSQL connection + helpers - auth.py: Auth functions (hash, verify, sessions) - models.py: 11 Pydantic models Benefits: - Self-contained modules with clear responsibilities - Easier to navigate and modify specific features - Improved code organization and readability - 100% functional compatibility maintained - All syntax checks passed Updated CLAUDE.md with new architecture documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 11:15:35 +01:00
@router.get("/correlations")
def nutrition_correlations(x_profile_id: Optional[str]=Header(default=None), session: dict=Depends(require_auth)):
"""Get nutrition data correlated with weight and body fat."""
pid = get_pid(x_profile_id)
with get_db() as conn:
cur = get_cursor(conn)
cur.execute("SELECT * FROM nutrition_log WHERE profile_id=%s ORDER BY date",(pid,))
nutr={r['date']:r2d(r) for r in cur.fetchall()}
cur.execute("SELECT date,weight FROM weight_log WHERE profile_id=%s ORDER BY date",(pid,))
wlog={r['date']:r['weight'] for r in cur.fetchall()}
cur.execute("SELECT date,lean_mass,body_fat_pct FROM caliper_log WHERE profile_id=%s ORDER BY date",(pid,))
cals=sorted([r2d(r) for r in cur.fetchall()],key=lambda x:x['date'])
all_dates=sorted(set(list(nutr)+list(wlog)))
mi,last_cal,cal_by_date=0,{},{}
for d in all_dates:
while mi<len(cals) and cals[mi]['date']<=d: last_cal=cals[mi]; mi+=1
if last_cal: cal_by_date[d]=last_cal
result=[]
for d in all_dates:
if d not in nutr and d not in wlog: continue
row={'date':d}
if d in nutr: row.update({k:float(nutr[d][k]) if nutr[d][k] is not None else None for k in ['kcal','protein_g','fat_g','carbs_g']})
if d in wlog: row['weight']=float(wlog[d])
if d in cal_by_date:
lm = cal_by_date[d].get('lean_mass')
bf = cal_by_date[d].get('body_fat_pct')
row['lean_mass']=float(lm) if lm is not None else None
row['body_fat_pct']=float(bf) if bf is not None else None
result.append(row)
return result
@router.get("/weekly")
def nutrition_weekly(weeks: int=16, x_profile_id: Optional[str]=Header(default=None), session: dict=Depends(require_auth)):
"""Get nutrition data aggregated by week."""
pid = get_pid(x_profile_id)
with get_db() as conn:
cur = get_cursor(conn)
cur.execute("SELECT * FROM nutrition_log WHERE profile_id=%s ORDER BY date DESC LIMIT %s",(pid,weeks*7))
rows=[r2d(r) for r in cur.fetchall()]
if not rows: return []
wm={}
for d in rows:
# Handle both datetime.date objects (from DB) and strings
date_obj = d['date'] if hasattr(d['date'], 'strftime') else datetime.strptime(d['date'],'%Y-%m-%d')
wk = date_obj.strftime('%Y-W%V')
refactor: modular backend architecture with 14 router modules Phase 2 Complete - Backend Refactoring: - Extracted all endpoints to dedicated router modules - main.py: 1878 → 75 lines (-96% reduction) - Created modular structure for maintainability Router Structure (60 endpoints total): ├── auth.py - 7 endpoints (login, logout, password reset) ├── profiles.py - 7 endpoints (CRUD + current user) ├── weight.py - 5 endpoints (tracking + stats) ├── circumference.py - 4 endpoints (body measurements) ├── caliper.py - 4 endpoints (skinfold tracking) ├── activity.py - 6 endpoints (workouts + Apple Health import) ├── nutrition.py - 4 endpoints (diet + FDDB import) ├── photos.py - 3 endpoints (progress photos) ├── insights.py - 8 endpoints (AI analysis + pipeline) ├── prompts.py - 2 endpoints (AI prompt management) ├── admin.py - 7 endpoints (user management) ├── stats.py - 1 endpoint (dashboard stats) ├── exportdata.py - 3 endpoints (CSV/JSON/ZIP export) └── importdata.py - 1 endpoint (ZIP import) Core modules maintained: - db.py: PostgreSQL connection + helpers - auth.py: Auth functions (hash, verify, sessions) - models.py: 11 Pydantic models Benefits: - Self-contained modules with clear responsibilities - Easier to navigate and modify specific features - Improved code organization and readability - 100% functional compatibility maintained - All syntax checks passed Updated CLAUDE.md with new architecture documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 11:15:35 +01:00
wm.setdefault(wk,[]).append(d)
result=[]
for wk in sorted(wm):
en=wm[wk]; n=len(en)
def avg(k): return round(sum(float(e.get(k) or 0) for e in en)/n,1)
result.append({'week':wk,'days':n,'kcal':avg('kcal'),'protein_g':avg('protein_g'),'fat_g':avg('fat_g'),'carbs_g':avg('carbs_g')})
return result
@router.get("/import-history")
def import_history(x_profile_id: Optional[str]=Header(default=None), session: dict=Depends(require_auth)):
"""Get import history by grouping entries by created timestamp."""
pid = get_pid(x_profile_id)
with get_db() as conn:
cur = get_cursor(conn)
cur.execute("""
SELECT
DATE(created) as import_date,
COUNT(*) as count,
MIN(date) as date_from,
MAX(date) as date_to,
MAX(created) as last_created
FROM nutrition_log
WHERE profile_id=%s AND source='csv'
GROUP BY DATE(created)
ORDER BY DATE(created) DESC
""", (pid,))
return [r2d(r) for r in cur.fetchall()]
@router.put("/{entry_id}")
def update_nutrition(entry_id: str, kcal: float, protein_g: float, fat_g: float, carbs_g: float,
x_profile_id: Optional[str]=Header(default=None), session: dict=Depends(require_auth)):
"""Update nutrition entry macros."""
pid = get_pid(x_profile_id)
with get_db() as conn:
cur = get_cursor(conn)
# Verify ownership
cur.execute("SELECT id FROM nutrition_log WHERE id=%s AND profile_id=%s", (entry_id, pid))
if not cur.fetchone():
raise HTTPException(404, "Eintrag nicht gefunden")
cur.execute("""
UPDATE nutrition_log
SET kcal=%s, protein_g=%s, fat_g=%s, carbs_g=%s
WHERE id=%s AND profile_id=%s
""", (round(kcal,1), round(protein_g,1), round(fat_g,1), round(carbs_g,1), entry_id, pid))
return {"success": True}
@router.delete("/{entry_id}")
def delete_nutrition(entry_id: str, x_profile_id: Optional[str]=Header(default=None), session: dict=Depends(require_auth)):
"""Delete nutrition entry."""
pid = get_pid(x_profile_id)
with get_db() as conn:
cur = get_cursor(conn)
# Verify ownership
cur.execute("SELECT id FROM nutrition_log WHERE id=%s AND profile_id=%s", (entry_id, pid))
if not cur.fetchone():
raise HTTPException(404, "Eintrag nicht gefunden")
cur.execute("DELETE FROM nutrition_log WHERE id=%s AND profile_id=%s", (entry_id, pid))
return {"success": True}