Пример #1
0
    def test_pre_post_fill(self):
        """
        Tests whether the filling algorithm correctly fills a typical
        timeslot query in which both ends of the range require
        pre or post-filling.

        """

        hour = timedelta(hours=1)

        filled = filler.fill(
            self.timeslots,
            self.timeslots[0].range_start() - hour,
            self.timeslots[-1].range_end() + hour
        )

        self.general_fill_tests(filled)
        self.assertNotEqual(
            self.timeslots[0],
            filled[0],
            'Filler mistakenly did not fill before first show.'
        )
        self.assertNotEqual(
            self.timeslots[-1],
            filled[-1],
            'Filler mistakenly did not fill after first show.'
        )
Пример #2
0
    def test_normal_fill(self):
        """
        Tests whether the filling algorithm correctly fills a typical
        timeslot query in which neither end of the range requires
        pre or post-filling.

        """

        filled = filler.fill(
            self.timeslots,
            self.timeslots[0].range_start(),
            self.timeslots[-1].range_end()
        )

        self.general_fill_tests(filled)
        self.assertIs(
            self.timeslots[0],
            filled[0],
            'Filler mistakenly filled before first show.'
        )
        self.assertIs(
            self.timeslots[-1],
            filled[-1],
            'Filler mistakenly filled after first show.'
        )
Пример #3
0
    def test_normal_fill(self):
        """
        Tests whether an attempt to fill an empty list returns a
        single filler slot spanning the entire requested range.

        """
        # First, some sanity checks on the test fixture
        self.assertIsNotNone(Term.of(self.past_time))
        self.assertIsNotNone(self.past_time)
        self.assertIsNotNone(self.future_time)
        self.assertIsNotNone(self.duration)

        filled = filler.fill(
            self.timeslots,
            self.past_time,
            self.future_time
        )
        # This should only have filled with one item
        self.assertEqual(len(filled), 1)

        filler_slot = filled[0]
        # ...which should be a Timeslot...
        self.assertIsInstance(filler_slot, Timeslot)
        # ... and should take up the entire required range.
        # The times might be out because filler slots try to set
        # their start/ends to end/starts of adjacent shows.
        self.assertTrue(filler_slot.start_time <= self.past_time)
        self.assertTrue(filler_slot.end_time >= self.future_time)
Пример #4
0
    def test_negative_fill(self):
        """
        Tests whether an attempt to fill an empty list whose
        start time is after its end time results in an exception.

        """
        # First, some sanity checks on the test fixture
        self.assertIsNotNone(Term.of(self.past_time))
        self.assertIsNotNone(self.past_time)
        self.assertIsNotNone(self.future_time)
        self.assertIsNotNone(self.duration)

        with self.assertRaises(ValueError):
            filler.fill(
                self.timeslots,
                self.future_time,
                self.past_time
            )
Пример #5
0
    def test_normal_fill(self):
        """Tests whether an attempt to fill an empty list returns a
        single filler slot spanning the entire requested range.

        """
        filled = filler.fill(
            self.timeslots,
            self.past_time,
            self.future_time
        )
        self.AssertTrue(len(filled) == 1)
        
        filler_slot = filled[0]
        self.assertTrue(isinstance(filler_slot, Timeslot))
        self.assertEqual(filler_slot.start_time, self.past_time)
        self.assertEqual(filler_slot.duration, self.duration)
Пример #6
0
def coming_up(date=None, quantity=10, with_filler_timeslots=True):
    """Retrieves the next 'quantity' timeslots, relative to 'date'.

    If there are not enough timeslots to make up 'quantity', the
    resulting list may be smaller.

    If 'with_filler_timeslots' is True, these are included in the
    total.

    Keyword arguments:
    date -- the reference point from which the list is made; the
        first listed timeslot is the timeslot active at the moment
        of time 'date' refers to (default: now)
    quantity -- the maximum amount of slots to retrieve, as a
        positive integer (default: 10)
    with_filler_timeslots -- if True, non-contiguous entries in the
        list will cause the resulting gap to be filled in with a
        filler timeslot; filler timeslots count towards 'quantity'
        (default: True)
    """
    if quantity <= 0:
        raise ValueError("'quantity' must be positive.")
    if date is None:
        date = timezone.now()

    query = Timeslot.objects.filter(
        duration__gte=date - F('start_time')).order_by('start_time')
    coming_up_unfilled = \
        list(query)[:min(query.count(), quantity)] \
        if query.exists() else []

    if with_filler_timeslots:
        end = date if not coming_up_unfilled else \
            coming_up_unfilled[-1]
        filled_up = filler.fill(
            coming_up_unfilled,
            date,
            end)
        # Filling might have added some more slots, so we need to
        # re-trim
        coming_up = filled_up[:min(len(filled_up), quantity)]
    else:
        coming_up = coming_up_unfilled
    return coming_up
Пример #7
0
    def between(cls,
                start,
                end,
                exclude_before_start=False,
                exclude_after_end=False,
                exclude_subsuming=False,
                with_filler_timeslots=True):
        """Returns all the timeslots within a range defined by two
        datetime objects.

        Keyword arguments:
        start -- the start of the range, as a datetime
        end -- the end of the range, as a datetime
        exclude_before_start -- if True, the list will exclude all
            shows that start before the range, but end within it
            (default: False)
        exclude_after_end -- if True, the list will exclude all shows
            that start within the range, but end after it
            (default: False)
        exclude_subsuming -- if True, the list will exclude all shows
            that start before, but end after, the range (that is,
            they "subsume" the range)
            (default: False)
        with_filler_timeslots -- if True, gaps within the range will be
            filled with references to the filler pseudo-show
            (default: True)

        """
        # THIS IS NOT A TRIVIAL FUNCTION!

        # Start with ALL the timeslots (Django doesn't execute
        # database queries immediately so this is perfectly fine,
        # we'll be whittling this query down soon!
        timeslots = Timeslot.objects.all()

        # ADVICE: Whenever you see an inequality on duration, just
        # mentally move the subtraction of 'start_time' over to
        # an addition on the other end, and replace duration +
        # start_time with end_time.  That should make sense hopefully

        # (this is because the model doesn't store end times in the
        # database)

        # Get rid of shows that start and end before the range
        # (diagrammatically, ##|  |  )
        timeslots = timeslots.exclude(
            start_time__lt=start,
            duration__lte=start - F('start_time')
        )
        # And start and end after the range
        # (diagrammatically,   |  |##)
        timeslots = timeslots.exclude(
            start_time__gte=end,
            duration__gt=end - F('start_time')
        )

        # This leaves:
        #   1) Shows that start and end inside the range
        #      - these will always be returned
        #        (diagrammatically,   |##|  )
        #   2) Shows that start before but end inside the range
        #      - these will be returned if exclude_before_start=False
        #        (diagrammatically, ##|##|  )
        #   3) Shows that start inside but end after the range
        #      - these will be returned if exclude_after_end=False
        #        (diagrammatically,   |##|##)
        #   4) Shows that completely subsume the range
        #      - these will be returned if exclude_subsuming=False
        #        (diagrammatically, ##|##|##)
        if exclude_before_start:  # 1)
            timeslots = timeslots.exclude(
                start_time__lt=start,
                duration__lte=end - F('start_time'))

        if exclude_after_end:  # 2)
            timeslots = timeslots.exclude(
                start_time__gte=start,
                duration__gt=end - F('start_time'))

        if exclude_subsuming:  # 3)
            timeslots = timeslots.exclude(
                start_time__lt=start,
                duration__gt=end - F('start_time'))

        # Of course, we want some form of ordering
        timeslots = timeslots.order_by("start_time")

        # And jukebox filling
        if with_filler_timeslots:
            # Can't do add_filler_timeslots if this is a queryset
            # so force it to be a list
            timeslots = filler.fill(
                list(timeslots),
                start,
                end)

        # For all intents and purposes, this is just the queryset
        # with some added metadata on which range it actually
        # represents
        return cls(
            timeslots,
            start,
            end,
            exclude_before_start,
            exclude_after_end,
            exclude_subsuming,
            with_filler_timeslots)