from __future__ import annotations from decimal import Decimal from typing import List, Optional from uuid import UUID from pydantic import BaseModel, Field, field_validator class IngredientBase(BaseModel): name: str = Field(min_length=1, max_length=200) aliases: List[str] = Field(default_factory=list) aisle: Optional[str] = Field(default=None, max_length=100) unit: Optional[str] = Field(default=None, max_length=50) typical_price: Optional[Decimal] = None @field_validator("aliases") @classmethod def _strip_and_drop_empty(cls, v: List[str]) -> List[str]: return [s.strip() for s in v if s and s.strip()] class IngredientCreate(IngredientBase): pass class IngredientUpdate(BaseModel): name: Optional[str] = Field(default=None, min_length=1, max_length=200) aliases: Optional[List[str]] = None aisle: Optional[str] = Field(default=None, max_length=100) unit: Optional[str] = Field(default=None, max_length=50) typical_price: Optional[Decimal] = None @field_validator("aliases") @classmethod def _strip_and_drop_empty(cls, v: Optional[List[str]]) -> Optional[List[str]]: if v is None: return None return [s.strip() for s in v if s and s.strip()] class IngredientRead(IngredientBase): id: UUID model_config = {"from_attributes": True} class IngredientGroceryMatchRead(BaseModel): id: UUID ingredient_id: UUID grocery_item_id: UUID confidence: Decimal source: str grocery_item_name: Optional[str] = None current_price: Optional[Decimal] = None regular_price: Optional[Decimal] = None is_on_sale: Optional[bool] = None model_config = {"from_attributes": True}