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:
parent
9a306bc250
commit
5dd5d39ca6
8 changed files with 818 additions and 6 deletions
67
backend/app/schemas/nutrition.py
Normal file
67
backend/app/schemas/nutrition.py
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class NutritionGoalIn(BaseModel):
|
||||
calorie_target: int | None = None
|
||||
protein_g_target: int | None = None
|
||||
fat_g_target: int | None = None
|
||||
net_carbs_g_cap: int | None = None
|
||||
training_calorie_target: int | None = None
|
||||
training_protein_g_target: int | None = None
|
||||
training_fat_g_target: int | None = None
|
||||
eating_window_start_hour: int | None = None
|
||||
eating_window_end_hour: int | None = None
|
||||
default_fast_hours: int | None = None
|
||||
diet_style: str | None = None
|
||||
timezone_str: str | None = None
|
||||
|
||||
|
||||
class NutritionGoalOut(BaseModel):
|
||||
id: str
|
||||
calorie_target: int
|
||||
protein_g_target: int
|
||||
fat_g_target: int
|
||||
net_carbs_g_cap: int
|
||||
training_calorie_target: int | None
|
||||
training_protein_g_target: int | None
|
||||
training_fat_g_target: int | None
|
||||
eating_window_start_hour: int
|
||||
eating_window_end_hour: int
|
||||
default_fast_hours: int
|
||||
diet_style: str
|
||||
timezone_str: str
|
||||
|
||||
|
||||
class FastStart(BaseModel):
|
||||
target_hours: int
|
||||
fast_type: str = "daily" # daily, weekly_24, long_48, long_72, extended
|
||||
started_at: datetime | None = None # defaults to now if omitted
|
||||
notes: str | None = None
|
||||
|
||||
|
||||
class FastUpdate(BaseModel):
|
||||
ended_at: datetime | None = None # defaults to now if omitted, terminates the fast
|
||||
notes: str | None = None
|
||||
|
||||
|
||||
class FastOut(BaseModel):
|
||||
id: str
|
||||
started_at: datetime
|
||||
ended_at: datetime | None
|
||||
target_hours: int
|
||||
fast_type: str
|
||||
completed: bool
|
||||
broken_early: bool
|
||||
points_awarded: int
|
||||
elapsed_hours: float
|
||||
notes: str | None
|
||||
|
||||
|
||||
class ElectrolyteIn(BaseModel):
|
||||
sodium_mg: int = 0
|
||||
potassium_mg: int = 0
|
||||
magnesium_mg: int = 0
|
||||
notes: str | None = None
|
||||
Loading…
Add table
Add a link
Reference in a new issue