- add grocery category

- add Dockerfile
This commit is contained in:
2025-05-26 21:55:49 +02:00
parent 6118415f05
commit f88a931008
23 changed files with 1304 additions and 937 deletions

28
backend/Dockerfile Normal file
View File

@@ -0,0 +1,28 @@
FROM python:3.11-slim
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y \
gcc \
curl \
&& rm -rf /var/lib/apt/lists/*
# Copy requirements first for better caching
COPY requirements.txt .
# Install Python dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Create a non-root user
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
USER appuser
# Expose port
EXPOSE 8000
# Command to run the application
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

View File

@@ -31,13 +31,16 @@ def build_shopping_event_response(event: models.ShoppingEvent, db: Session) -> s
text("""
SELECT p.id, p.name, p.organic, p.weight, p.weight_unit,
sep.amount, sep.price,
g.id as grocery_id, g.name as grocery_name, g.category as grocery_category,
g.id as grocery_id, g.name as grocery_name,
g.created_at as grocery_created_at, g.updated_at as grocery_updated_at,
gc.id as category_id, gc.name as category_name,
gc.created_at as category_created_at, gc.updated_at as category_updated_at,
b.id as brand_id, b.name as brand_name,
b.created_at as brand_created_at, b.updated_at as brand_updated_at
FROM products p
JOIN shopping_event_products sep ON p.id = sep.product_id
JOIN groceries g ON p.grocery_id = g.id
JOIN grocery_categories gc ON g.category_id = gc.id
LEFT JOIN brands b ON p.brand_id = b.id
WHERE sep.shopping_event_id = :event_id
"""),
@@ -47,12 +50,20 @@ def build_shopping_event_response(event: models.ShoppingEvent, db: Session) -> s
# Convert to ProductWithEventData objects
products_with_data = []
for row in product_data:
category = schemas.GroceryCategory(
id=row.category_id,
name=row.category_name,
created_at=row.category_created_at,
updated_at=row.category_updated_at
)
grocery = schemas.Grocery(
id=row.grocery_id,
name=row.grocery_name,
category=row.grocery_category,
category_id=row.category_id,
created_at=row.grocery_created_at,
updated_at=row.grocery_updated_at
updated_at=row.grocery_updated_at,
category=category
)
brand = None
@@ -261,9 +272,67 @@ def delete_brand(brand_id: int, db: Session = Depends(get_db)):
db.commit()
return {"message": "Brand deleted successfully"}
# Grocery Category endpoints
@app.post("/grocery-categories/", response_model=schemas.GroceryCategory)
def create_grocery_category(category: schemas.GroceryCategoryCreate, db: Session = Depends(get_db)):
db_category = models.GroceryCategory(**category.dict())
db.add(db_category)
db.commit()
db.refresh(db_category)
return db_category
@app.get("/grocery-categories/", response_model=List[schemas.GroceryCategory])
def read_grocery_categories(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
categories = db.query(models.GroceryCategory).offset(skip).limit(limit).all()
return categories
@app.get("/grocery-categories/{category_id}", response_model=schemas.GroceryCategory)
def read_grocery_category(category_id: int, db: Session = Depends(get_db)):
category = db.query(models.GroceryCategory).filter(models.GroceryCategory.id == category_id).first()
if category is None:
raise HTTPException(status_code=404, detail="Grocery category not found")
return category
@app.put("/grocery-categories/{category_id}", response_model=schemas.GroceryCategory)
def update_grocery_category(category_id: int, category_update: schemas.GroceryCategoryUpdate, db: Session = Depends(get_db)):
category = db.query(models.GroceryCategory).filter(models.GroceryCategory.id == category_id).first()
if category is None:
raise HTTPException(status_code=404, detail="Grocery category not found")
update_data = category_update.dict(exclude_unset=True)
for field, value in update_data.items():
setattr(category, field, value)
db.commit()
db.refresh(category)
return category
@app.delete("/grocery-categories/{category_id}")
def delete_grocery_category(category_id: int, db: Session = Depends(get_db)):
category = db.query(models.GroceryCategory).filter(models.GroceryCategory.id == category_id).first()
if category is None:
raise HTTPException(status_code=404, detail="Grocery category not found")
# Check if any groceries reference this category
groceries_with_category = db.query(models.Grocery).filter(models.Grocery.category_id == category_id).first()
if groceries_with_category:
raise HTTPException(
status_code=400,
detail="Cannot delete category: groceries are still associated with this category"
)
db.delete(category)
db.commit()
return {"message": "Grocery category deleted successfully"}
# Grocery endpoints
@app.post("/groceries/", response_model=schemas.Grocery)
def create_grocery(grocery: schemas.GroceryCreate, db: Session = Depends(get_db)):
# Validate category exists
category = db.query(models.GroceryCategory).filter(models.GroceryCategory.id == grocery.category_id).first()
if category is None:
raise HTTPException(status_code=404, detail="Grocery category not found")
db_grocery = models.Grocery(**grocery.dict())
db.add(db_grocery)
db.commit()
@@ -289,6 +358,13 @@ def update_grocery(grocery_id: int, grocery_update: schemas.GroceryUpdate, db: S
raise HTTPException(status_code=404, detail="Grocery not found")
update_data = grocery_update.dict(exclude_unset=True)
# Validate category exists if category_id is being updated
if 'category_id' in update_data:
category = db.query(models.GroceryCategory).filter(models.GroceryCategory.id == update_data['category_id']).first()
if category is None:
raise HTTPException(status_code=404, detail="Grocery category not found")
for field, value in update_data.items():
setattr(grocery, field, value)

View File

@@ -28,16 +28,28 @@ class Brand(Base):
# Relationships
products = relationship("Product", back_populates="brand")
class GroceryCategory(Base):
__tablename__ = "grocery_categories"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, nullable=False, index=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Relationships
groceries = relationship("Grocery", back_populates="category")
class Grocery(Base):
__tablename__ = "groceries"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, nullable=False, index=True)
category = Column(String, nullable=False)
category_id = Column(Integer, ForeignKey("grocery_categories.id"), nullable=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Relationships
category = relationship("GroceryCategory", back_populates="groceries")
products = relationship("Product", back_populates="grocery")
class Product(Base):

View File

@@ -20,22 +20,41 @@ class Brand(BrandBase):
class Config:
from_attributes = True
# Grocery Category schemas
class GroceryCategoryBase(BaseModel):
name: str
class GroceryCategoryCreate(GroceryCategoryBase):
pass
class GroceryCategoryUpdate(BaseModel):
name: Optional[str] = None
class GroceryCategory(GroceryCategoryBase):
id: int
created_at: datetime
updated_at: Optional[datetime] = None
class Config:
from_attributes = True
# Grocery schemas
class GroceryBase(BaseModel):
name: str
category: str
category_id: int
class GroceryCreate(GroceryBase):
pass
class GroceryUpdate(BaseModel):
name: Optional[str] = None
category: Optional[str] = None
category_id: Optional[int] = None
class Grocery(GroceryBase):
id: int
created_at: datetime
updated_at: Optional[datetime] = None
category: GroceryCategory
class Config:
from_attributes = True