33from typing import Any , Dict , List , Optional
44
55from fastapi import APIRouter , Depends , HTTPException , Request
6+ from loguru import logger
67from sqlalchemy .exc import SQLAlchemyError
78from sqlalchemy .orm import Session
89from sqlalchemy .orm .exc import MultipleResultsFound , NoResultFound
910from starlette import status
1011from starlette .responses import RedirectResponse
11- from starlette .status import HTTP_302_FOUND
1212
1313from app .database .database import get_db
1414from app .database .models import Event , User , UserEvent
15- from app .dependencies import logger , templates
15+ from app .dependencies import templates
1616from app .internal .event import validate_zoom_link
1717from app .internal .utils import create_model
1818from app .routers .user import create_user
@@ -53,18 +53,13 @@ async def create_new_event(request: Request, session=Depends(get_db)):
5353 location )
5454 return RedirectResponse (router .url_path_for ('eventview' ,
5555 event_id = event .id ),
56- status_code = HTTP_302_FOUND )
56+ status_code = status . HTTP_302_FOUND )
5757
5858
5959@router .get ("/{event_id}" )
6060async def eventview (request : Request , event_id : int ,
6161 db : Session = Depends (get_db )):
62- try :
63- event = get_event_by_id (db , event_id )
64- except NoResultFound :
65- raise HTTPException (status_code = 404 , detail = "Event not found" )
66- except MultipleResultsFound :
67- raise HTTPException (status_code = 500 , detail = "Multiple events found" )
62+ event = by_id (db , event_id )
6863 start_format = '%A, %d/%m/%Y %H:%M'
6964 end_format = ('%H:%M' if event .start .date () == event .end .date ()
7065 else start_format )
@@ -74,102 +69,116 @@ async def eventview(request: Request, event_id: int,
7469 "end_format" : end_format })
7570
7671
77- @router .delete ("/{event_id}" )
78- def delete_event (request : Request , event_id : int ,
79- db : Session = Depends (get_db )):
80- # TODO: Check if the user is the owner of the event.
81- try :
82- event = get_event_by_id (db , event_id )
83- except NoResultFound :
84- raise HTTPException (status_code = 404 , detail = "Event not found" )
85- except MultipleResultsFound :
86- raise HTTPException (status_code = 500 , detail = "Multiple events found" )
87-
88- participants = get_participants_emails_by_event (db , event_id )
89-
90- try :
91- db .delete (event )
92- db .query (UserEvent ).filter_by (event_id = event_id ).delete ()
93- db .commit ()
94- except (SQLAlchemyError , TypeError ):
95- return templates .TemplateResponse (
96- "event/eventview.html" , {"request" : request , "event_id" : event_id },
97- status_code = status .HTTP_500_INTERNAL_SERVER_ERROR )
98-
99- if participants and event .start > datetime .now ():
100- pass
101- # TODO: Send them a cancellation notice
102- # if the deletion is successful
103- return RedirectResponse (
104- url = "/calendar" , status_code = status .HTTP_200_OK )
72+ UPDATE_EVENTS_FIELDS = {
73+ 'title' : str ,
74+ 'start' : datetime ,
75+ 'end' : datetime ,
76+ 'content' : (str , type (None )),
77+ 'location' : (str , type (None ))
78+ }
10579
10680
107- def get_event_by_id (db : Session , event_id : int ) -> Event :
108- """Gets a single event by id"""
81+ def by_id (db : Session , event_id : int ) -> Event :
82+ """Get a single event by id"""
10983 if not isinstance (db , Session ):
110- raise AttributeError (
84+ error_message = (
11185 f'Could not connect to database. '
11286 f'db instance type received: { type (db )} ' )
87+ logger .critical (error_message )
88+ raise HTTPException (
89+ status_code = status .HTTP_500_INTERNAL_SERVER_ERROR ,
90+ detail = error_message )
91+
11392 try :
11493 event = db .query (Event ).filter_by (id = event_id ).one ()
11594 except NoResultFound :
116- raise NoResultFound (f"Event ID does not exist. ID: { event_id } " )
95+ error_message = f"Event ID does not exist. ID: { event_id } "
96+ logger .exception (error_message )
97+ raise HTTPException (
98+ status_code = status .HTTP_404_NOT_FOUND ,
99+ detail = error_message )
117100 except MultipleResultsFound :
118101 error_message = (
119102 f'Multiple results found when getting event. Expected only one. '
120103 f'ID: { event_id } ' )
121104 logger .critical (error_message )
122- raise MultipleResultsFound (error_message )
105+ raise HTTPException (
106+ status_code = status .HTTP_500_INTERNAL_SERVER_ERROR ,
107+ detail = error_message )
123108 return event
124109
125110
126- def is_date_before (start_date : datetime , end_date : datetime ) -> bool :
111+ def is_end_date_before_start_date (
112+ start_date : datetime , end_date : datetime ) -> bool :
127113 """Check if the start date is earlier than the end date"""
128114
129- return start_date < end_date
130-
115+ return start_date > end_date
131116
132- def is_it_possible_to_change_dates (old_event : Event ,
133- event : Dict [str , Any ]) -> bool :
134- return is_date_before (
135- event .get ('start' , old_event .start ),
136- event .get ('end' , old_event .end ))
137-
138-
139- def get_items_that_can_be_updated (event : Dict [str , Any ]) -> Dict [str , Any ]:
140- """Extract only that keys to update"""
141-
142- return {i : event [i ] for i in (
143- 'title' , 'start' , 'end' , 'content' , 'location' ) if i in event }
144-
145-
146- def update_event (event_id : int , event : Dict , db : Session
147- ) -> Optional [Event ]:
148- # TODO Check if the user is the owner of the event.
149117
150- event_to_update = get_items_that_can_be_updated ( event )
151- if not event_to_update :
152- return None
118+ def check_change_dates_allowed (
119+ old_event : Event , event : Dict [ str , Any ]) :
120+ allowed = 1
153121 try :
154- old_event = get_event_by_id (db , event_id )
155- except NoResultFound :
156- raise HTTPException (status_code = 404 , detail = "Event not found" )
157- except MultipleResultsFound :
158- raise HTTPException (status_code = 500 , detail = "Multiple events found" )
122+ start_date = event .get ('start' , old_event .start )
123+ end_date = event .get ('end' , old_event .end )
124+ if is_end_date_before_start_date (start_date , end_date ):
125+ allowed = 0
126+ except TypeError :
127+ allowed = 0
128+ if allowed == 0 :
129+ raise HTTPException (
130+ status_code = status .HTTP_400_BAD_REQUEST ,
131+ detail = "Invalid times" )
132+
133+
134+ def is_fields_types_valid (to_check : Dict [str , Any ], types : Dict [str , Any ]):
135+ """validate dictionary values by dictionary of types"""
136+ errors = []
137+ for field_name , field_type in to_check .items ():
138+ if types [field_name ] and not isinstance (field_type , types [field_name ]):
139+ errors .append (
140+ f"{ field_name } is '{ type (field_type ).__name__ } ' and"
141+ + f"it should be from type '{ types [field_name ].__name__ } '" )
142+ logger .warning (errors )
143+ if errors :
144+ raise HTTPException (
145+ status_code = status .HTTP_400_BAD_REQUEST , detail = errors )
146+
147+
148+ def get_event_with_editable_fields_only (event : Dict [str , Any ]
149+ ) -> Dict [str , Any ]:
150+ """Remove all keys that are not allowed to update"""
151+
152+ return {i : event [i ] for i in UPDATE_EVENTS_FIELDS if i in event }
153+
154+
155+ def _update_event (db : Session , event_id : int , event_to_update : Dict ) -> Event :
159156 try :
160- if not is_it_possible_to_change_dates (old_event , event_to_update ):
161- return None
162-
163157 # Update database
164158 db .query (Event ).filter (Event .id == event_id ).update (
165159 event_to_update , synchronize_session = False )
160+
166161 db .commit ()
162+ return by_id (db , event_id )
163+ except (AttributeError , SQLAlchemyError ) as e :
164+ logger .exception (str (e ))
165+ raise HTTPException (
166+ status_code = status .HTTP_500_INTERNAL_SERVER_ERROR ,
167+ detail = "Internal server error" )
167168
168- # TODO: Send emails to recipients.
169- except (AttributeError , SQLAlchemyError , TypeError ):
170- return None
171169
172- return get_event_by_id (db = db , event_id = event_id )
170+ def update_event (event_id : int , event : Dict , db : Session
171+ ) -> Optional [Event ]:
172+ # TODO Check if the user is the owner of the event.
173+ old_event = by_id (db , event_id )
174+ event_to_update = get_event_with_editable_fields_only (event )
175+ is_fields_types_valid (event_to_update , UPDATE_EVENTS_FIELDS )
176+ check_change_dates_allowed (old_event , event_to_update )
177+ if not event_to_update :
178+ return None
179+ event_updated = _update_event (db , event_id , event_to_update )
180+ # TODO: Send emails to recipients.
181+ return event_updated
173182
174183
175184def create_event (db , title , start , end , owner_id , content = None , location = None ):
@@ -203,10 +212,42 @@ def get_participants_emails_by_event(db: Session, event_id: int) -> List[str]:
203212 """Returns a list of all the email address of the event invited users,
204213 by event id."""
205214
206- return (
207- [email [0 ] for email in db .query (User .email ).
215+ return [email [0 ] for email in db .query (User .email ).
208216 select_from (Event ).
209217 join (UserEvent , UserEvent .event_id == Event .id ).
210218 join (User , User .id == UserEvent .user_id ).
211219 filter (Event .id == event_id ).
212- all ()])
220+ all ()]
221+
222+
223+ def _delete_event (db : Session , event : Event ):
224+ try :
225+ # Delete event
226+ db .delete (event )
227+
228+ # Delete user_event
229+ db .query (UserEvent ).filter (UserEvent .event_id == event .id ).delete ()
230+
231+ db .commit ()
232+
233+ except (SQLAlchemyError , AttributeError ) as e :
234+ logger .exception (str (e ))
235+ raise HTTPException (
236+ status_code = status .HTTP_500_INTERNAL_SERVER_ERROR ,
237+ detail = "Deletion failed" )
238+
239+
240+ @router .delete ("/{event_id}" )
241+ def delete_event (event_id : int ,
242+ db : Session = Depends (get_db )):
243+
244+ # TODO: Check if the user is the owner of the event.
245+ event = by_id (db , event_id )
246+ participants = get_participants_emails_by_event (db , event_id )
247+ _delete_event (db , event )
248+ if participants and event .start > datetime .now ():
249+ pass
250+ # TODO: Send them a cancellation notice
251+ # if the deletion is successful
252+ return RedirectResponse (
253+ url = "/calendar" , status_code = status .HTTP_200_OK )
0 commit comments