The frontend was sending quality_filter_level to the backend, but the Pydantic ProfileUpdate model didn't include this field, so it was silently ignored. Profile updates never actually saved the filter. This is why the charts didn't react to filter changes - the backend database was never updated. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
130 lines
4.0 KiB
Python
130 lines
4.0 KiB
Python
"""
|
|
Pydantic Models for Mitai Jinkendo API
|
|
|
|
Data validation schemas for request/response bodies.
|
|
"""
|
|
from typing import Optional
|
|
from pydantic import BaseModel
|
|
|
|
|
|
# ── Profile Models ────────────────────────────────────────────────────────────
|
|
|
|
class ProfileCreate(BaseModel):
|
|
name: str
|
|
avatar_color: Optional[str] = '#1D9E75'
|
|
sex: Optional[str] = 'm'
|
|
dob: Optional[str] = None
|
|
height: Optional[float] = 178
|
|
goal_weight: Optional[float] = None
|
|
goal_bf_pct: Optional[float] = None
|
|
|
|
|
|
class ProfileUpdate(BaseModel):
|
|
name: Optional[str] = None
|
|
avatar_color: Optional[str] = None
|
|
sex: Optional[str] = None
|
|
dob: Optional[str] = None
|
|
height: Optional[float] = None
|
|
goal_weight: Optional[float] = None
|
|
goal_bf_pct: Optional[float] = None
|
|
quality_filter_level: Optional[str] = None # Issue #31: Global quality filter
|
|
|
|
|
|
# ── Tracking Models ───────────────────────────────────────────────────────────
|
|
|
|
class WeightEntry(BaseModel):
|
|
date: str
|
|
weight: float
|
|
note: Optional[str] = None
|
|
|
|
|
|
class CircumferenceEntry(BaseModel):
|
|
date: str
|
|
c_neck: Optional[float] = None
|
|
c_chest: Optional[float] = None
|
|
c_waist: Optional[float] = None
|
|
c_belly: Optional[float] = None
|
|
c_hip: Optional[float] = None
|
|
c_thigh: Optional[float] = None
|
|
c_calf: Optional[float] = None
|
|
c_arm: Optional[float] = None
|
|
notes: Optional[str] = None
|
|
photo_id: Optional[str] = None
|
|
|
|
|
|
class CaliperEntry(BaseModel):
|
|
date: str
|
|
sf_method: Optional[str] = 'jackson3'
|
|
sf_chest: Optional[float] = None
|
|
sf_axilla: Optional[float] = None
|
|
sf_triceps: Optional[float] = None
|
|
sf_subscap: Optional[float] = None
|
|
sf_suprailiac: Optional[float] = None
|
|
sf_abdomen: Optional[float] = None
|
|
sf_thigh: Optional[float] = None
|
|
sf_calf_med: Optional[float] = None
|
|
sf_lowerback: Optional[float] = None
|
|
sf_biceps: Optional[float] = None
|
|
body_fat_pct: Optional[float] = None
|
|
lean_mass: Optional[float] = None
|
|
fat_mass: Optional[float] = None
|
|
notes: Optional[str] = None
|
|
|
|
|
|
class ActivityEntry(BaseModel):
|
|
date: str
|
|
start_time: Optional[str] = None
|
|
end_time: Optional[str] = None
|
|
activity_type: str
|
|
duration_min: Optional[float] = None
|
|
kcal_active: Optional[float] = None
|
|
kcal_resting: Optional[float] = None
|
|
hr_avg: Optional[float] = None
|
|
hr_max: Optional[float] = None
|
|
distance_km: Optional[float] = None
|
|
rpe: Optional[int] = None
|
|
source: Optional[str] = 'manual'
|
|
notes: Optional[str] = None
|
|
training_type_id: Optional[int] = None # v9d: Training type categorization
|
|
training_category: Optional[str] = None # v9d: Denormalized category
|
|
training_subcategory: Optional[str] = None # v9d: Denormalized subcategory
|
|
|
|
|
|
class NutritionDay(BaseModel):
|
|
date: str
|
|
kcal: Optional[float] = None
|
|
protein_g: Optional[float] = None
|
|
fat_g: Optional[float] = None
|
|
carbs_g: Optional[float] = None
|
|
|
|
|
|
# ── Auth Models ───────────────────────────────────────────────────────────────
|
|
|
|
class LoginRequest(BaseModel):
|
|
email: str
|
|
password: str
|
|
|
|
|
|
class PasswordResetRequest(BaseModel):
|
|
email: str
|
|
|
|
|
|
class PasswordResetConfirm(BaseModel):
|
|
token: str
|
|
new_password: str
|
|
|
|
|
|
class RegisterRequest(BaseModel):
|
|
name: str
|
|
email: str
|
|
password: str
|
|
|
|
|
|
# ── Admin Models ──────────────────────────────────────────────────────────────
|
|
|
|
class AdminProfileUpdate(BaseModel):
|
|
role: Optional[str] = None
|
|
ai_enabled: Optional[int] = None
|
|
ai_limit_day: Optional[int] = None
|
|
export_enabled: Optional[int] = None
|