diff --git a/controllers/v2/unavailability/api.py b/controllers/v2/unavailability/api.py index 87870c79..854f2f2a 100644 --- a/controllers/v2/unavailability/api.py +++ b/controllers/v2/unavailability/api.py @@ -1,14 +1,8 @@ -import json -import uuid - -from flask import jsonify from flask_restful import reqparse, Resource, marshal_with, inputs -from domain.entity import unavailability_time from .response_models import volunteer_unavailability_time -from domain import session_scope, UserType -from repository.volunteer_unavailability_v2 import * -from repository.unavailability_repository import * +from domain import UserType +from repository.volunteer_unavailability_v2 import EventRepository from services.jwk import requires_auth, is_user_or_has_role from controllers.v2.v2_blueprint import v2_api @@ -20,49 +14,53 @@ class SpecificVolunteerUnavailabilityV2(Resource): + event_repository: EventRepository + + def __init__(self, event_repository: EventRepository = EventRepository()): + self.event_repository = event_repository @requires_auth @is_user_or_has_role(None, UserType.ROOT_ADMIN) def put(self, user_id, event_id): args = edit_parser.parse_args() - with session_scope() as session: - success = edit_event(session, user_id, event_id, **args) - if success is True: - return {"message": "Updated successfully"}, 200 - elif success is False: - return {"message": "Event not found"}, 404 - else: - return {"message": "Unexpected Error Occurred"}, 400 + success = self.event_repository.edit_event(user_id, event_id, **args) + if success is True: + return {"message": "Updated successfully"}, 200 + elif success is False: + return {"message": "Event not found"}, 404 + else: + return {"message": "Unexpected Error Occurred"}, 400 @requires_auth @is_user_or_has_role(None, UserType.ROOT_ADMIN) def delete(self, user_id, event_id): - with session_scope() as session: - try: - success = remove_event(session, user_id, event_id) - if success: - # If the event is successfully removed, return HTTP 200 OK. - return {"message": "Unavailability event removed successfully."}, 200 - else: - # If the event does not exist or could not be removed, return HTTP 404 Not Found. - return {"message": "Unavailability event not found."}, 404 - except Exception as e: - # HTTP 500 Internal Server Error - return {"message": "Internal server error", "error": str(e)}, 500 + try: + success = self.event_repository.remove_event(user_id, event_id) + if success: + # If the event is successfully removed, return HTTP 200 OK. + return {"message": "Unavailability event removed successfully."}, 200 + else: + # If the event does not exist or could not be removed, return HTTP 404 Not Found. + return {"message": "Unavailability event not found."}, 404 + except Exception as e: + # HTTP 500 Internal Server Error + return {"message": "Internal server error", "error": str(e)}, 500 class VolunteerUnavailabilityV2(Resource): + def __init__(self): + self.event_repository = EventRepository() + @requires_auth @marshal_with(volunteer_unavailability_time) @is_user_or_has_role(None, UserType.ROOT_ADMIN) def get(self, user_id): - with session_scope() as session: - volunteer_unavailability_record = fetch_event(session, user_id) - if volunteer_unavailability_record is not None: - return volunteer_unavailability_record - else: - return jsonify({'userID': user_id, 'success': False}), 400 + volunteer_unavailability_record = self.event_repository.get_event(user_id) + if volunteer_unavailability_record is not None: + return volunteer_unavailability_record + else: + return {"message": "No unavailability record found."}, 400 @requires_auth @is_user_or_has_role(None, UserType.ROOT_ADMIN) @@ -73,40 +71,33 @@ def post(self, user_id): if args['start'] >= args['end']: return {"message": "Start time must be earlier than end time"}, 400 # HTTP 400 Bad Request - with session_scope() as session: - # checks if new time frame overlaps with any existing in the database for specific userId - overlapping_events = session.query(UnavailabilityTime).filter( - UnavailabilityTime.userId == user_id, - UnavailabilityTime.start < args['end'], - UnavailabilityTime.end > args['start'], - UnavailabilityTime.periodicity == args['periodicity'] - ).all() - if overlapping_events: - overlapping_details = [] - for event in overlapping_events: - overlapping_details.append({ - "eventId": event.eventId}) - return {"message": "Time frames overlap with existing events", - "overlapping_events": overlapping_details}, 400 # HTTP 400 Bad Request - - eventId = create_event( - session, - user_id, - args['title'], - args['start'], - args['end'], - args['periodicity'] - ) - if eventId is not None: - return {"eventId": eventId}, 200 # HTTP 200 OK - else: - return {"description": "Failed to create event"}, 400 # HTTP 400 Bad Request + overlapping_events = self.event_repository.check_overlapping_events(user_id, args['start'], args['end'], args['periodicity']) + if overlapping_events: + overlapping_details = [] + for event in overlapping_events: + overlapping_details.append({ + "eventId": event.eventId}) + return {"message": "Time frames overlap with existing events", + "overlapping_events": overlapping_details}, 400 # HTTP 400 Bad Request + + eventId = self.event_repository.create_event( + user_id, + args['title'], + args['start'], + args['end'], + args['periodicity'] + ) + if eventId is not None: + return {"eventId": eventId}, 200 # HTTP 200 OK + else: + return {"description": "Failed to create event"}, 400 # HTTP 400 Bad Request except Exception as e: return {"description": "Internal server error", "error": str(e)}, 500 # HTTP 500 Internal Server Error + v2_api.add_resource(SpecificVolunteerUnavailabilityV2, '/v2/volunteers/', - '/v2/volunteers//unavailability/') + '/v2/volunteers//unavailability/') v2_api.add_resource(VolunteerUnavailabilityV2, '/v2/volunteers/', - '/v2/volunteers//unavailability') + '/v2/volunteers//unavailability') diff --git a/repository/volunteer_unavailability_v2.py b/repository/volunteer_unavailability_v2.py index e717e372..f5ed69a5 100644 --- a/repository/volunteer_unavailability_v2.py +++ b/repository/volunteer_unavailability_v2.py @@ -1,25 +1,115 @@ import logging -from repository.unavailability_repository import * +from flask import jsonify +from datetime import datetime -def edit_event(session, userId, eventId, title=None, start=None, end=None, periodicity=None): - try: - event = session.query(UnavailabilityTime).filter(UnavailabilityTime.eventId == eventId, - UnavailabilityTime.userId == userId).first() - if event is None: +from domain import UnavailabilityTime, session_scope + + +class EventRepository: + def __init__(self): + pass + def edit_event(self, userId, eventId, title=None, start=None, end=None, periodicity=None): + with session_scope() as session: + try: + event = session.query(UnavailabilityTime).filter(UnavailabilityTime.eventId == eventId, + UnavailabilityTime.userId == userId).first() + if event is None: + return False + if title is not None: + event.title = title + if start is not None: + event.start = start + if end is not None: + event.end = end + if end is not None: + event.periodicity = periodicity + session.commit() + return True + except Exception as e: + session.rollback() + logging.error(e) + return None + + def get_event(self, userId): + """ + get all the non-availability events of the given user + :param session: session + :param userId: Integer, user id, who want to query the events + """ + now = datetime.now() + with session_scope() as session: + try: + # only show the unavailability time that is end in the future + events = session.query(UnavailabilityTime).filter( + UnavailabilityTime.userId == userId, UnavailabilityTime.status == 1, UnavailabilityTime.end > now).all() + if events: + event_records = [] + for event in events: + # if the start time is earlier than now, then show from now to the end time + start_time = max(event.start, now) + event_record = { + "eventId": event.eventId, + "userId": event.userId, + "title": event.title, + "startTime": start_time.isoformat(), + "endTime": event.end.isoformat(), + "periodicity": event.periodicity + } + event_records.append(event_record) + return jsonify(event_records) + else: + return None + except Exception as e: + logging.error(e) + return None + + # copy from repository.unavailability_repository.py + def create_event(self, userId, title, startTime, endTime, periodicity): + """ + Function to create an event + :param session: session + :param userId: Integer, user id + :param title: String, reason why unavailable + :param startTime: DateTime, from what time is unavailable + :param endTime: DateTime, to what time is unavailable + :param periodicity: Integer, Daily = 1, Weekly = 2, One-Off = 3 + """ + event = UnavailabilityTime(userId=userId, title=title, start=startTime, end=endTime, + periodicity=periodicity) + with session_scope() as session: + session.add(event) + # session.expunge(question) + session.flush() + return event.eventId + + # copy from repository.unavailability_repository.py + def remove_event(self, userId, eventId): + """ + Function to remove an event + :param session: session + :param userId: Integer, user id, who want to remove an event + :param eventId: Integer, event id want to remove + :return: True: remove successful + False: remove failed + """ + with session_scope() as session: + existing = session.query(UnavailabilityTime).filter(UnavailabilityTime.userId == userId, + UnavailabilityTime.eventId == eventId).first() + if existing is not None and existing.status is True: + existing.status = False + return True return False - if title is not None: - event.title = title - if start is not None: - event.start = start - if end is not None: - event.end = end - if end is not None: - event.periodicity = periodicity - session.commit() - return True - except Exception as e: - session.rollback() - logging.error(e) - return None + + # copy from post function in api.py written by Steven + def check_overlapping_events(self, userId, startTime, endTime, periodicity): + with session_scope() as session: + # checks if new time frame overlaps with any existing in the database for specific userId + overlapping_events = session.query(UnavailabilityTime).filter( + UnavailabilityTime.userId == userId, + UnavailabilityTime.start < endTime, + UnavailabilityTime.end > startTime, + UnavailabilityTime.periodicity == periodicity + ).all() + return overlapping_events \ No newline at end of file