Exemplo n.º 1
0
    def post(self):
        """Listing all availabilities for a given day, and for a given room if requested."""
        # Get and validate inputs:
        args = self.parser.parse_args(strict=True)
        target_day = args["target_day"]
        room_code = args.get("room_code")
        floor = args.get("floor")

        # Get the rooms for which the computations must be done:
        db_session = new_session()
        if room_code:
            room = db_session.query(Room).get(room_code)
            if not room:
                raise NotFound(f"Unknown room code: {room_code}.")
            rooms = [room]
        elif floor is not None:
            rooms = db_session.query(Room).filter_by(floor=floor).all()
        else:
            rooms = db_session.query(Room).all()
        db_session.close()

        # Compute availabilities for all these rooms:
        availabilities = get_available_slots(
            target_day, room_codes=[r.code for r in rooms])
        return availabilities, 200
Exemplo n.º 2
0
    def post(self):
        """Try to book a room"""
        # Get and validate inputs:
        args = self.post_parser.parse_args(strict=True)
        args = _validate_booking_inputs(args)

        # Check the availability of the room for the requested period:
        room_code = args["room_code"]
        start_datetime = args["start_datetime"]
        if not is_room_available(room_code, start_datetime,
                                 args["duration_in_hours"]):
            room_availability_info = get_available_slots(
                start_datetime.date(), room_codes=[room_code])
            assert len(room_availability_info) == 1
            return marshal(room_availability_info[0],
                           room_availabilities_model), 409

        # Book the room:
        new_booking = Booking(
            author=args["author"],
            start_datetime=start_datetime,
            duration=args["duration_in_hours"],
            room_code=room_code,
        )
        db_session = new_session()
        db_session.add(new_booking)
        db_session.commit()
        db_session.close()

        return marshal(new_booking, booking_model), 201
Exemplo n.º 3
0
 def get(self, id: int):
     """Get a booking from its id."""
     db_session = new_session()
     booking = db_session.query(Booking).get(id)
     db_session.close()
     if not booking:
         raise NotFound(f"This booking ID does not exist: {id}.")
     return booking, 200
Exemplo n.º 4
0
 def get(self, code: str):
     """Get the room identified by the code."""
     db_session = new_session()
     room = db_session.query(Room).get(code)
     db_session.close()
     if not room:
         raise NotFound(f"The code {code} does not identify any room.")
     return room, 200
Exemplo n.º 5
0
    def delete(self, id: int):
        """Delete a booking identified by its id."""
        db_session = new_session()

        # First check that this booking exists:
        booking = db_session.query(Booking).get(id)
        if not booking:
            raise NotFound(f"This booking ID does not exist: {id}.")

        # Then delete it:
        db_session.delete(booking)
        db_session.commit()
        db_session.close()

        return None, 204
Exemplo n.º 6
0
    def get(self):
        """List all bookings"""
        # Get the filters from inputs:
        filters = self.list_parser.parse_args(strict=True)

        # Build the filtered query:
        db_session = new_session()
        query = db_session.query(Booking)
        day_filter_value = filters.pop("day")
        if day_filter_value:
            query = query.filter(
                func.DATE(Booking.start_datetime) == day_filter_value)
        actual_filters = {
            key: value
            for key, value in filters.items() if value is not None
        }
        query = query.filter_by(**actual_filters)

        # Return all matching results:
        bookings = query.all()
        db_session.close()
        return bookings, 200
Exemplo n.º 7
0
    def get(self):
        """List all rooms"""
        # Get the input filters, if any:
        filters = self.parser.parse_args(strict=True)
        search_in_name = filters.get("search_in_name")
        floor = filters.get("floor")
        min_capacity = filters.get("min_capacity")

        # Build the query:
        db_session = new_session()
        query = db_session.query(Room)
        if search_in_name:
            query = query.filter(Room.name.ilike(f"%{search_in_name}%"))
        if floor is not None:
            query = query.filter_by(floor=floor)
        if min_capacity:
            query = query.filter(Room.capacity >= min_capacity)

        # Return all matching results:
        res = query.all()
        db_session.close()
        return res, 200
Exemplo n.º 8
0
def _validate_booking_inputs(input_args: Dict[str, Any]) -> Dict[str, Any]:
    # Extract values (all are required, no error expected):
    output_args = input_args.copy()
    room_code: str = input_args["room_code"]
    start_datetime: dt.datetime = input_args["start_datetime"]
    duration: int = input_args["duration_in_hours"]

    # Check that the room_code refers to an existing room:
    db_session = new_session()
    room = db_session.query(Room).get(room_code)
    if not room:
        raise NotFound(
            f"No room bearing the code {room_code}. Please provide a valid one."
        )

    # Check that the start datetime is an hour start:
    if start_datetime.minute != 0 or start_datetime.second != 0:
        raise UnprocessableEntity(
            "The start_datetime must not contain minutes nor seconds: one can only book rooms for entire hours."
        )

    # Make the start datetime localized in the same time zone as the room:
    local_tz = timezone(room.building.tz_name)
    if start_datetime.tzinfo is None:
        output_args["start_datetime"] = local_tz.localize(start_datetime)
    else:
        output_args["start_datetime"] = start_datetime.astimezone(local_tz)

    # Check that the booking duration does not lead to the next day:
    if not 0 < duration <= 25:
        raise UnprocessableEntity(
            "No booking duration is allowed to exceed a day. "
            "The parameter duration_in_hours must be a positive number less or equal to 24."
        )

    db_session.close()

    return output_args
Exemplo n.º 9
0
def is_room_available(room_code: str, start_datetime: dt.datetime,
                      duration_in_hours: int) -> bool:
    """
    Returns True if the room is available during the whole requested period, False otherwise.
    """
    # Get all bookings related to this room:
    db_session = new_session()
    daily_room_bookings = db_session.query(Booking) \
        .filter_by(room_code=room_code) \
        .filter(func.DATE(Booking.start_datetime) == start_datetime.date()) \
        .all()

    # Detect any booked period overlapping the requested one:
    end_datetime = start_datetime + dt.timedelta(hours=duration_in_hours)
    for booking in daily_room_bookings:
        if start_datetime <= booking.start_datetime < end_datetime or \
                start_datetime < booking.start_datetime + dt.timedelta(hours=booking.duration) <= end_datetime:
            return False

    db_session.close()

    # If none was found, the room is available:
    return True
Exemplo n.º 10
0
def get_available_slots(requested_day: dt.date, *,
                        room_codes: List[str]) -> List[RoomFreeSlots]:
    """
    Return the list of bookable periods during the requested day for the target rooms.
    """
    # Get all bookings related to these rooms:
    db_session = new_session()
    requested_day_bookings = db_session.query(Booking) \
        .filter(Booking.room_code.in_(room_codes), func.DATE(Booking.start_datetime) == requested_day) \
        .order_by(Booking.room_code, Booking.start_datetime) \
        .all()

    # Reorganize the results per room:
    bookings_per_room: Dict[str, List[Booking]] = {}
    for booking in requested_day_bookings:
        bookings_per_room.setdefault(booking.room_code, []).append(booking)

    # Compute the remaining free slots...
    free_slots = []
    for code in room_codes:

        # ... for the rooms that are free for the whole day (for which no booking exists):
        if code not in bookings_per_room:
            room = db_session.query(Room).get(code)
            local_tz = timezone(room.building.tz_name)
            requested_day_start = local_tz.localize(
                dt.datetime.combine(requested_day, dt.time()))
            room_free_slots = [{
                "start_datetime": requested_day_start,
                "duration_in_hours": 24
            }]
            free_slots.append({
                "room_code": code,
                "free_slots": room_free_slots
            })
            continue

        # ... for each room having at least one booking:
        room_bookings = bookings_per_room[code]
        room_free_slots = []

        # ...   First, detect if there is a free slot before the first booking:
        first_booking = room_bookings[0]
        first_booking_start: dt.datetime = first_booking.start_datetime
        local_tz = timezone(first_booking.room.building.tz_name)
        requested_day_start = local_tz.localize(
            dt.datetime.combine(requested_day, dt.time()))
        if first_booking_start.hour != 0:
            room_free_slots.append({
                "start_datetime":
                requested_day_start,
                "duration_in_hours":
                first_booking_start.hour
            })

        # ...   Second, detect all available slots between two bookings:
        for i, booking in enumerate(requested_day_bookings[:-1]):
            current_booking_end_datetime = booking.start_datetime + dt.timedelta(
                hours=booking.duration)
            next_booking_start_datetime = requested_day_bookings[
                i + 1].start_datetime
            if current_booking_end_datetime != next_booking_start_datetime:
                new_free_slot_timedelta = next_booking_start_datetime - current_booking_end_datetime
                new_free_slot_duration_in_hours = int(
                    new_free_slot_timedelta.total_seconds() // 3600)
                room_free_slots.append({
                    "start_datetime":
                    current_booking_end_datetime,
                    "duration_in_hours":
                    new_free_slot_duration_in_hours,
                })

        # ...   Finally, detect if there is a free slot at the end of the day, after the last booking:
        last_booking = requested_day_bookings[-1]
        last_booking_end = last_booking.start_datetime + dt.timedelta(
            hours=last_booking.duration)
        if last_booking_end.hour != 0:
            room_free_slots.append({
                "start_datetime":
                last_booking_end,
                "duration_in_hours":
                24 - last_booking_end.hour
            })

        # ...   Save the results:
        free_slots.append({"room_code": code, "free_slots": room_free_slots})

    db_session.close()

    return free_slots