Beispiel #1
0
def test_find_overlapping_with_different_room(overlapping_reservation,
                                              create_room):
    reservation, occurrence = overlapping_reservation
    assert reservation in Reservation.find_overlapping_with(
        room=reservation.room, occurrences=[occurrence]).all()
    assert reservation not in Reservation.find_overlapping_with(
        room=create_room(), occurrences=[occurrence]).all()
Beispiel #2
0
def test_find_overlapping_with_skip_reservation(overlapping_reservation):
    reservation, occurrence = overlapping_reservation
    assert reservation in Reservation.find_overlapping_with(
        room=reservation.room, occurrences=[occurrence]).all()
    assert reservation not in Reservation.find_overlapping_with(
        room=reservation.room,
        occurrences=[occurrence],
        skip_reservation_id=reservation.id).all()
Beispiel #3
0
def test_find_overlapping_with_is_not_valid(overlapping_reservation,
                                            dummy_user):
    reservation, occurrence = overlapping_reservation
    assert reservation in Reservation.find_overlapping_with(
        room=reservation.room, occurrences=[occurrence]).all()
    reservation.cancel(dummy_user, silent=True)
    assert reservation not in Reservation.find_overlapping_with(
        room=reservation.room, occurrences=[occurrence]).all()
def _merge_users(target, source, **kwargs):
    BlockingPrincipal.merge_users(target, source, 'blocking')
    Blocking.find(created_by_id=source.id).update(
        {Blocking.created_by_id: target.id})
    Reservation.find(created_by_id=source.id).update(
        {Reservation.created_by_id: target.id})
    Reservation.find(booked_for_id=source.id).update(
        {Reservation.booked_for_id: target.id})
    Room.find(owner_id=source.id).update({Room.owner_id: target.id})
    rb_settings.acls.merge_users(target, source)
def compose_rooms_stats(rooms):
    reservations = Reservation.find(
        Reservation.room_id.in_(r.id for r in rooms))
    return {
        'active': {
            'valid':
            reservations.filter(Reservation.is_valid,
                                ~Reservation.is_archived).count(),
            'pending':
            reservations.filter(Reservation.is_pending,
                                ~Reservation.is_archived).count(),
            'cancelled':
            reservations.filter(Reservation.is_cancelled,
                                ~Reservation.is_archived).count(),
            'rejected':
            reservations.filter(Reservation.is_rejected,
                                ~Reservation.is_archived).count(),
        },
        'archived': {
            'valid':
            reservations.filter(Reservation.is_valid,
                                Reservation.is_archived).count(),
            'pending':
            reservations.filter(Reservation.is_pending,
                                Reservation.is_archived).count(),
            'cancelled':
            reservations.filter(Reservation.is_cancelled,
                                Reservation.is_archived).count(),
            'rejected':
            reservations.filter(Reservation.is_rejected,
                                Reservation.is_archived).count()
        }
    }
def calculate_rooms_booked_time(rooms, start_date=None, end_date=None):
    if end_date is None:
        end_date = date.today() - relativedelta(days=1)
    if start_date is None:
        start_date = end_date - relativedelta(days=29)
    # Reservations on working days
    reservations = Reservation.find(
        Reservation.room_id.in_(r.id for r in rooms),
        db.extract('dow', ReservationOccurrence.start_dt).between(1, 5),
        ReservationOccurrence.start_dt >= start_date,
        ReservationOccurrence.end_dt <= end_date,
        ReservationOccurrence.is_valid,
        _join=ReservationOccurrence)

    rsv_start = db.cast(ReservationOccurrence.start_dt, db.TIME)
    rsv_end = db.cast(ReservationOccurrence.end_dt, db.TIME)
    slots = ((db.cast(start, db.TIME), db.cast(end, db.TIME))
             for start, end in Location.working_time_periods)

    # this basically handles all possible ways an occurrence overlaps with each one of the working time slots
    overlaps = sum(
        db.case([((rsv_start < start) & (rsv_end > end),
                  db.extract('epoch', end - start)),
                 ((rsv_start < start) & (rsv_end > start) & (rsv_end <= end),
                  db.extract('epoch', rsv_end - start)),
                 ((rsv_start >= start) & (rsv_start < end) & (rsv_end > end),
                  db.extract('epoch', end - rsv_start)),
                 ((rsv_start >= start) & (rsv_end <= end),
                  db.extract('epoch', rsv_end - rsv_start))],
                else_=0) for start, end in slots)

    return reservations.with_entities(db.func.sum(overlaps)).scalar() or 0
 def _process(self):
     rooms = sorted(self._location.rooms,
                    key=lambda r: natural_sort_key(r.full_name))
     kpi = {}
     if self._with_kpi:
         kpi['occupancy'] = calculate_rooms_occupancy(self._location.rooms)
         kpi['total_rooms'] = len(self._location.rooms)
         kpi['active_rooms'] = sum(1 for room in self._location.rooms
                                   if room.is_active)
         kpi['reservable_rooms'] = sum(1 for room in self._location.rooms
                                       if room.is_reservable)
         kpi['reservable_capacity'] = sum(room.capacity or 0
                                          for room in self._location.rooms
                                          if room.is_reservable)
         kpi['reservable_surface'] = sum(room.surface_area or 0
                                         for room in self._location.rooms
                                         if room.is_reservable)
         kpi['booking_stats'] = compose_rooms_stats(self._location.rooms)
         kpi['booking_count'] = Reservation.find(
             Reservation.room.has(Room.location == self._location)).count()
     return WPRoomBookingAdminLocation(
         self,
         'rb-rooms',
         location=self._location,
         rooms=rooms,
         action_succeeded=self._actionSucceeded,
         equipment_types=self._location.equipment_types.all(),
         attributes=self._location.attributes.all(),
         kpi=kpi).display()
Beispiel #8
0
def test_is_valid(create_reservation, is_accepted, is_rejected, is_cancelled,
                  expected):
    reservation = create_reservation(is_accepted=is_accepted,
                                     is_rejected=is_rejected,
                                     is_cancelled=is_cancelled)
    assert reservation.is_valid == expected
    assert Reservation.find_first(is_valid=expected) == reservation
 def _create_reservation(**params):
     params.setdefault('start_dt',
                       date.today() + relativedelta(hour=8, minute=30))
     params.setdefault('end_dt',
                       date.today() + relativedelta(hour=17, minute=30))
     params.setdefault('repeat_frequency', RepeatFrequency.NEVER)
     params.setdefault(
         'repeat_interval',
         int(params['repeat_frequency'] != RepeatFrequency.NEVER))
     params.setdefault('is_accepted', True)
     params.setdefault('booking_reason', u'Testing')
     params.setdefault('room', dummy_room)
     params.setdefault('booked_for_user', dummy_user)
     params.setdefault('created_by_user', dummy_user)
     reservation = Reservation(**params)
     reservation.create_occurrences(skip_conflicts=False)
     db.session.add(reservation)
     db.session.flush()
     return reservation
 def _create_booking(self, form, room):
     if 'submit_book' in form and 'submit_prebook' in form:
         # Admins have the choice
         prebook = form.submit_prebook.data
     else:
         # Otherwise the existence of the book submit button means the user can book
         prebook = 'submit_book' not in form
     reservation = Reservation.create_from_data(room, form.data,
                                                session.user, prebook)
     db.session.add(reservation)
     db.session.flush()
     return reservation
Beispiel #11
0
def _export_reservations(hook,
                         limit_per_room,
                         include_rooms,
                         extra_filters=None):
    """Exports reservations.

    :param hook: The HTTPAPIHook instance
    :param limit_per_room: Should the limit/offset be applied per room
    :param include_rooms: Should reservations include room information
    """
    filters = list(extra_filters) if extra_filters else []
    if hook._fromDT and hook._toDT:
        filters.append(cast(Reservation.start_dt, Date) <= hook._toDT.date())
        filters.append(cast(Reservation.end_dt, Date) >= hook._fromDT.date())
        filters.append(cast(Reservation.start_dt, Time) <= hook._toDT.time())
        filters.append(cast(Reservation.end_dt, Time) >= hook._fromDT.time())
    elif hook._toDT:
        filters.append(cast(Reservation.end_dt, Date) <= hook._toDT.date())
        filters.append(cast(Reservation.end_dt, Time) <= hook._toDT.time())
    elif hook._fromDT:
        filters.append(cast(Reservation.start_dt, Date) >= hook._fromDT.date())
        filters.append(cast(Reservation.start_dt, Time) >= hook._fromDT.time())
    filters += _get_reservation_state_filter(hook._queryParams)
    occurs = [
        datetime.strptime(x, '%Y-%m-%d').date() for x in filter(
            None,
            get_query_parameter(hook._queryParams, ['occurs'], '').split(','))
    ]
    data = ['vc_equipment']
    if hook._occurrences:
        data.append('occurrences')
    order = {
        'start': Reservation.start_dt,
        'end': Reservation.end_dt
    }.get(hook._orderBy, Reservation.start_dt)
    if hook._descending:
        order = order.desc()
    reservations_data = Reservation.get_with_data(
        *data,
        filters=filters,
        limit=hook._limit,
        offset=hook._offset,
        order=order,
        limit_per_room=limit_per_room,
        occurs_on=occurs)
    for result in reservations_data:
        yield result['reservation'].room_id, _serializable_reservation(
            result, include_rooms)
    def approve(self, notify_blocker=True):
        """Approve the room blocking, rejecting all colliding reservations/occurrences."""
        self.state = BlockedRoomState.accepted

        # Get colliding reservations
        start_dt = datetime.combine(self.blocking.start_date, time())
        end_dt = datetime.combine(self.blocking.end_date, time(23, 59, 59))

        reservation_criteria = [
            Reservation.room_id == self.room_id, ~Reservation.is_rejected,
            ~Reservation.is_cancelled
        ]

        # Whole reservations to reject
        reservations = Reservation.find_all(Reservation.start_dt >= start_dt,
                                            Reservation.end_dt <= end_dt,
                                            *reservation_criteria)

        # Single occurrences to reject
        occurrences = ReservationOccurrence.find_all(
            ReservationOccurrence.start_dt >= start_dt,
            ReservationOccurrence.end_dt <= end_dt,
            ReservationOccurrence.is_valid,
            ~ReservationOccurrence.reservation_id.in_(
                map(attrgetter('id'), reservations)) if reservations else True,
            *reservation_criteria,
            _join=Reservation)

        reason = 'Conflict with blocking {}: {}'.format(
            self.blocking.id, self.blocking.reason)

        for reservation in reservations:
            if self.blocking.can_be_overridden(reservation.created_by_user,
                                               reservation.room):
                continue
            reservation.reject(self.blocking.created_by_user, reason)

        for occurrence in occurrences:
            reservation = occurrence.reservation
            if self.blocking.can_be_overridden(reservation.created_by_user,
                                               reservation.room):
                continue
            occurrence.reject(self.blocking.created_by_user, reason)

        if notify_blocker:
            # We only need to notify the blocking creator if the blocked room wasn't approved yet.
            # This is the case if it's a new blocking for a room managed by the creator
            notify_request_response(self)
Beispiel #13
0
 def api_roomBooking(self, user):
     data = MultiDict({
         'start_dt': self._params['from'],
         'end_dt': self._params['to'],
         'repeat_frequency': RepeatFrequency.NEVER,
         'repeat_interval': 0,
         'room_id': self._room.id,
         'booked_for_user': self._params['booked_for'],
         'contact_email': self._params['booked_for'].email,
         'contact_phone': self._params['booked_for'].phone,
         'booking_reason': self._params['reason']
     })
     try:
         reservation = Reservation.create_from_data(self._room, data, user)
     except ConflictingOccurrences:
         raise HTTPAPIError(
             'Failed to create the booking due to conflicts with other bookings'
         )
     except fossirError as e:
         raise HTTPAPIError('Failed to create the booking: {}'.format(e))
     db.session.add(reservation)
     db.session.flush()
     return {'reservationID': reservation.id}
 def _process_args(self):
     self._reservation = Reservation.get_one(request.view_args['resvID'])