mitai-jinkendo/backend/routers/weight.py
Lars b4a1856f79
All checks were successful
Deploy Development / deploy (push) Successful in 58s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
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

82 lines
3.3 KiB
Python

"""
Weight Tracking Endpoints for Mitai Jinkendo
Handles weight log CRUD operations and statistics.
"""
import uuid
from typing import Optional
from fastapi import APIRouter, Header, Depends
from db import get_db, get_cursor, r2d
from auth import require_auth
from models import WeightEntry
from routers.profiles import get_pid
router = APIRouter(prefix="/api/weight", tags=["weight"])
@router.get("")
def list_weight(limit: int=365, x_profile_id: Optional[str]=Header(default=None), session: dict=Depends(require_auth)):
"""Get weight entries for current profile."""
pid = get_pid(x_profile_id)
with get_db() as conn:
cur = get_cursor(conn)
cur.execute(
"SELECT * FROM weight_log WHERE profile_id=%s ORDER BY date DESC LIMIT %s", (pid,limit))
return [r2d(r) for r in cur.fetchall()]
@router.post("")
def upsert_weight(e: WeightEntry, x_profile_id: Optional[str]=Header(default=None), session: dict=Depends(require_auth)):
"""Create or update weight entry (upsert by date)."""
pid = get_pid(x_profile_id)
with get_db() as conn:
cur = get_cursor(conn)
cur.execute("SELECT id FROM weight_log WHERE profile_id=%s AND date=%s", (pid,e.date))
ex = cur.fetchone()
if ex:
cur.execute("UPDATE weight_log SET weight=%s,note=%s WHERE id=%s", (e.weight,e.note,ex['id']))
wid = ex['id']
else:
wid = str(uuid.uuid4())
cur.execute("INSERT INTO weight_log (id,profile_id,date,weight,note,created) VALUES (%s,%s,%s,%s,%s,CURRENT_TIMESTAMP)",
(wid,pid,e.date,e.weight,e.note))
return {"id":wid,"date":e.date,"weight":e.weight}
@router.put("/{wid}")
def update_weight(wid: str, e: WeightEntry, x_profile_id: Optional[str]=Header(default=None), session: dict=Depends(require_auth)):
"""Update existing weight entry."""
pid = get_pid(x_profile_id)
with get_db() as conn:
cur = get_cursor(conn)
cur.execute("UPDATE weight_log SET date=%s,weight=%s,note=%s WHERE id=%s AND profile_id=%s",
(e.date,e.weight,e.note,wid,pid))
return {"id":wid}
@router.delete("/{wid}")
def delete_weight(wid: str, x_profile_id: Optional[str]=Header(default=None), session: dict=Depends(require_auth)):
"""Delete weight entry."""
pid = get_pid(x_profile_id)
with get_db() as conn:
cur = get_cursor(conn)
cur.execute("DELETE FROM weight_log WHERE id=%s AND profile_id=%s", (wid,pid))
return {"ok":True}
@router.get("/stats")
def weight_stats(x_profile_id: Optional[str]=Header(default=None), session: dict=Depends(require_auth)):
"""Get weight statistics (last 90 days)."""
pid = get_pid(x_profile_id)
with get_db() as conn:
cur = get_cursor(conn)
cur.execute("SELECT date,weight FROM weight_log WHERE profile_id=%s ORDER BY date DESC LIMIT 90", (pid,))
rows = cur.fetchall()
if not rows: return {"count":0,"latest":None,"prev":None,"min":None,"max":None,"avg_7d":None}
w=[float(r['weight']) for r in rows]
return {"count":len(rows),"latest":{"date":rows[0]['date'],"weight":float(rows[0]['weight'])},
"prev":{"date":rows[1]['date'],"weight":float(rows[1]['weight'])} if len(rows)>1 else None,
"min":min(w),"max":max(w),"avg_7d":round(sum(w[:7])/min(7,len(w)),2)}