def test_align_dates(self):
        self.assertEqual(utils.align_date_to_day(datetime(2012, 1, 1, 0, 0), "down"), datetime(2012, 1, 1, 0, 0))

        self.assertEqual(
            utils.align_date_to_day(datetime(2012, 1, 1, 0, 0), "up"), datetime(2012, 1, 1, 23, 59, 59, 999999)
        )

        self.assertEqual(utils.align_date_to_day(datetime(2012, 1, 1, 0, 1), "down"), datetime(2012, 1, 1, 0, 0))

        self.assertEqual(
            utils.align_date_to_day(datetime(2012, 1, 1, 0, 1), "up"), datetime(2012, 1, 1, 23, 59, 59, 999999)
        )

        self.assertEqual(
            utils.align_date_to_day(datetime(2012, 1, 1, 23, 59, 59, 999999), "down"), datetime(2012, 1, 1, 0, 0)
        )

        self.assertEqual(
            utils.align_date_to_day(datetime(2012, 1, 1, 23, 59, 59, 999999), "up"),
            datetime(2012, 1, 1, 23, 59, 59, 999999),
        )

        self.assertEqual(
            utils.align_range_to_day(datetime(2012, 1, 2, 0, 0), datetime(2012, 1, 2, 0, 0)),
            (datetime(2012, 1, 2, 0, 0), datetime(2012, 1, 2, 23, 59, 59, 999999)),
        )

        self.assertEqual(
            utils.align_range_to_day(datetime(2012, 1, 2, 0, 0), datetime(2012, 1, 3, 0, 0)),
            (datetime(2012, 1, 2, 0, 0), datetime(2012, 1, 3, 23, 59, 59, 999999)),
        )
示例#2
0
    def reserve(self, data):

        allocation = self.allocation(data['id'])
        approve_manually = allocation.approve_manually

        self.inject_missing_data(data, allocation)

        start, end = self.validate(data)
        quota = int(data.get('quota', 1))

        # whole day allocations don't show the start / end time which is to
        # say the data arrives with 00:00 - 00:00. we align that to the day
        if allocation.whole_day:
            assert start == end
            start, end = utils.align_range_to_day(start, end)

        def reserve():
            self.run_reserve(
                data=data, approve_manually=approve_manually,
                start=start, end=end, quota=quota
            )

        utils.handle_action(
            action=reserve, success=self.redirect_to_your_reservations
        )
示例#3
0
    def reserve(self, data):

        allocation = self.allocation(data['id'])
        approve_manually = allocation.approve_manually

        self.inject_missing_data(data, allocation)

        start, end = self.validate(data)
        quota = int(data.get('quota', 1))

        # whole day allocations don't show the start / end time which is to
        # say the data arrives with 00:00 - 00:00. we align that to the day
        if allocation.whole_day:

            if not allocation.partly_available:
                assert start == end

            if start == end:
                start, end = utils.align_range_to_day(start, end)

        def reserve():
            self.run_reserve(data=data,
                             approve_manually=approve_manually,
                             start=start,
                             end=end,
                             quota=quota)

        utils.handle_action(action=reserve,
                            success=self.redirect_to_your_reservations)
示例#4
0
    def test_align_dates(self):
        self.assertEqual(
            utils.align_date_to_day(datetime(2012, 1, 1, 0, 0), 'down'),
            datetime(2012, 1, 1, 0, 0))

        self.assertEqual(
            utils.align_date_to_day(datetime(2012, 1, 1, 0, 0), 'up'),
            datetime(2012, 1, 1, 23, 59, 59, 999999))

        self.assertEqual(
            utils.align_date_to_day(datetime(2012, 1, 1, 0, 1), 'down'),
            datetime(2012, 1, 1, 0, 0))

        self.assertEqual(
            utils.align_date_to_day(datetime(2012, 1, 1, 0, 1), 'up'),
            datetime(2012, 1, 1, 23, 59, 59, 999999))

        self.assertEqual(
            utils.align_date_to_day(datetime(2012, 1, 1, 23, 59, 59, 999999),
                                    'down'), datetime(2012, 1, 1, 0, 0))

        self.assertEqual(
            utils.align_date_to_day(datetime(2012, 1, 1, 23, 59, 59, 999999),
                                    'up'),
            datetime(2012, 1, 1, 23, 59, 59, 999999))

        self.assertEqual(
            utils.align_range_to_day(datetime(2012, 1, 2, 0, 0),
                                     datetime(2012, 1, 2, 0, 0)),
            (datetime(2012, 1, 2, 0,
                      0), datetime(2012, 1, 2, 23, 59, 59, 999999)))

        self.assertEqual(
            utils.align_range_to_day(datetime(2012, 1, 2, 0, 0),
                                     datetime(2012, 1, 3, 0, 0)),
            (datetime(2012, 1, 2, 0,
                      0), datetime(2012, 1, 3, 23, 59, 59, 999999)))
示例#5
0
    def move_allocation(
            self, master_id, new_start=None, new_end=None,
            group=None, new_quota=None, approve_manually=None,
            reservation_quota_limit=0, whole_day=None):

        assert master_id
        assert any([new_start and new_end, group, new_quota])

        # Find allocation
        master = self.allocation_by_id(master_id)
        mirrors = self.allocation_mirrors_by_master(master)
        changing = [master] + mirrors
        ids = [c.id for c in changing]

        assert(group or master.group)

        # Simulate the new allocation
        new_start = new_start or master.start
        new_end = new_end or master.end

        if whole_day:
            new_start, new_end = utils.align_range_to_day(new_start, new_end)

        new = Allocation(start=new_start, end=new_end, raster=master.raster)

        # Ensure that the new span does not overlap an existing one
        existing_allocations = self.allocations_in_range(new.start, new.end)

        for existing in existing_allocations:
            if existing.id not in ids:
                raise OverlappingAllocationError(new.start, new.end, existing)

        for change in changing:

            if change.partly_available:
                # confirmed reservations
                for reservation in change.reserved_slots:
                    if not new.contains(reservation.start, reservation.end):
                        raise AffectedReservationError(reservation)

                # pending reservations
                if change.is_master:  # (mirrors return the same values)
                    for pending in change.pending_reservations.with_entities(
                            Reservation.start, Reservation.end):
                        if not new.contains(*pending):
                            raise AffectedPendingReservationError(pending)

            else:
                # confirmed reservations
                if change.start != new.start or change.end != new.end:
                    if len(change.reserved_slots):
                        raise AffectedReservationError(
                            change.reserved_slots[0]
                        )

                    if change.is_master and \
                            change.pending_reservations.count():
                        raise AffectedPendingReservationError(
                            change.pending_reservations[0]
                        )

        # the following attributes must be equal over all group members
        # (this still allows to use move_allocation to remove an allocation
        #  from an existing group by specifiying the new group)
        for allocation in self.allocations_by_group(group or master.group):

            if approve_manually is not None:
                allocation.approve_manually = approve_manually

            if reservation_quota_limit is not None:
                allocation.reservation_quota_limit = reservation_quota_limit

            if new_quota is not None and allocation.is_master:
                self.change_quota(allocation, new_quota)

        for change in changing:
            change.start = new.start
            change.end = new.end
            change.group = group or master.group
示例#6
0
    def allocate(self, dates, raster=15, quota=None, partly_available=False,
                 grouped=False, approve_manually=True,
                 reservation_quota_limit=0, whole_day=False
                 ):
        """Allocates a spot in the calendar.

        An allocation defines a timerange which can be reserved. No
        reservations can exist outside of existing allocations. In fact any
        reserved slot will link to an allocation.

        An allocation may be available as a whole (to reserve all or nothing).
        It may also be partly available which means reservations can be made
        for parts of the allocation.

        If an allocation is partly available a raster defines the granularity
        with which a reservation can be made (e.g. a raster of 15min will
        ensure that reservations are at least 15 minutes long and start either
        at :00, :15, :30 or :45)

        The reason for the raster is mainly to ensure that different
        reservations trying to reserve overlapping times need the same keys in
        the reserved_slots table, ensuring integrity at the database level.

        Allocations may have a quota, which determines how many times an
        allocation may be reserved. Quotas are enabled using a master-mirrors
        relationship.

        The master is the first allocation to be created. The mirrors copies of
        that allocation. See Scheduler.__doc__

        """
        dates = utils.pairs(dates)

        group = new_uuid()
        quota = quota or 1

        # if the allocation is not partly available the raster is set to lowest
        # possible raster value
        raster = partly_available and raster or MIN_RASTER_VALUE

        # the whole day option results in the dates being aligned to
        # the beginning of the day / end of it -> not timezone aware!
        if whole_day:
            for ix, (start, end) in enumerate(dates):
                dates[ix] = utils.align_range_to_day(start, end)

        # Ensure that the list of dates contains no overlaps inside
        for start, end in dates:
            if utils.count_overlaps(dates, start, end) > 1:
                raise InvalidAllocationError

        # Make sure that this span does not overlap another master
        for start, end in dates:
            start, end = rasterize_span(start, end, raster)

            existing = self.allocations_in_range(start, end).first()
            if existing:
                raise OverlappingAllocationError(start, end, existing)

        # Write the master allocations
        allocations = []
        for start, end in dates:
            allocation = Allocation(raster=raster)
            allocation.start = start
            allocation.end = end
            allocation.resource = self.uuid
            allocation.quota = quota
            allocation.mirror_of = self.uuid
            allocation.partly_available = partly_available
            allocation.approve_manually = approve_manually
            allocation.reservation_quota_limit = reservation_quota_limit

            if grouped:
                allocation.group = group
            else:
                allocation.group = new_uuid()

            allocations.append(allocation)

        Session.add_all(allocations)

        return allocations