1- from typing import List
2- from datetime import datetime , timedelta
1+ from typing import List , Optional
2+ from datetime import datetime
33
44from app .database import get_db
55from sqlalchemy .orm import Session
6- from fastapi import Path , Depends , APIRouter
6+ from sqlalchemy import or_ , and_ , desc , asc
7+ from fastapi import Path , Depends , APIRouter , Query , HTTPException , status
78from app .filters .events_filter import EventsFilter
89from app .repositories .event_repository import EventRepository , get_event_repository
910from app .schemas .event import EventBase , EventUpdate , EventDetails , NotificationRequest
1011from app .utils .jwt_auth import get_current_organizer , get_current_admin
12+ from app .models .events import EventModel
13+ from app .models .location import LocationModel
14+ from app .models .ticket_type import TicketTypeModel
1115
1216router = APIRouter (prefix = "/events" , tags = ["events" ])
1317
1418
1519@router .post ("/" , response_model = EventDetails )
1620async def create_event (
17- event_data : EventBase ,
18- event_repo : EventRepository = Depends (get_event_repository ),
19- current_organizer = Depends (get_current_organizer )
21+ event_data : EventBase ,
22+ event_repo : EventRepository = Depends (get_event_repository ),
23+ current_organizer = Depends (get_current_organizer )
2024):
2125 """Create a new event (requires authentication)"""
2226 return event_repo .create_event (event_data , current_organizer ["role_id" ])
2327
2428
2529@router .post ("/authorize/{event_id}" , response_model = bool )
2630async def authorize_event (
27- event_id : int = Path (..., title = "Event ID" ),
28- event_repo : EventRepository = Depends (get_event_repository ),
29- current_admin = Depends (get_current_admin )
31+ event_id : int = Path (..., title = "Event ID" ),
32+ event_repo : EventRepository = Depends (get_event_repository ),
33+ current_admin = Depends (get_current_admin )
3034):
3135 """Authorize an event (requires admin authentication)"""
3236 event_repo .authorize_event (event_id )
@@ -35,9 +39,9 @@ async def authorize_event(
3539
3640@router .post ("/reject/{event_id}" , response_model = bool )
3741async def reject_event (
38- event_id : int = Path (..., title = "Event ID" ),
39- event_repo : EventRepository = Depends (get_event_repository ),
40- current_admin = Depends (get_current_admin )
42+ event_id : int = Path (..., title = "Event ID" ),
43+ event_repo : EventRepository = Depends (get_event_repository ),
44+ current_admin = Depends (get_current_admin )
4145):
4246 """Reject an event (requires admin authentication)"""
4347 event_repo .reject_event (event_id )
@@ -46,38 +50,151 @@ async def reject_event(
4650
4751@router .get ("" , response_model = List [EventDetails ])
4852def get_events_endpoint (
49- filters : EventsFilter = Depends (),
50- event_repo : EventRepository = Depends (get_event_repository ),
53+ page : int = Query (1 , ge = 1 , description = "Page number" ),
54+ limit : int = Query (50 , ge = 1 , le = 100 , description = "Items per page" ),
55+ search : Optional [str ] = Query (None , description = "Search by event name or description" ),
56+ location : Optional [str ] = Query (None , description = "Filter by location name" ),
57+ start_date_from : Optional [datetime ] = Query (None ,
58+ description = "Events starting after this date" ),
59+ start_date_to : Optional [datetime ] = Query (None ,
60+ description = "Events starting before this date" ),
61+ min_price : Optional [float ] = Query (None , ge = 0 ,
62+ description = "Minimum ticket price available" ),
63+ max_price : Optional [float ] = Query (None , ge = 0 ,
64+ description = "Maximum ticket price available" ),
65+ organizer_id : Optional [int ] = Query (None , description = "Filter by specific organizer" ),
66+ minimum_age : Optional [int ] = Query (None , ge = 0 ,
67+ description = "Minimum required age for attendees" ),
68+ status : Optional [str ] = Query (None , description = "Filter by event status" ),
69+ categories : Optional [str ] = Query (None ,
70+ description = "Filter by categories (comma-separated)" ),
71+ sort_by : str = Query ("start_date" ,
72+ description = "Sort field (start_date, name, creation_date)" ),
73+ sort_order : str = Query ("asc" , description = "Sort order (asc/desc)" ),
74+ db : Session = Depends (get_db ),
5175):
52- events = event_repo .get_events (filters )
76+ """
77+ Get list of events with advanced filtering, searching, and pagination
78+ """
79+ # Build the query with joins for filtering
80+ query = db .query (EventModel ).join (LocationModel ,
81+ EventModel .location_id == LocationModel .location_id )
82+
83+ # Apply search filter
84+ if search :
85+ search_filter = f"%{ search } %"
86+ query = query .filter (
87+ or_ (
88+ EventModel .name .ilike (search_filter ),
89+ EventModel .description .ilike (search_filter )
90+ )
91+ )
92+
93+ # Apply location filter
94+ if location :
95+ query = query .filter (LocationModel .name .ilike (f"%{ location } %" ))
96+
97+ # Apply date filters
98+ if start_date_from :
99+ query = query .filter (EventModel .start_date >= start_date_from )
100+ if start_date_to :
101+ query = query .filter (EventModel .start_date <= start_date_to )
102+
103+ # Apply organizer filter
104+ if organizer_id :
105+ query = query .filter (EventModel .organizer_id == organizer_id )
106+
107+ # Apply minimum age filter
108+ if minimum_age :
109+ query = query .filter (EventModel .minimum_age >= minimum_age )
110+
111+ # Apply status filter
112+ if status :
113+ if status not in ["pending" , "created" , "rejected" , "cancelled" ]:
114+ raise HTTPException (
115+ status_code = status .HTTP_400_BAD_REQUEST ,
116+ detail = "Invalid status. Must be one of: pending, created, rejected, cancelled"
117+ )
118+ query = query .filter (EventModel .status == status )
119+
120+ # Apply price filters (requires subquery on ticket types)
121+ if min_price is not None or max_price is not None :
122+ price_subquery = db .query (TicketTypeModel .event_id ).distinct ()
123+ if min_price is not None :
124+ price_subquery = price_subquery .filter (TicketTypeModel .price >= min_price )
125+ if max_price is not None :
126+ price_subquery = price_subquery .filter (TicketTypeModel .price <= max_price )
127+
128+ query = query .filter (EventModel .event_id .in_ (price_subquery ))
129+
130+ # Apply categories filter (simplified - assuming categories are stored as comma-separated values in description or using JSON)
131+ if categories :
132+ category_list = [cat .strip () for cat in categories .split ("," )]
133+ category_filters = [EventModel .description .ilike (f"%{ cat } %" ) for cat in category_list ]
134+ query = query .filter (or_ (* category_filters ))
135+
136+ # Apply sorting
137+ if sort_by not in ["start_date" , "name" , "creation_date" ]:
138+ raise HTTPException (
139+ status_code = status .HTTP_400_BAD_REQUEST ,
140+ detail = "Invalid sort_by. Must be one of: start_date, name, creation_date"
141+ )
142+
143+ if sort_order not in ["asc" , "desc" ]:
144+ raise HTTPException (
145+ status_code = status .HTTP_400_BAD_REQUEST ,
146+ detail = "Invalid sort_order. Must be 'asc' or 'desc'"
147+ )
148+
149+ # Map sort fields
150+ sort_field_map = {
151+ "start_date" : EventModel .start_date ,
152+ "name" : EventModel .name ,
153+ "creation_date" : EventModel .event_id # Assuming event_id correlates with creation order
154+ }
155+
156+ sort_field = sort_field_map [sort_by ]
157+ if sort_order == "desc" :
158+ query = query .order_by (desc (sort_field ))
159+ else :
160+ query = query .order_by (asc (sort_field ))
161+
162+ # Apply pagination
163+ offset = (page - 1 ) * limit
164+ query = query .offset (offset ).limit (limit )
165+
166+ # Execute query and get results
167+ events = query .all ()
168+
169+ # Convert to response models
53170 return [EventDetails .model_validate (e ) for e in events ]
54171
55172
56173@router .put ("/{event_id}" , response_model = EventDetails )
57174def update_event_endpoint (
58- event_id : int = Path (..., title = "Event ID" ),
59- update_data : EventUpdate = Depends (),
60- event_repo : EventRepository = Depends (get_event_repository ),
61- current_organizer = Depends (get_current_organizer )
175+ event_id : int = Path (..., title = "Event ID" ),
176+ update_data : EventUpdate = Depends (),
177+ event_repo : EventRepository = Depends (get_event_repository ),
178+ current_organizer = Depends (get_current_organizer )
62179):
63180 return event_repo .update_event (event_id , update_data , current_organizer ["role_id" ])
64181
65182
66183@router .delete ("/{event_id}" , response_model = bool )
67184def cancel_event_endpoint (
68- event_id : int = Path (..., title = "Event ID" ),
69- event_repo : EventRepository = Depends (get_event_repository ),
70- current_organizer = Depends (get_current_organizer )
185+ event_id : int = Path (..., title = "Event ID" ),
186+ event_repo : EventRepository = Depends (get_event_repository ),
187+ current_organizer = Depends (get_current_organizer )
71188):
72189 event_repo .cancel_event (event_id , current_organizer ["role_id" ])
73190 return True
74191
75192
76193@router .post ("/{event_id}/notify" )
77194async def notify_participants (
78- event_id : int = Path (..., title = "Event ID" ),
79- notification : NotificationRequest = None ,
80- current_organizer = Depends (get_current_organizer ),
195+ event_id : int = Path (..., title = "Event ID" ),
196+ notification : NotificationRequest = None ,
197+ current_organizer = Depends (get_current_organizer ),
81198):
82199 """Notify participants of an event (requires organizer authentication)"""
83200 return {
0 commit comments