from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session, joinedload from typing import List from datetime import datetime from config.database import get_db from models.event import Event, EventParticipation, ParticipationStatus from models.user import User from models.notification import Notification, NotificationType from schemas.event import EventCreate, EventUpdate, EventResponse, ParticipationUpdate from utils.security import get_current_active_user from utils.email import send_event_notification router = APIRouter() @router.post("/", response_model=EventResponse) async def create_event( event_data: EventCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """Create a new event.""" event = Event( **event_data.dict(), creator_id=current_user.id ) db.add(event) db.commit() db.refresh(event) # Create participations for all active users users = db.query(User).filter(User.is_active == True).all() for user in users: participation = EventParticipation( event_id=event.id, user_id=user.id, status=ParticipationStatus.PENDING ) db.add(participation) # Create notification if user.id != current_user.id: notification = Notification( user_id=user.id, type=NotificationType.EVENT_INVITATION, title=f"Nouvel événement: {event.title}", message=f"{current_user.full_name} a créé un nouvel événement", link=f"/events/{event.id}" ) db.add(notification) # Send email notification (async task would be better) try: send_event_notification(user.email, event) except: pass # Don't fail if email sending fails db.commit() return format_event_response(event, db) @router.get("/", response_model=List[EventResponse]) async def get_events( db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user), upcoming: bool = None ): """Get all events, optionally filtered by upcoming status.""" query = db.query(Event).options(joinedload(Event.creator)) if upcoming is True: # Only upcoming events query = query.filter(Event.date >= datetime.utcnow()) elif upcoming is False: # Only past events query = query.filter(Event.date < datetime.utcnow()) # If upcoming is None, return all events events = query.order_by(Event.date.desc()).all() return [format_event_response(event, db) for event in events] @router.get("/upcoming", response_model=List[EventResponse]) async def get_upcoming_events( db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """Get only upcoming events.""" events = db.query(Event).options(joinedload(Event.creator)).filter( Event.date >= datetime.utcnow() ).order_by(Event.date).all() return [format_event_response(event, db) for event in events] @router.get("/past", response_model=List[EventResponse]) async def get_past_events( db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """Get only past events.""" events = db.query(Event).options(joinedload(Event.creator)).filter( Event.date < datetime.utcnow() ).order_by(Event.date.desc()).all() return [format_event_response(event, db) for event in events] @router.get("/{event_id}", response_model=EventResponse) async def get_event( event_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """Get a specific event.""" event = db.query(Event).filter(Event.id == event_id).first() if not event: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Event not found" ) return format_event_response(event, db) @router.put("/{event_id}", response_model=EventResponse) async def update_event( event_id: int, event_update: EventUpdate, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """Update an event.""" event = db.query(Event).filter(Event.id == event_id).first() if not event: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Event not found" ) if event.creator_id != current_user.id and not current_user.is_admin: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not authorized to update this event" ) for field, value in event_update.dict(exclude_unset=True).items(): setattr(event, field, value) db.commit() db.refresh(event) return format_event_response(event, db) @router.delete("/{event_id}") async def delete_event( event_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """Delete an event.""" event = db.query(Event).filter(Event.id == event_id).first() if not event: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Event not found" ) if event.creator_id != current_user.id and not current_user.is_admin: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Not authorized to delete this event" ) db.delete(event) db.commit() return {"message": "Event deleted successfully"} @router.put("/{event_id}/participation", response_model=EventResponse) async def update_participation( event_id: int, participation_update: ParticipationUpdate, db: Session = Depends(get_db), current_user: User = Depends(get_current_active_user) ): """Update user participation status for an event.""" participation = db.query(EventParticipation).filter( EventParticipation.event_id == event_id, EventParticipation.user_id == current_user.id ).first() if not participation: # Create new participation if it doesn't exist participation = EventParticipation( event_id=event_id, user_id=current_user.id, status=participation_update.status ) db.add(participation) else: participation.status = participation_update.status participation.response_date = datetime.utcnow() db.commit() # Update user attendance rate update_user_attendance_rate(current_user, db) event = db.query(Event).filter(Event.id == event_id).first() return format_event_response(event, db) def format_event_response(event: Event, db: Session) -> dict: """Format event response with participation counts.""" participations = [] present_count = absent_count = maybe_count = pending_count = 0 # Get creator user directly creator = db.query(User).filter(User.id == event.creator_id).first() for p in event.participations: user = db.query(User).filter(User.id == p.user_id).first() participations.append({ "user_id": user.id, "username": user.username, "full_name": user.full_name, "avatar_url": user.avatar_url, "status": p.status, "response_date": p.response_date }) if p.status == ParticipationStatus.PRESENT: present_count += 1 elif p.status == ParticipationStatus.ABSENT: absent_count += 1 elif p.status == ParticipationStatus.MAYBE: maybe_count += 1 else: pending_count += 1 return { "id": event.id, "title": event.title, "description": event.description, "location": event.location, "date": event.date, "end_date": event.end_date, "creator_id": event.creator_id, "cover_image": event.cover_image, "created_at": event.created_at, "updated_at": event.updated_at, "creator_name": creator.full_name if creator else "Unknown", "creator_avatar": creator.avatar_url if creator else None, "participations": participations, "present_count": present_count, "absent_count": absent_count, "maybe_count": maybe_count, "pending_count": pending_count } def update_user_attendance_rate(user: User, db: Session): """Update user attendance rate based on past events.""" past_participations = db.query(EventParticipation).join(Event).filter( EventParticipation.user_id == user.id, Event.date < datetime.utcnow(), EventParticipation.status.in_([ParticipationStatus.PRESENT, ParticipationStatus.ABSENT]) ).all() if past_participations: present_count = sum(1 for p in past_participations if p.status == ParticipationStatus.PRESENT) user.attendance_rate = (present_count / len(past_participations)) * 100 db.commit()