Feature: nutrition module (keto macros + intermittent fasting + electrolytes)

Backend:
- models/nutrition.py: NutritionGoal, Fast, ElectrolyteLog tables
- schemas/nutrition.py: Pydantic schemas for new endpoints
- routers/nutrition.py: /api/nutrition/{today,goals,fasts,electrolytes}
- main.py: register router + migration for new tables + seed keto point rules

Frontend:
- pages/Nutrition.jsx: macro bars, fast timer, eating window, electrolyte quick-add
- App.jsx: /nutrition route + Keto nav tab
- api.js: nutrition endpoints

Points schema for fasts (tier-based partial credit):
16:8=25, 18:6=40, 20:4=60, 24h=200, 36h=350, 48h=500, 72h=1000

Defaults tuned for Scot: 135kg M 44yo strict keto, 12:00-20:00 window,
Asia/Bangkok timezone, 2400/2600 cal rest/training, 20g net carb cap.
This commit is contained in:
claude 2026-04-12 05:13:52 +00:00
parent 9a306bc250
commit 5dd5d39ca6
8 changed files with 818 additions and 6 deletions

View file

@ -63,7 +63,7 @@ async def lifespan(app: FastAPI):
logger.info("Fitness Rewards backend shutting down")
app = FastAPI(title="Fitness Rewards", version="0.2.0", lifespan=lifespan)
app = FastAPI(title="Fitness Rewards", version="0.3.0", lifespan=lifespan)
app.add_middleware(
CORSMiddleware,
@ -83,6 +83,7 @@ from app.routers.integrations import router as integrations_router
from app.routers.programs import router as programs_router
from app.routers.catalog import router as catalog_router
from app.routers.support import router as support_router
from app.routers.nutrition import router as nutrition_router
app.include_router(auth_router)
app.include_router(onboarding_router)
@ -94,11 +95,12 @@ app.include_router(integrations_router)
app.include_router(catalog_router)
app.include_router(support_router)
app.include_router(programs_router)
app.include_router(nutrition_router)
@app.get("/api/health")
async def health():
return {"status": "ok", "version": "0.2.0"}
return {"status": "ok", "version": "0.3.0"}
@ -134,6 +136,27 @@ async def run_migrations():
END $$;
"""))
# v3: Seed keto point rules (fast_completed, keto_day, meal_logged)
await conn.execute(text("""
DO $$ BEGIN
INSERT INTO point_rules (id, user_id, category, points, unit, is_active)
SELECT gen_random_uuid(), u.id, 'fast_completed', 200, 'per_event', TRUE
FROM users u
WHERE NOT EXISTS (
SELECT 1 FROM point_rules pr
WHERE pr.user_id = u.id AND pr.category = 'fast_completed'
);
INSERT INTO point_rules (id, user_id, category, points, unit, is_active)
SELECT gen_random_uuid(), u.id, 'keto_compliant_day', 100, 'per_event', TRUE
FROM users u
WHERE NOT EXISTS (
SELECT 1 FROM point_rules pr
WHERE pr.user_id = u.id AND pr.category = 'keto_compliant_day'
);
EXCEPTION WHEN undefined_table THEN NULL;
END $$;
"""))
async def seed_resources():
"""Seed the resource library with curated fitness programs."""