from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from datetime import datetime, timedelta, date import json from database import get_db from schemas import MenuPlanRead from models import MenuPlan, Recipe from services import pantry_service, ai_service from config import settings router = APIRouter(prefix="/api/menus", tags=["menus"]) @router.get("/current") async def get_current_menu(db: Session = Depends(get_db)): """Get menu plan for current week.""" today = datetime.utcnow().date() monday = today - timedelta(days=today.weekday()) menu = db.query(MenuPlan).filter(MenuPlan.week_start == monday).first() if not menu: raise HTTPException(status_code=404, detail="No menu plan for current week") return MenuPlanRead.from_orm(menu) @router.post("/generate") async def generate_menu(request: dict = None, db: Session = Depends(get_db)): """Generate a weekly menu using AI.""" # Parse request body week_start = None if request and isinstance(request, dict): week_start = request.get("week_start") # Determine week_start date if week_start: try: if isinstance(week_start, str): week_start = datetime.fromisoformat(week_start).date() else: week_start = week_start except Exception: raise HTTPException(status_code=400, detail="Invalid week_start date") else: today = datetime.utcnow().date() week_start = today - timedelta(days=today.weekday()) # Build pantry context and generate menu try: pantry_context = pantry_service.build_pantry_context(db) ai_result = await ai_service.generate_weekly_menu(pantry_context) except ValueError as e: raise HTTPException(status_code=503, detail=str(e)) except (ConnectionError, Exception) as e: raise HTTPException(status_code=503, detail=f"Ollama service error: {str(e)}") # Save recipes and build plan dict week_plan = ai_result.get("week_plan", {}) plan_dict = {} for day, meals in week_plan.items(): plan_dict[day] = {} for meal_type, meal_data in meals.items(): if isinstance(meal_data, dict) and "name" in meal_data: meal_name = meal_data["name"] # Check if recipe exists by name existing_recipe = db.query(Recipe).filter(Recipe.name == meal_name).first() if existing_recipe: recipe_id = existing_recipe.id else: # Create new recipe new_recipe = Recipe( name=meal_name, meal_type=meal_type, ingredients=json.dumps(meal_data.get("ingredients", [])), instructions=meal_data.get("instructions", ""), estimated_time_minutes=meal_data.get("time_minutes", 30), servings=meal_data.get("serves", 2), source="ai", ) db.add(new_recipe) db.flush() recipe_id = new_recipe.id plan_dict[day][meal_type] = recipe_id # Upsert MenuPlan existing_plan = db.query(MenuPlan).filter(MenuPlan.week_start == week_start).first() if existing_plan: existing_plan.plan = json.dumps(plan_dict) existing_plan.generated_at = datetime.utcnow() existing_plan.model_used = settings.model_name menu = existing_plan else: menu = MenuPlan( week_start=week_start, plan=json.dumps(plan_dict), generated_at=datetime.utcnow(), model_used=settings.model_name, ) db.add(menu) db.commit() db.refresh(menu) return { "menu_plan": MenuPlanRead.from_orm(menu), "week_plan": week_plan, "notes": ai_result.get("notes", ""), } @router.get("") async def list_menus(db: Session = Depends(get_db)): """List all menu plans.""" menus = db.query(MenuPlan).order_by(MenuPlan.week_start.desc()).all() return [MenuPlanRead.from_orm(m) for m in menus] @router.get("/{week_start}") async def get_menu(week_start: date, db: Session = Depends(get_db)): """Get menu plan for a specific week.""" menu = db.query(MenuPlan).filter(MenuPlan.week_start == week_start).first() if not menu: raise HTTPException(status_code=404, detail="Menu plan not found") return MenuPlanRead.from_orm(menu) @router.delete("/{id}") async def delete_menu(id: int, db: Session = Depends(get_db)): """Delete a menu plan.""" menu = db.query(MenuPlan).filter(MenuPlan.id == id).first() if not menu: raise HTTPException(status_code=404, detail="Menu plan not found") db.delete(menu) db.commit() return {"status": "deleted"}