Add missing endpoints, fix batch updates, and implement RSVP status #21

Merged
andika merged 1 commits from dev into loaf-prod 2026-01-05 18:08:21 +00:00

121
server.py
View File

@@ -364,6 +364,9 @@ class AttendanceUpdate(BaseModel):
user_id: str user_id: str
attended: bool attended: bool
class BatchAttendanceUpdate(BaseModel):
updates: list[AttendanceUpdate]
class UpdateUserStatusRequest(BaseModel): class UpdateUserStatusRequest(BaseModel):
status: str status: str
@@ -1499,7 +1502,14 @@ async def get_events(
EventRSVP.rsvp_status == RSVPStatus.yes EventRSVP.rsvp_status == RSVPStatus.yes
).count() ).count()
# No user_rsvp_status in public endpoint # Get current user's RSVP status for this event
user_rsvp = db.query(EventRSVP).filter(
EventRSVP.event_id == event.id,
EventRSVP.user_id == current_user.id
).first()
user_rsvp_status = user_rsvp.rsvp_status.value if user_rsvp else None
result.append(EventResponse( result.append(EventResponse(
id=str(event.id), id=str(event.id),
title=event.title, title=event.title,
@@ -1512,7 +1522,7 @@ async def get_events(
created_by=str(event.created_by), created_by=str(event.created_by),
created_at=event.created_at, created_at=event.created_at,
rsvp_count=rsvp_count, rsvp_count=rsvp_count,
user_rsvp_status=None user_rsvp_status=user_rsvp_status
)) ))
return result return result
@@ -1532,8 +1542,13 @@ async def get_event(
EventRSVP.rsvp_status == RSVPStatus.yes EventRSVP.rsvp_status == RSVPStatus.yes
).count() ).count()
# No user_rsvp_status in public endpoint # Get current user's RSVP status for this event
user_rsvp = None user_rsvp = db.query(EventRSVP).filter(
EventRSVP.event_id == event_id,
EventRSVP.user_id == current_user.id
).first()
user_rsvp_status = user_rsvp.rsvp_status.value if user_rsvp else None
return EventResponse( return EventResponse(
id=str(event.id), id=str(event.id),
@@ -1547,7 +1562,7 @@ async def get_event(
created_by=str(event.created_by), created_by=str(event.created_by),
created_at=event.created_at, created_at=event.created_at,
rsvp_count=rsvp_count, rsvp_count=rsvp_count,
user_rsvp_status=user_rsvp user_rsvp_status=user_rsvp_status
) )
@api_router.post("/events/{event_id}/rsvp") @api_router.post("/events/{event_id}/rsvp")
@@ -1618,7 +1633,9 @@ async def get_my_event_activity(
} }
# Separate upcoming vs past events # Separate upcoming vs past events
if event.end_at > now: # Ensure timezone-aware comparison
event_end_at = event.end_at.replace(tzinfo=timezone.utc) if event.end_at.tzinfo is None else event.end_at
if event_end_at > now:
upcoming_events.append(event_data) upcoming_events.append(event_data)
else: else:
past_events.append(event_data) past_events.append(event_data)
@@ -3796,6 +3813,37 @@ async def update_event(
return {"message": "Event updated successfully"} return {"message": "Event updated successfully"}
@api_router.get("/admin/events/{event_id}", response_model=EventResponse)
async def get_admin_event(
event_id: str,
current_user: User = Depends(require_permission("events.view")),
db: Session = Depends(get_db)
):
"""Get single event details (admin) - allows viewing unpublished events"""
event = db.query(Event).filter(Event.id == event_id).first()
if not event:
raise HTTPException(status_code=404, detail="Event not found")
rsvp_count = db.query(EventRSVP).filter(
EventRSVP.event_id == event.id,
EventRSVP.rsvp_status == RSVPStatus.yes
).count()
return EventResponse(
id=str(event.id),
title=event.title,
description=event.description,
start_at=event.start_at,
end_at=event.end_at,
location=event.location,
capacity=event.capacity,
published=event.published,
created_by=str(event.created_by),
created_at=event.created_at,
rsvp_count=rsvp_count,
user_rsvp_status=None
)
@api_router.get("/admin/events/{event_id}/rsvps") @api_router.get("/admin/events/{event_id}/rsvps")
async def get_event_rsvps( async def get_event_rsvps(
event_id: str, event_id: str,
@@ -3826,46 +3874,53 @@ async def get_event_rsvps(
@api_router.put("/admin/events/{event_id}/attendance") @api_router.put("/admin/events/{event_id}/attendance")
async def mark_attendance( async def mark_attendance(
event_id: str, event_id: str,
request: AttendanceUpdate, request: BatchAttendanceUpdate,
current_user: User = Depends(require_permission("events.attendance")), current_user: User = Depends(require_permission("events.attendance")),
db: Session = Depends(get_db) db: Session = Depends(get_db)
): ):
"""Mark attendance for one or more users (supports batch updates)"""
event = db.query(Event).filter(Event.id == event_id).first() event = db.query(Event).filter(Event.id == event_id).first()
if not event: if not event:
raise HTTPException(status_code=404, detail="Event not found") raise HTTPException(status_code=404, detail="Event not found")
rsvp = db.query(EventRSVP).filter( updated_count = 0
EventRSVP.event_id == event_id,
EventRSVP.user_id == request.user_id
).first()
# Auto-create RSVP if it doesn't exist (for retroactive attendance marking) # Process each update in the batch
if not rsvp: for update in request.updates:
rsvp = EventRSVP( rsvp = db.query(EventRSVP).filter(
event_id=event_id, EventRSVP.event_id == event_id,
user_id=request.user_id, EventRSVP.user_id == update.user_id
rsvp_status=RSVPStatus.yes, # Default to 'yes' for attended events ).first()
attended=False,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
db.add(rsvp)
db.flush() # Get the ID without committing
rsvp.attended = request.attended # Auto-create RSVP if it doesn't exist (for retroactive attendance marking)
rsvp.attended_at = datetime.now(timezone.utc) if request.attended else None if not rsvp:
rsvp.updated_at = datetime.now(timezone.utc) rsvp = EventRSVP(
event_id=event_id,
user_id=update.user_id,
rsvp_status=RSVPStatus.yes, # Default to 'yes' for attended events
attended=False,
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc)
)
db.add(rsvp)
db.flush() # Get the ID without committing
# If user attended and they were pending validation, update their status rsvp.attended = update.attended
if request.attended: rsvp.attended_at = datetime.now(timezone.utc) if update.attended else None
user = db.query(User).filter(User.id == request.user_id).first() rsvp.updated_at = datetime.now(timezone.utc)
if user and user.status == UserStatus.pending_validation:
user.status = UserStatus.pre_validated # If user attended and they were pending validation, update their status
user.updated_at = datetime.now(timezone.utc) if update.attended:
user = db.query(User).filter(User.id == update.user_id).first()
if user and user.status == UserStatus.pending_validation:
user.status = UserStatus.pre_validated
user.updated_at = datetime.now(timezone.utc)
updated_count += 1
db.commit() db.commit()
return {"message": "Attendance marked successfully"} return {"message": f"Attendance marked successfully for {updated_count} {'person' if updated_count == 1 else 'people'}"}
@api_router.get("/admin/events") @api_router.get("/admin/events")
async def get_admin_events( async def get_admin_events(