Health page: VO2 max gauge, layout improvements, 3Y/5Y trends, biological sex profile
- Add biological_sex field to User model, profile API, and ProfilePage toggle (male/female) — used to select the correct ACSM VO2 max threshold table - Replace simple VO2 max number in daily snapshot with a colour-coded SVG radial gauge (Very Poor=red, Poor=orange, Fair=green, Good=blue, Excellent=purple) driven by sex- and age-appropriate thresholds - Shrink Sleep widget to half-width, expand Heart & HRV to half-width; reorganise Heart & HRV internals into a 2×2 grid to reduce vertical height - Add connectNulls + showDots to VO2 Max trend chart so sparse readings connect with a continuous line - Add 3Y and 5Y range options to the Trends selector; increase allDays limit to 2000 for full 5yr snapshot navigation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@ class ProfileUpdate(BaseModel):
|
||||
resting_heart_rate: Optional[int] = None
|
||||
birth_year: Optional[int] = None
|
||||
height_cm: Optional[float] = None
|
||||
biological_sex: Optional[str] = None
|
||||
|
||||
|
||||
class ProfileOut(BaseModel):
|
||||
@@ -29,6 +30,7 @@ class ProfileOut(BaseModel):
|
||||
resting_heart_rate: Optional[int]
|
||||
birth_year: Optional[int]
|
||||
height_cm: Optional[float]
|
||||
biological_sex: Optional[str]
|
||||
estimated_max_hr: Optional[int]
|
||||
is_admin: bool
|
||||
|
||||
@@ -72,6 +74,10 @@ async def update_profile(
|
||||
if not (50 <= body.height_cm <= 300):
|
||||
raise HTTPException(400, "Height must be 50–300 cm")
|
||||
current_user.height_cm = body.height_cm
|
||||
if body.biological_sex is not None:
|
||||
if body.biological_sex not in ('male', 'female', ''):
|
||||
raise HTTPException(400, "biological_sex must be 'male' or 'female'")
|
||||
current_user.biological_sex = body.biological_sex or None
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(current_user)
|
||||
|
||||
@@ -64,6 +64,15 @@ async def init_db():
|
||||
except Exception as e:
|
||||
print(f"health_metrics column migration skipped: {e}")
|
||||
|
||||
# biological_sex column on users added after initial creation
|
||||
try:
|
||||
async with engine.begin() as conn:
|
||||
await conn.execute(text(
|
||||
"ALTER TABLE users ADD COLUMN IF NOT EXISTS biological_sex VARCHAR(8)"
|
||||
))
|
||||
except Exception as e:
|
||||
print(f"users.biological_sex column migration skipped: {e}")
|
||||
|
||||
# route_segments auto_generated column added after initial creation
|
||||
try:
|
||||
async with engine.begin() as conn:
|
||||
|
||||
@@ -27,6 +27,7 @@ class User(Base):
|
||||
resting_heart_rate = Column(Integer, nullable=True)
|
||||
birth_year = Column(Integer, nullable=True)
|
||||
height_cm = Column(Float, nullable=True)
|
||||
biological_sex = Column(String(8), nullable=True) # 'male' | 'female'
|
||||
|
||||
# PocketID config (stored per-user so admin can set via UI)
|
||||
pocketid_issuer = Column(String(512), nullable=True)
|
||||
|
||||
Reference in New Issue
Block a user