Merge pull request 'Add missing endpoints, fix batch updates, and implement RSVP status' (#21) from dev into loaf-prod
Reviewed-on: #21
This commit was merged in pull request #21.
This commit is contained in:
131
server.py
131
server.py
@@ -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,9 +1542,14 @@ 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),
|
||||||
title=event.title,
|
title=event.title,
|
||||||
@@ -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)
|
||||||
@@ -3793,9 +3810,40 @@ async def update_event(
|
|||||||
|
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(event)
|
db.refresh(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(
|
|
||||||
EventRSVP.event_id == event_id,
|
|
||||||
EventRSVP.user_id == request.user_id
|
|
||||||
).first()
|
|
||||||
|
|
||||||
# Auto-create RSVP if it doesn't exist (for retroactive attendance marking)
|
updated_count = 0
|
||||||
if not rsvp:
|
|
||||||
rsvp = EventRSVP(
|
# Process each update in the batch
|
||||||
event_id=event_id,
|
for update in request.updates:
|
||||||
user_id=request.user_id,
|
rsvp = db.query(EventRSVP).filter(
|
||||||
rsvp_status=RSVPStatus.yes, # Default to 'yes' for attended events
|
EventRSVP.event_id == event_id,
|
||||||
attended=False,
|
EventRSVP.user_id == update.user_id
|
||||||
created_at=datetime.now(timezone.utc),
|
).first()
|
||||||
updated_at=datetime.now(timezone.utc)
|
|
||||||
)
|
# Auto-create RSVP if it doesn't exist (for retroactive attendance marking)
|
||||||
db.add(rsvp)
|
if not rsvp:
|
||||||
db.flush() # Get the ID without committing
|
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
|
||||||
|
|
||||||
|
rsvp.attended = update.attended
|
||||||
|
rsvp.attended_at = datetime.now(timezone.utc) if update.attended else None
|
||||||
|
rsvp.updated_at = datetime.now(timezone.utc)
|
||||||
|
|
||||||
|
# If user attended and they were pending validation, update their status
|
||||||
|
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
|
||||||
|
|
||||||
rsvp.attended = request.attended
|
|
||||||
rsvp.attended_at = datetime.now(timezone.utc) if request.attended else None
|
|
||||||
rsvp.updated_at = datetime.now(timezone.utc)
|
|
||||||
|
|
||||||
# If user attended and they were pending validation, update their status
|
|
||||||
if request.attended:
|
|
||||||
user = db.query(User).filter(User.id == request.user_id).first()
|
|
||||||
if user and user.status == UserStatus.pending_validation:
|
|
||||||
user.status = UserStatus.pre_validated
|
|
||||||
user.updated_at = datetime.now(timezone.utc)
|
|
||||||
|
|
||||||
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(
|
||||||
|
|||||||
Reference in New Issue
Block a user