from fastapi import FastAPI, Depends, HTTPException, status from fastapi.middleware.cors import CORSMiddleware from sqlalchemy.orm import Session from typing import List import models, schemas from database import engine, get_db # Create database tables models.Base.metadata.create_all(bind=engine) app = FastAPI( title="Grocery Tracker API", description="API for tracking grocery prices and shopping events", version="1.0.0" ) # CORS middleware for React frontend app.add_middleware( CORSMiddleware, allow_origins=["http://localhost:3000", "http://localhost:5173"], # React dev servers allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Root endpoint @app.get("/") def read_root(): return {"message": "Grocery Tracker API", "version": "1.0.0"} # Grocery endpoints @app.post("/groceries/", response_model=schemas.Grocery) def create_grocery(grocery: schemas.GroceryCreate, db: Session = Depends(get_db)): db_grocery = models.Grocery(**grocery.dict()) db.add(db_grocery) db.commit() db.refresh(db_grocery) return db_grocery @app.get("/groceries/", response_model=List[schemas.Grocery]) def read_groceries(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): groceries = db.query(models.Grocery).offset(skip).limit(limit).all() return groceries @app.get("/groceries/{grocery_id}", response_model=schemas.Grocery) def read_grocery(grocery_id: int, db: Session = Depends(get_db)): grocery = db.query(models.Grocery).filter(models.Grocery.id == grocery_id).first() if grocery is None: raise HTTPException(status_code=404, detail="Grocery not found") return grocery @app.put("/groceries/{grocery_id}", response_model=schemas.Grocery) def update_grocery(grocery_id: int, grocery_update: schemas.GroceryUpdate, db: Session = Depends(get_db)): grocery = db.query(models.Grocery).filter(models.Grocery.id == grocery_id).first() if grocery is None: raise HTTPException(status_code=404, detail="Grocery not found") update_data = grocery_update.dict(exclude_unset=True) for field, value in update_data.items(): setattr(grocery, field, value) db.commit() db.refresh(grocery) return grocery @app.delete("/groceries/{grocery_id}") def delete_grocery(grocery_id: int, db: Session = Depends(get_db)): grocery = db.query(models.Grocery).filter(models.Grocery.id == grocery_id).first() if grocery is None: raise HTTPException(status_code=404, detail="Grocery not found") db.delete(grocery) db.commit() return {"message": "Grocery deleted successfully"} # Shop endpoints @app.post("/shops/", response_model=schemas.Shop) def create_shop(shop: schemas.ShopCreate, db: Session = Depends(get_db)): db_shop = models.Shop(**shop.dict()) db.add(db_shop) db.commit() db.refresh(db_shop) return db_shop @app.get("/shops/", response_model=List[schemas.Shop]) def read_shops(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): shops = db.query(models.Shop).offset(skip).limit(limit).all() return shops @app.get("/shops/{shop_id}", response_model=schemas.Shop) def read_shop(shop_id: int, db: Session = Depends(get_db)): shop = db.query(models.Shop).filter(models.Shop.id == shop_id).first() if shop is None: raise HTTPException(status_code=404, detail="Shop not found") return shop @app.put("/shops/{shop_id}", response_model=schemas.Shop) def update_shop(shop_id: int, shop_update: schemas.ShopUpdate, db: Session = Depends(get_db)): shop = db.query(models.Shop).filter(models.Shop.id == shop_id).first() if shop is None: raise HTTPException(status_code=404, detail="Shop not found") update_data = shop_update.dict() for field, value in update_data.items(): setattr(shop, field, value) db.commit() db.refresh(shop) return shop @app.delete("/shops/{shop_id}") def delete_shop(shop_id: int, db: Session = Depends(get_db)): shop = db.query(models.Shop).filter(models.Shop.id == shop_id).first() if shop is None: raise HTTPException(status_code=404, detail="Shop not found") db.delete(shop) db.commit() return {"message": "Shop deleted successfully"} # Shopping Event endpoints @app.post("/shopping-events/", response_model=schemas.ShoppingEventResponse) def create_shopping_event(event: schemas.ShoppingEventCreate, db: Session = Depends(get_db)): # Verify shop exists shop = db.query(models.Shop).filter(models.Shop.id == event.shop_id).first() if shop is None: raise HTTPException(status_code=404, detail="Shop not found") # Create shopping event db_event = models.ShoppingEvent( shop_id=event.shop_id, date=event.date, total_amount=event.total_amount, notes=event.notes ) db.add(db_event) db.commit() db.refresh(db_event) # Add groceries to the event for grocery_item in event.groceries: grocery = db.query(models.Grocery).filter(models.Grocery.id == grocery_item.grocery_id).first() if grocery is None: raise HTTPException(status_code=404, detail=f"Grocery with id {grocery_item.grocery_id} not found") # Insert into association table db.execute( models.shopping_event_groceries.insert().values( shopping_event_id=db_event.id, grocery_id=grocery_item.grocery_id, amount=grocery_item.amount ) ) db.commit() db.refresh(db_event) return db_event @app.get("/shopping-events/", response_model=List[schemas.ShoppingEventResponse]) def read_shopping_events(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): events = db.query(models.ShoppingEvent).offset(skip).limit(limit).all() return events @app.get("/shopping-events/{event_id}", response_model=schemas.ShoppingEventResponse) def read_shopping_event(event_id: int, db: Session = Depends(get_db)): event = db.query(models.ShoppingEvent).filter(models.ShoppingEvent.id == event_id).first() if event is None: raise HTTPException(status_code=404, detail="Shopping event not found") return event # Statistics endpoints @app.get("/stats/categories", response_model=List[schemas.CategoryStats]) def get_category_stats(db: Session = Depends(get_db)): # This would need more complex SQL query - placeholder for now return [] @app.get("/stats/shops", response_model=List[schemas.ShopStats]) def get_shop_stats(db: Session = Depends(get_db)): # This would need more complex SQL query - placeholder for now return [] if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)