"""Pydantic request/response schemas — the API contract (spec §8.3 rule 1). Defined separately from ORM models; services convert via from_attributes. Validation happens here at the boundary (spec §8.1 rule 11). """ from datetime import date, datetime from typing import Literal from pydantic import BaseModel, ConfigDict, Field UnitType = Literal["weight", "count"] FoodSource = Literal["openfoodfacts", "manual", "meal"] MealSlot = Literal["breakfast", "lunch", "dinner", "snack"] class FoodCreate(BaseModel): name: str brand: str | None = None barcode: str | None = None source: FoodSource = "manual" unit_type: UnitType = "weight" calories_per_unit: float | None = Field(default=None, gt=0) protein_per_unit: float | None = Field(default=None, ge=0) carbs_per_unit: float | None = Field(default=None, ge=0) fat_per_unit: float | None = Field(default=None, ge=0) serving_size_g: float | None = Field(default=None, gt=0) serving_name: str | None = None class FoodRead(BaseModel): model_config = ConfigDict(from_attributes=True) id: int name: str brand: str | None barcode: str | None source: FoodSource is_meal: bool unit_type: UnitType calories_per_unit: float | None protein_per_unit: float | None carbs_per_unit: float | None fat_per_unit: float | None serving_size_g: float | None serving_name: str | None deleted_at: datetime | None class LogEntryCreate(BaseModel): food_id: int quantity: float = Field(gt=0) meal_slot: MealSlot | None = None date: date # client-supplied YYYY-MM-DD (spec §8.1 rule 8) class LogEntryRead(BaseModel): model_config = ConfigDict(from_attributes=True) id: int date: date food_id: int quantity: float meal_slot: MealSlot | None sort_order: int class TargetRead(BaseModel): model_config = ConfigDict(from_attributes=True) id: int start_date: date end_date: date | None calories: int protein_g: float | None carbs_g: float | None fat_g: float | None