added bound checking to van invintory

This commit is contained in:
2026-03-09 07:20:26 -04:00
parent 58b25f2e89
commit 75a67294ea
7 changed files with 49 additions and 24 deletions

View File

@@ -5,7 +5,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from pydantic import BaseModel, Field
from sqlalchemy import select
from sqlalchemy.orm import selectinload
@@ -22,7 +22,7 @@ class ItemCreate(BaseModel):
"""Schema for creating an item."""
name: str
quantity: float = 0
quantity: float = Field(default=0, ge=0)
unit: str
category: str | None = None
@@ -31,7 +31,7 @@ class ItemUpdate(BaseModel):
"""Schema for updating an item."""
name: str | None = None
quantity: float | None = None
quantity: float | None = Field(default=None, ge=0)
unit: str | None = None
category: str | None = None
@@ -52,7 +52,7 @@ class IngredientCreate(BaseModel):
"""Schema for adding an ingredient to a meal."""
item_id: int
quantity_needed: float
quantity_needed: float = Field(gt=0)
class MealCreate(BaseModel):
@@ -192,6 +192,9 @@ def delete_item(item_id: int, db: DbSession) -> dict[str, bool]:
@router.post("/meals", response_model=MealResponse)
def create_meal(meal: MealCreate, db: DbSession) -> MealResponse:
"""Create a new meal with optional ingredients."""
for ing in meal.ingredients:
if not db.get(Item, ing.item_id):
raise HTTPException(status_code=422, detail=f"Item {ing.item_id} not found")
db_meal = Meal(name=meal.name, instructions=meal.instructions)
db.add(db_meal)
db.flush()
@@ -217,6 +220,15 @@ def list_meals(db: DbSession) -> list[MealResponse]:
return [MealResponse.from_meal(m) for m in meals]
@router.get("/meals/availability", response_model=list[MealAvailability])
def check_all_meals(db: DbSession) -> list[MealAvailability]:
"""Check which meals can be made with current inventory."""
meals = list(
db.scalars(select(Meal).options(selectinload(Meal.ingredients).selectinload(MealIngredient.item))).all()
)
return [_check_meal(m) for m in meals]
@router.get("/meals/{meal_id}", response_model=MealResponse)
def get_meal(meal_id: int, db: DbSession) -> MealResponse:
"""Get a meal by ID with ingredients."""
@@ -245,6 +257,13 @@ def add_ingredient(meal_id: int, ingredient: IngredientCreate, db: DbSession) ->
meal = db.get(Meal, meal_id)
if not meal:
raise HTTPException(status_code=404, detail="Meal not found")
if not db.get(Item, ingredient.item_id):
raise HTTPException(status_code=422, detail="Item not found")
existing = db.scalar(
select(MealIngredient).where(MealIngredient.meal_id == meal_id, MealIngredient.item_id == ingredient.item_id)
)
if existing:
raise HTTPException(status_code=409, detail="Ingredient already exists for this meal")
db.add(MealIngredient(meal_id=meal_id, item_id=ingredient.item_id, quantity_needed=ingredient.quantity_needed))
db.commit()
meal = db.scalar(
@@ -264,18 +283,6 @@ def remove_ingredient(meal_id: int, item_id: int, db: DbSession) -> dict[str, bo
return {"deleted": True}
# What can I make / what do I need
@router.get("/meals/availability", response_model=list[MealAvailability])
def check_all_meals(db: DbSession) -> list[MealAvailability]:
"""Check which meals can be made with current inventory."""
meals = list(
db.scalars(select(Meal).options(selectinload(Meal.ingredients).selectinload(MealIngredient.item))).all()
)
return [_check_meal(m) for m in meals]
@router.get("/meals/{meal_id}/availability", response_model=MealAvailability)
def check_meal(meal_id: int, db: DbSession) -> MealAvailability:
"""Check if a specific meal can be made and what's missing."""