82 lines
2.4 KiB
Python
82 lines
2.4 KiB
Python
from datetime import date, datetime
|
|
from pydantic import BaseModel, ConfigDict, EmailStr, Field, model_validator
|
|
from .models import EntryKind
|
|
|
|
|
|
class Register(BaseModel):
|
|
email: EmailStr
|
|
password: str = Field(min_length=12, max_length=128)
|
|
|
|
|
|
class Token(BaseModel):
|
|
access_token: str
|
|
token_type: str = "bearer"
|
|
|
|
|
|
class ProfileIn(BaseModel):
|
|
display_name: str | None = Field(None, max_length=80)
|
|
birth_date: date | None = None
|
|
height_cm: float | None = Field(None, ge=80, le=250)
|
|
objective: str | None = Field(None, max_length=120)
|
|
health_context: str | None = Field(None, max_length=1000)
|
|
consent_health_data: bool = False
|
|
|
|
|
|
class ProfileOut(ProfileIn):
|
|
user_id: str
|
|
updated_at: datetime
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
RANGES = {
|
|
EntryKind.weight: (20, 500), EntryKind.glucose: (20, 600), EntryKind.water: (0, 15000),
|
|
EntryKind.activity: (0, 1440), EntryKind.sleep: (0, 24), EntryKind.mood: (1, 5),
|
|
EntryKind.meal: (0, 10000),
|
|
}
|
|
|
|
|
|
class EntryIn(BaseModel):
|
|
kind: EntryKind
|
|
value: float
|
|
unit: str = Field(min_length=1, max_length=24)
|
|
note: str | None = Field(None, max_length=500)
|
|
recorded_at: datetime | None = None
|
|
|
|
@model_validator(mode="after")
|
|
def sensible_range(self):
|
|
if self.kind == EntryKind.glucose:
|
|
glucose_ranges = {"mg/dL": (20, 600), "mmol/L": (1.1, 33.3)}
|
|
if self.unit not in glucose_ranges:
|
|
raise ValueError("glucose unit must be mg/dL or mmol/L")
|
|
low, high = glucose_ranges[self.unit]
|
|
else:
|
|
low, high = RANGES[self.kind]
|
|
if not low <= self.value <= high:
|
|
raise ValueError(f"value must be between {low} and {high} for {self.kind.value}")
|
|
return self
|
|
|
|
|
|
class EntryOut(EntryIn):
|
|
id: str
|
|
user_id: str
|
|
recorded_at: datetime
|
|
created_at: datetime
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
class CoachMessage(BaseModel):
|
|
role: str = Field(pattern="^(user|assistant)$")
|
|
content: str = Field(min_length=1, max_length=2000)
|
|
|
|
|
|
class CoachChatIn(BaseModel):
|
|
message: str = Field(min_length=1, max_length=2000)
|
|
history: list[CoachMessage] = Field(default_factory=list, max_length=12)
|
|
|
|
|
|
class CoachChatOut(BaseModel):
|
|
assistant: str = "Peg"
|
|
message: str
|
|
source: str = Field(description="model, safety, or fallback")
|
|
medical_notice: str = "Conseils généraux uniquement — Peg ne remplace pas un professionnel de santé."
|