# app/models/schemas/meal_plan.py """Pydantic schemas for meal planning endpoints.""" from __future__ import annotations from pydantic import BaseModel, Field VALID_MEAL_TYPES = {"breakfast", "lunch", "dinner", "snack"} class CreatePlanRequest(BaseModel): week_start: str # ISO date string, e.g. "2026-04-14" (must be Monday) meal_types: list[str] = Field(default_factory=lambda: ["dinner"]) class UpsertSlotRequest(BaseModel): recipe_id: int | None = None servings: float = Field(2.0, gt=0) custom_label: str | None = None class SlotSummary(BaseModel): id: int plan_id: int day_of_week: int meal_type: str recipe_id: int | None recipe_title: str | None servings: float custom_label: str | None class PlanSummary(BaseModel): id: int week_start: str meal_types: list[str] slots: list[SlotSummary] created_at: str class RetailerLink(BaseModel): retailer: str label: str url: str class GapItem(BaseModel): ingredient_name: str needed_raw: str | None # e.g. "2 cups" from recipe text have_quantity: float | None # from pantry have_unit: str | None covered: bool # True = pantry has it retailer_links: list[RetailerLink] = Field(default_factory=list) class ShoppingListResponse(BaseModel): plan_id: int gap_items: list[GapItem] covered_items: list[GapItem] disclosure: str | None = None # affiliate disclosure text when links present class PrepTaskSummary(BaseModel): id: int recipe_id: int | None task_label: str duration_minutes: int | None sequence_order: int equipment: str | None is_parallel: bool notes: str | None user_edited: bool class PrepSessionSummary(BaseModel): id: int plan_id: int scheduled_date: str status: str tasks: list[PrepTaskSummary] class UpdatePrepTaskRequest(BaseModel): duration_minutes: int | None = None sequence_order: int | None = None notes: str | None = None equipment: str | None = None