"""SQLAlchemy ORM models (SPEC.md §2). Separate from Pydantic schemas (spec §1).""" from datetime import date, datetime from sqlalchemy import CheckConstraint, ForeignKey from sqlalchemy.orm import Mapped, mapped_column, relationship from database import Base class Food(Base): __tablename__ = "foods" __table_args__ = ( # Meal nutrition is derived from components, never stored (spec §2.1) CheckConstraint("is_meal = 1 OR calories_per_unit IS NOT NULL"), ) id: Mapped[int] = mapped_column(primary_key=True) name: Mapped[str] brand: Mapped[str | None] barcode: Mapped[str | None] = mapped_column(unique=True) source: Mapped[str] # "openfoodfacts" | "manual" | "meal" is_meal: Mapped[bool] = mapped_column(default=False) unit_type: Mapped[str] = mapped_column(default="weight") # "weight" | "count" calories_per_unit: Mapped[float | None] protein_per_unit: Mapped[float | None] carbs_per_unit: Mapped[float | None] fat_per_unit: Mapped[float | None] fiber_per_unit: Mapped[float | None] saturated_fat_per_unit: Mapped[float | None] sugars_per_unit: Mapped[float | None] sodium_per_unit: Mapped[float | None] serving_size_g: Mapped[float | None] serving_name: Mapped[str | None] off_data: Mapped[str | None] # raw OFF JSON blob as TEXT deleted_at: Mapped[datetime | None] created_at: Mapped[datetime] updated_at: Mapped[datetime] components: Mapped[list["MealComponent"]] = relationship( foreign_keys="MealComponent.meal_id", back_populates="meal" ) class MealComponent(Base): __tablename__ = "meal_components" id: Mapped[int] = mapped_column(primary_key=True) meal_id: Mapped[int] = mapped_column(ForeignKey("foods.id")) food_id: Mapped[int] = mapped_column(ForeignKey("foods.id")) quantity: Mapped[float] meal: Mapped[Food] = relationship(foreign_keys=[meal_id], back_populates="components") food: Mapped[Food] = relationship(foreign_keys=[food_id]) class DailyLogEntry(Base): __tablename__ = "daily_log" id: Mapped[int] = mapped_column(primary_key=True) date: Mapped[date] food_id: Mapped[int] = mapped_column(ForeignKey("foods.id")) quantity: Mapped[float] meal_slot: Mapped[str | None] # "breakfast" | "lunch" | "dinner" | "snack" sort_order: Mapped[int] = mapped_column(default=0) created_at: Mapped[datetime] food: Mapped[Food] = relationship() class Target(Base): __tablename__ = "targets" id: Mapped[int] = mapped_column(primary_key=True) start_date: Mapped[date] end_date: Mapped[date | None] # null = currently active calories: Mapped[int] protein_g: Mapped[float | None] carbs_g: Mapped[float | None] fat_g: Mapped[float | None]