Big refactor of gift categories model
This commit is contained in:
@@ -1,19 +1,20 @@
|
||||
from typing import List, Optional, Dict, Any
|
||||
from typing import List, Optional, Dict, Any, Union
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Path
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.dependencies.auth import get_current_active_user, get_current_user
|
||||
from app.crud.gift import gift_item_crud, gift_category_crud, gift_purchase_crud
|
||||
from app.crud.gift import gift_item_crud, gift_category_crud, gift_purchase_crud, event_gift_category_crud
|
||||
from app.crud.guest import guest_crud
|
||||
from app.crud.event import event_crud
|
||||
from app.models.gift import GiftStatus, GiftPriority
|
||||
from app.models.gift import GiftStatus, GiftPriority, EventGiftCategory, GiftItem
|
||||
from app.models.user import User
|
||||
from app.schemas.gifts import (
|
||||
GiftItem, GiftItemCreate, GiftItemUpdate,
|
||||
GiftCategory, GiftCategoryCreate, GiftCategoryUpdate,
|
||||
GiftPurchase, GiftPurchaseCreate, GiftPurchaseUpdate
|
||||
GiftPurchase, GiftPurchaseCreate, GiftPurchaseUpdate,
|
||||
EventGiftCategoryCreate, EventGiftCategoryUpdate, EventGiftCategoryInDB
|
||||
)
|
||||
from app.core.database import get_db
|
||||
|
||||
@@ -26,13 +27,14 @@ def create_gift_category(
|
||||
*,
|
||||
db: Session = Depends(get_db),
|
||||
category_in: GiftCategoryCreate,
|
||||
event_id: UUID,
|
||||
current_user: User = Depends(get_current_active_user)
|
||||
) -> Any:
|
||||
"""
|
||||
Create new gift category.
|
||||
Create new gift category and associate it with an event.
|
||||
"""
|
||||
# Check if user has permission to manage this event
|
||||
event = event_crud.get(db, category_in.event_id)
|
||||
event = event_crud.get(db, event_id)
|
||||
if not event:
|
||||
raise HTTPException(status_code=404, detail="Event not found")
|
||||
|
||||
@@ -41,7 +43,23 @@ def create_gift_category(
|
||||
if event.created_by != current_user.id:
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
return gift_category_crud.create(db, obj_in=category_in)
|
||||
# Create the category
|
||||
category = gift_category_crud.create(db, obj_in=category_in)
|
||||
|
||||
# Create the association between the category and the event
|
||||
association_data = EventGiftCategoryCreate(
|
||||
event_id=event_id,
|
||||
category_id=category.id,
|
||||
display_order=0, # Default display order
|
||||
is_visible=True # Default visibility
|
||||
)
|
||||
event_gift_category_crud.create(db, obj_in=association_data)
|
||||
|
||||
# Set display properties for the response
|
||||
category.display_order = 0
|
||||
category.is_visible = True
|
||||
|
||||
return category
|
||||
|
||||
|
||||
@router.get("/categories/event/{event_id}", response_model=List[GiftCategory])
|
||||
@@ -67,11 +85,34 @@ def read_gift_categories(
|
||||
if not current_user and not event.is_public:
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
# Get categories
|
||||
categories = gift_category_crud.get_multi_by_event(
|
||||
# Get categories for this event using the association table
|
||||
categories = event_gift_category_crud.get_categories_by_event(
|
||||
db, event_id=event_id, skip=skip, limit=limit, include_hidden=include_hidden
|
||||
)
|
||||
|
||||
# Enhance categories with display information from the association
|
||||
for category in categories:
|
||||
# Get the association to access display_order and is_visible
|
||||
association = event_gift_category_crud.get(
|
||||
db, event_id=event_id, category_id=category.id
|
||||
)
|
||||
if association:
|
||||
category.display_order = association.display_order
|
||||
category.is_visible = association.is_visible
|
||||
|
||||
# Calculate statistics for this event
|
||||
total_gifts = 0
|
||||
available_gifts = 0
|
||||
if category.gifts:
|
||||
for gift in category.gifts:
|
||||
if gift.event_id == event_id:
|
||||
total_gifts += 1
|
||||
if gift.status == GiftStatus.AVAILABLE and gift.is_visible:
|
||||
available_gifts += 1
|
||||
|
||||
category.total_gifts = total_gifts
|
||||
category.available_gifts = available_gifts
|
||||
|
||||
# If include_gifts is true, fetch gift items for each category
|
||||
if include_gifts:
|
||||
for category in categories:
|
||||
@@ -89,31 +130,79 @@ def read_gift_category(
|
||||
*,
|
||||
db: Session = Depends(get_db),
|
||||
category_id: UUID = Path(...),
|
||||
event_id: Optional[UUID] = None,
|
||||
include_gifts: bool = False,
|
||||
current_user: Optional[User] = Depends(get_current_user)
|
||||
) -> Any:
|
||||
"""
|
||||
Get gift category by ID.
|
||||
Get gift category by ID. If event_id is provided, includes event-specific display settings.
|
||||
"""
|
||||
category = gift_category_crud.get(db, id=category_id)
|
||||
if not category:
|
||||
raise HTTPException(status_code=404, detail="Gift category not found")
|
||||
|
||||
# Check if event is public or user is authorized
|
||||
event = event_crud.get(db, category.event_id)
|
||||
if not event:
|
||||
raise HTTPException(status_code=404, detail="Event not found")
|
||||
# Initialize event-specific properties
|
||||
category.display_order = None
|
||||
category.is_visible = None
|
||||
category.total_gifts = None
|
||||
category.available_gifts = None
|
||||
|
||||
if not event.is_public and not current_user:
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
# If event_id is provided, get event-specific information
|
||||
if event_id:
|
||||
# Check if event exists and is accessible
|
||||
event = event_crud.get(db, event_id)
|
||||
if not event:
|
||||
raise HTTPException(status_code=404, detail="Event not found")
|
||||
|
||||
# If include_gifts is true, fetch gift items for the category
|
||||
if include_gifts:
|
||||
gifts = gift_item_crud.get_multi_by_event(
|
||||
db, event_id=category.event_id, category_id=category.id,
|
||||
include_hidden=current_user is not None # Only include hidden for logged-in users
|
||||
)
|
||||
# Set the gifts attribute which is initially None in the model
|
||||
# For public access, ensure event is public
|
||||
if not event.is_public and not current_user:
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
# Check if this category is associated with the event
|
||||
association = event_gift_category_crud.get(db, event_id=event_id, category_id=category_id)
|
||||
if not association:
|
||||
raise HTTPException(status_code=404, detail="Category not associated with this event")
|
||||
|
||||
# Set event-specific display properties
|
||||
category.display_order = association.display_order
|
||||
category.is_visible = association.is_visible
|
||||
|
||||
# Calculate statistics for this event
|
||||
total_gifts = 0
|
||||
available_gifts = 0
|
||||
|
||||
# If include_gifts is true, fetch gift items for the category in this event
|
||||
if include_gifts:
|
||||
gifts = gift_item_crud.get_multi_by_event(
|
||||
db, event_id=event_id, category_id=category.id,
|
||||
include_hidden=current_user is not None # Only include hidden for logged-in users
|
||||
)
|
||||
# Set the gifts attribute
|
||||
setattr(category, "gifts", gifts)
|
||||
|
||||
# Calculate statistics
|
||||
for gift in gifts:
|
||||
total_gifts += 1
|
||||
if gift.status == GiftStatus.AVAILABLE and gift.is_visible:
|
||||
available_gifts += 1
|
||||
else:
|
||||
# Calculate statistics without fetching all gifts
|
||||
gifts_query = db.query(GiftItem).filter(
|
||||
GiftItem.event_id == event_id,
|
||||
GiftItem.category_id == category_id
|
||||
)
|
||||
total_gifts = gifts_query.count()
|
||||
available_gifts = gifts_query.filter(
|
||||
GiftItem.status == GiftStatus.AVAILABLE,
|
||||
GiftItem.is_visible == True
|
||||
).count()
|
||||
|
||||
category.total_gifts = total_gifts
|
||||
category.available_gifts = available_gifts
|
||||
elif include_gifts:
|
||||
# If no event_id but include_gifts is true, just get all gifts for this category
|
||||
# This is less useful without event context but included for completeness
|
||||
gifts = db.query(GiftItem).filter(GiftItem.category_id == category_id).all()
|
||||
setattr(category, "gifts", gifts)
|
||||
|
||||
return category
|
||||
@@ -125,25 +214,76 @@ def update_gift_category(
|
||||
db: Session = Depends(get_db),
|
||||
category_id: UUID = Path(...),
|
||||
category_in: GiftCategoryUpdate,
|
||||
event_id: Optional[UUID] = None,
|
||||
current_user: User = Depends(get_current_active_user)
|
||||
) -> Any:
|
||||
"""
|
||||
Update a gift category.
|
||||
Update a gift category. If event_id is provided, also updates the event-specific settings.
|
||||
"""
|
||||
category = gift_category_crud.get(db, id=category_id)
|
||||
if not category:
|
||||
raise HTTPException(status_code=404, detail="Gift category not found")
|
||||
|
||||
# Check if user has permission to manage this event
|
||||
event = event_crud.get(db, category.event_id)
|
||||
if not event:
|
||||
raise HTTPException(status_code=404, detail="Event not found")
|
||||
# Update the category itself
|
||||
updated_category = gift_category_crud.update(db, db_obj=category, obj_in=category_in)
|
||||
|
||||
# Check permissions (basic implementation)
|
||||
if event.created_by != current_user.id:
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
# Initialize event-specific properties for the response
|
||||
updated_category.display_order = None
|
||||
updated_category.is_visible = None
|
||||
updated_category.total_gifts = None
|
||||
updated_category.available_gifts = None
|
||||
|
||||
return gift_category_crud.update(db, db_obj=category, obj_in=category_in)
|
||||
# If event_id is provided, update the event-specific settings
|
||||
if event_id:
|
||||
# Check if event exists
|
||||
event = event_crud.get(db, event_id)
|
||||
if not event:
|
||||
raise HTTPException(status_code=404, detail="Event not found")
|
||||
|
||||
# Check permissions (basic implementation)
|
||||
if event.created_by != current_user.id:
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
# Check if this category is associated with the event
|
||||
association = event_gift_category_crud.get(db, event_id=event_id, category_id=category_id)
|
||||
if not association:
|
||||
# If not associated, create the association
|
||||
association_data = EventGiftCategoryCreate(
|
||||
event_id=event_id,
|
||||
category_id=category_id,
|
||||
display_order=0, # Default display order
|
||||
is_visible=True # Default visibility
|
||||
)
|
||||
association = event_gift_category_crud.create(db, obj_in=association_data)
|
||||
else:
|
||||
# If display_order or is_visible are in the update data, update the association
|
||||
association_update = {}
|
||||
if hasattr(category_in, 'display_order') and category_in.display_order is not None:
|
||||
association_update['display_order'] = category_in.display_order
|
||||
if hasattr(category_in, 'is_visible') and category_in.is_visible is not None:
|
||||
association_update['is_visible'] = category_in.is_visible
|
||||
|
||||
if association_update:
|
||||
association = event_gift_category_crud.update(
|
||||
db, db_obj=association, obj_in=association_update
|
||||
)
|
||||
|
||||
# Set event-specific properties for the response
|
||||
updated_category.display_order = association.display_order
|
||||
updated_category.is_visible = association.is_visible
|
||||
|
||||
# Calculate statistics for this event
|
||||
gifts_query = db.query(GiftItem).filter(
|
||||
GiftItem.event_id == event_id,
|
||||
GiftItem.category_id == category_id
|
||||
)
|
||||
updated_category.total_gifts = gifts_query.count()
|
||||
updated_category.available_gifts = gifts_query.filter(
|
||||
GiftItem.status == GiftStatus.AVAILABLE,
|
||||
GiftItem.is_visible == True
|
||||
).count()
|
||||
|
||||
return updated_category
|
||||
|
||||
|
||||
@router.delete("/categories/{category_id}", response_model=GiftCategory)
|
||||
@@ -151,25 +291,242 @@ def delete_gift_category(
|
||||
*,
|
||||
db: Session = Depends(get_db),
|
||||
category_id: UUID = Path(...),
|
||||
event_id: Optional[UUID] = None,
|
||||
force: bool = False,
|
||||
current_user: User = Depends(get_current_active_user)
|
||||
) -> Any:
|
||||
"""
|
||||
Delete a gift category.
|
||||
Delete a gift category. If event_id is provided, only removes the association with that event.
|
||||
If force=True and no event_id is provided, deletes the category completely.
|
||||
"""
|
||||
category = gift_category_crud.get(db, id=category_id)
|
||||
if not category:
|
||||
raise HTTPException(status_code=404, detail="Gift category not found")
|
||||
|
||||
# Check if user has permission to manage this event
|
||||
event = event_crud.get(db, category.event_id)
|
||||
# Make a copy of the category for the response
|
||||
category_copy = GiftCategory.model_validate(category)
|
||||
|
||||
if event_id:
|
||||
# Check if event exists
|
||||
event = event_crud.get(db, event_id)
|
||||
if not event:
|
||||
raise HTTPException(status_code=404, detail="Event not found")
|
||||
|
||||
# Check permissions (basic implementation)
|
||||
if event.created_by != current_user.id:
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
# Check if this category is associated with the event
|
||||
association = event_gift_category_crud.get(db, event_id=event_id, category_id=category_id)
|
||||
if not association:
|
||||
raise HTTPException(status_code=404, detail="Category not associated with this event")
|
||||
|
||||
# Remove the association
|
||||
event_gift_category_crud.remove(db, event_id=event_id, category_id=category_id)
|
||||
|
||||
# Return the category with event-specific properties set to None
|
||||
category_copy.display_order = None
|
||||
category_copy.is_visible = None
|
||||
category_copy.total_gifts = None
|
||||
category_copy.available_gifts = None
|
||||
|
||||
return category_copy
|
||||
elif force:
|
||||
# Check if the user has permission to delete the category
|
||||
# This is a more restrictive operation, so we might want to add additional checks
|
||||
|
||||
# Check if the category is used by any events
|
||||
# Get all associations for this category
|
||||
associations = db.query(EventGiftCategory).filter(
|
||||
EventGiftCategory.category_id == category_id
|
||||
).all()
|
||||
|
||||
if associations and len(associations) > 0:
|
||||
# If there are associations, we need to check if the user has permission to manage all events
|
||||
for assoc in associations:
|
||||
event = event_crud.get(db, assoc.event_id)
|
||||
if not event:
|
||||
continue
|
||||
|
||||
# If the user doesn't have permission for any of the events, deny the operation
|
||||
if event.created_by != current_user.id:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="Not enough permissions. Category is used by events you don't manage."
|
||||
)
|
||||
|
||||
# Remove all associations
|
||||
for assoc in associations:
|
||||
event_gift_category_crud.remove(db, event_id=assoc.event_id, category_id=category_id)
|
||||
|
||||
# Now delete the category itself
|
||||
return gift_category_crud.remove(db, id=category_id)
|
||||
else:
|
||||
# If no event_id and not force, raise an error
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Must provide event_id to remove association or set force=True to delete category completely"
|
||||
)
|
||||
|
||||
|
||||
# ===== EVENT-CATEGORY ASSOCIATIONS ===== #
|
||||
|
||||
@router.post("/events/{event_id}/categories/{category_id}", response_model=GiftCategory)
|
||||
def associate_category_with_event(
|
||||
*,
|
||||
db: Session = Depends(get_db),
|
||||
event_id: UUID = Path(...),
|
||||
category_id: UUID = Path(...),
|
||||
display_order: int = 0,
|
||||
is_visible: bool = True,
|
||||
current_user: User = Depends(get_current_active_user)
|
||||
) -> Any:
|
||||
"""
|
||||
Associate an existing category with an event.
|
||||
"""
|
||||
# Check if event exists
|
||||
event = event_crud.get(db, event_id)
|
||||
if not event:
|
||||
raise HTTPException(status_code=404, detail="Event not found")
|
||||
|
||||
# Check permissions (basic implementation)
|
||||
# Check permissions
|
||||
if event.created_by != current_user.id:
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
return gift_category_crud.remove(db, id=category_id)
|
||||
# Check if category exists
|
||||
category = gift_category_crud.get(db, id=category_id)
|
||||
if not category:
|
||||
raise HTTPException(status_code=404, detail="Gift category not found")
|
||||
|
||||
# Check if association already exists
|
||||
existing_association = event_gift_category_crud.get(db, event_id=event_id, category_id=category_id)
|
||||
if existing_association:
|
||||
raise HTTPException(status_code=400, detail="Category already associated with this event")
|
||||
|
||||
# Create the association
|
||||
association_data = EventGiftCategoryCreate(
|
||||
event_id=event_id,
|
||||
category_id=category_id,
|
||||
display_order=display_order,
|
||||
is_visible=is_visible
|
||||
)
|
||||
event_gift_category_crud.create(db, obj_in=association_data)
|
||||
|
||||
# Set display properties for the response
|
||||
category.display_order = display_order
|
||||
category.is_visible = is_visible
|
||||
|
||||
# Calculate statistics for this event
|
||||
gifts_query = db.query(GiftItem).filter(
|
||||
GiftItem.event_id == event_id,
|
||||
GiftItem.category_id == category_id
|
||||
)
|
||||
category.total_gifts = gifts_query.count()
|
||||
category.available_gifts = gifts_query.filter(
|
||||
GiftItem.status == GiftStatus.AVAILABLE,
|
||||
GiftItem.is_visible == True
|
||||
).count()
|
||||
|
||||
return category
|
||||
|
||||
|
||||
@router.put("/events/{event_id}/categories/{category_id}", response_model=GiftCategory)
|
||||
def update_category_event_settings(
|
||||
*,
|
||||
db: Session = Depends(get_db),
|
||||
event_id: UUID = Path(...),
|
||||
category_id: UUID = Path(...),
|
||||
display_order: Optional[int] = None,
|
||||
is_visible: Optional[bool] = None,
|
||||
current_user: User = Depends(get_current_active_user)
|
||||
) -> Any:
|
||||
"""
|
||||
Update the display settings for a category in an event.
|
||||
"""
|
||||
# Check if event exists
|
||||
event = event_crud.get(db, event_id)
|
||||
if not event:
|
||||
raise HTTPException(status_code=404, detail="Event not found")
|
||||
|
||||
# Check permissions
|
||||
if event.created_by != current_user.id:
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
# Check if category exists
|
||||
category = gift_category_crud.get(db, id=category_id)
|
||||
if not category:
|
||||
raise HTTPException(status_code=404, detail="Gift category not found")
|
||||
|
||||
# Check if association exists
|
||||
association = event_gift_category_crud.get(db, event_id=event_id, category_id=category_id)
|
||||
if not association:
|
||||
raise HTTPException(status_code=404, detail="Category not associated with this event")
|
||||
|
||||
# Update the association
|
||||
update_data = {}
|
||||
if display_order is not None:
|
||||
update_data['display_order'] = display_order
|
||||
if is_visible is not None:
|
||||
update_data['is_visible'] = is_visible
|
||||
|
||||
if update_data:
|
||||
association = event_gift_category_crud.update(db, db_obj=association, obj_in=update_data)
|
||||
|
||||
# Set display properties for the response
|
||||
category.display_order = association.display_order
|
||||
category.is_visible = association.is_visible
|
||||
|
||||
# Calculate statistics for this event
|
||||
gifts_query = db.query(GiftItem).filter(
|
||||
GiftItem.event_id == event_id,
|
||||
GiftItem.category_id == category_id
|
||||
)
|
||||
category.total_gifts = gifts_query.count()
|
||||
category.available_gifts = gifts_query.filter(
|
||||
GiftItem.status == GiftStatus.AVAILABLE,
|
||||
GiftItem.is_visible == True
|
||||
).count()
|
||||
|
||||
return category
|
||||
|
||||
|
||||
@router.get("/categories/{category_id}/events", response_model=List[Dict[str, Any]])
|
||||
def get_events_for_category(
|
||||
*,
|
||||
db: Session = Depends(get_db),
|
||||
category_id: UUID = Path(...),
|
||||
current_user: User = Depends(get_current_active_user)
|
||||
) -> Any:
|
||||
"""
|
||||
Get all events that use a specific category.
|
||||
"""
|
||||
# Check if category exists
|
||||
category = gift_category_crud.get(db, id=category_id)
|
||||
if not category:
|
||||
raise HTTPException(status_code=404, detail="Gift category not found")
|
||||
|
||||
# Get all events for this category
|
||||
events = event_gift_category_crud.get_events_by_category(db, category_id=category_id)
|
||||
|
||||
# Filter events that the user has permission to see
|
||||
result = []
|
||||
for event in events:
|
||||
# Check if user has permission to see this event
|
||||
if event.created_by == current_user.id or event.is_public:
|
||||
# Get the association to access display_order and is_visible
|
||||
association = event_gift_category_crud.get(db, event_id=event.id, category_id=category_id)
|
||||
if association:
|
||||
# Create a response with event details and association settings
|
||||
event_data = {
|
||||
"event_id": event.id,
|
||||
"event_title": event.title,
|
||||
"event_date": event.event_date,
|
||||
"display_order": association.display_order,
|
||||
"is_visible": association.is_visible
|
||||
}
|
||||
result.append(event_data)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@router.put("/categories/{category_id}/reorder", response_model=GiftCategory)
|
||||
@@ -404,7 +761,6 @@ def reserve_gift_item(
|
||||
Reserve a gift item for a guest.
|
||||
"""
|
||||
gift = gift_item_crud.get(db, id=item_id)
|
||||
print(f"Gift {gift}")
|
||||
if not gift:
|
||||
raise HTTPException(status_code=404, detail="Gift item not found")
|
||||
|
||||
@@ -581,12 +937,3 @@ def read_gift_purchases_by_guest(
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
return gift_purchase_crud.get_by_guest(db, guest_id=guest_id)
|
||||
|
||||
|
||||
# For public users, additional validation
|
||||
# Check if event is public
|
||||
event = event_crud.get(db, gift.event_id)
|
||||
if not event or not event.is_public:
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
return gift_item_crud.cancel_reservation(db, gift_id=item_id, guest_id=guest_id)
|
||||
@@ -23,11 +23,13 @@ from app.schemas.events import (
|
||||
|
||||
from app.api.routes.events import guests
|
||||
from app.api.routes.events import rsvps
|
||||
from app.api.routes.events import gifts
|
||||
logger = logging.getLogger(__name__)
|
||||
events_router = APIRouter()
|
||||
|
||||
events_router.include_router(guests.router, prefix="/guests", tags=["guests"])
|
||||
events_router.include_router(rsvps.router, prefix="/rsvps", tags=["rsvps"])
|
||||
events_router.include_router(gifts.router, prefix="/gifts", tags=["gifts"])
|
||||
|
||||
def validate_event_access(
|
||||
*,
|
||||
|
||||
Reference in New Issue
Block a user