diff --git a/events/models.py b/events/models.py index 3334ca326..457e77aea 100644 --- a/events/models.py +++ b/events/models.py @@ -93,6 +93,16 @@ def for_datetime(self, dt=None): dt = convert_dt_to_aware(dt) return self.filter(Q(occurring_rule__dt_start__gt=dt) | Q(recurring_rules__finish__gt=dt)) + def ongoing_datetime(self, dt=None): + if dt is None: + dt = timezone.now() + else: + dt = convert_dt_to_aware(dt) + return self.filter( + (Q(occurring_rule__dt_start__lt=dt) & Q(occurring_rule__dt_end__gt=dt)) + | (Q(recurring_rules__begin__lt=dt) & Q(recurring_rules__finish__gt=dt)) + ) + def until_datetime(self, dt=None): if dt is None: dt = timezone.now() @@ -181,6 +191,39 @@ def next_time(self): except IndexError: return None + @property + def present_time(self): + """ + Return the OccurringRule or RecurringRule that are ongoing now. + """ + now = timezone.now() + recurring_start = occurring_start = None + + try: + occurring_rule = self.occurring_rule + except OccurringRule.DoesNotExist: + pass + else: + if occurring_rule and occurring_rule.dt_start < now and occurring_rule.dt_end > now: + occurring_start = (occurring_rule.dt_start, occurring_rule) + + rrules = self.recurring_rules.filter(begin__lt=now, finish__gt=now) + recurring_starts = [(rule.dt_start, rule) for rule in rrules if rule.dt_start is not None] + recurring_starts.sort(key=itemgetter(0)) + + try: + recurring_start = recurring_starts[0] + except IndexError: + pass + + starts = [i for i in (recurring_start, occurring_start) if i is not None] + starts.sort(key=itemgetter(0)) + + try: + return starts[0][1] + except IndexError: + return None + @property def previous_time(self): now = timezone.now() @@ -216,7 +259,7 @@ def next_or_previous_time(self): @property def is_past(self): - return self.next_time is None + return self.next_time is None and self.present_time is None class RuleMixin: diff --git a/events/tests/test_models.py b/events/tests/test_models.py index 0f3bafe76..855436408 100644 --- a/events/tests/test_models.py +++ b/events/tests/test_models.py @@ -47,6 +47,47 @@ def test_occurring_event(self): self.assertEqual(self.event.next_time, None) self.assertTrue(self.event.is_past) + def test_ongoing_occurring_event(self): + now = seconds_resolution(timezone.now()) + + # Starts before and ends after now + occurring_time_dtstart = now - datetime.timedelta(days=3) + occurring_time_dtend = occurring_time_dtstart + datetime.timedelta(days=5) + + ot = OccurringRule.objects.create( + event=self.event, + dt_start=occurring_time_dtstart, + dt_end=occurring_time_dtend, + ) + + self.assertEqual(Event.objects.ongoing_datetime().count(), 1) + self.assertIsNone(self.event.next_time) + self.assertIsNone(self.event.previous_time) + self.assertIsNotNone(self.event.present_time) + self.assertEqual(self.event.present_time.dt_start, occurring_time_dtstart) + self.assertEqual(self.event.present_time.dt_end, occurring_time_dtend) + self.assertFalse(self.event.is_past) + + # Starts before and ends before now + ot.dt_start = now - datetime.timedelta(days=5) + ot.dt_end = now - datetime.timedelta(days=3) + ot.save() + + event = Event.objects.get(pk=self.event.pk) + self.assertEqual(Event.objects.ongoing_datetime().count(), 0) + self.assertIsNone(event.present_time) + self.assertTrue(event.is_past) + + # Starts after and ends after now + ot.dt_start = now + datetime.timedelta(days=3) + ot.dt_end = now + datetime.timedelta(days=5) + ot.save() + + event = Event.objects.get(pk=self.event.pk) + self.assertEqual(Event.objects.ongoing_datetime().count(), 0) + self.assertIsNone(event.present_time) + self.assertFalse(event.is_past) + def test_recurring_event(self): now = seconds_resolution(timezone.now()) @@ -71,6 +112,43 @@ def test_recurring_event(self): self.assertEqual(event.next_time, None) self.assertEqual(Event.objects.for_datetime().count(), 0) + def test_ongoing_recurring_event(self): + now = seconds_resolution(timezone.now()) + + # Starts before and ends after now + recurring_time_dtstart = now - datetime.timedelta(days=3) + recurring_time_dtend = recurring_time_dtstart + datetime.timedelta(days=5) + + rt = RecurringRule.objects.create( + event=self.event, + begin=recurring_time_dtstart, + finish=recurring_time_dtend, + ) + self.assertEqual(Event.objects.ongoing_datetime().count(), 1) + self.assertGreater(self.event.present_time.dt_start, recurring_time_dtstart) + self.assertTrue(rt.valid_dt_end()) + self.assertFalse(self.event.is_past) + + # Starts before and ends before now + rt.begin = now - datetime.timedelta(days=5) + rt.finish = now - datetime.timedelta(days=3) + rt.save() + + event = Event.objects.get(pk=self.event.pk) + self.assertEqual(Event.objects.ongoing_datetime().count(), 0) + self.assertIsNone(event.present_time) + self.assertTrue(event.is_past) + + # Starts after and ends after now + rt.begin = now + datetime.timedelta(days=3) + rt.finish = now + datetime.timedelta(days=5) + rt.save() + + event = Event.objects.get(pk=self.event.pk) + self.assertEqual(Event.objects.ongoing_datetime().count(), 0) + self.assertIsNone(event.present_time) + self.assertFalse(event.is_past) + def test_rrule(self): now = seconds_resolution(timezone.now()) diff --git a/events/views.py b/events/views.py index 2490626e3..f84d7b4c4 100644 --- a/events/views.py +++ b/events/views.py @@ -72,7 +72,12 @@ def get_queryset(self): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['events_today'] = Event.objects.until_datetime(timezone.now()).filter(calendar__slug=self.kwargs['calendar_slug'])[:2] + context['events_today'] = Event.objects.until_datetime(timezone.now()).filter( + calendar__slug=self.kwargs['calendar_slug'] + )[:2] + context['events_ongoing'] = Event.objects.ongoing_datetime(timezone.now()).filter( + calendar__slug=self.kwargs['calendar_slug'] + ) context['calendar'] = get_object_or_404(Calendar, slug=self.kwargs['calendar_slug']) return context diff --git a/templates/events/event_list.html b/templates/events/event_list.html index bbfb764d2..b3e951a44 100644 --- a/templates/events/event_list.html +++ b/templates/events/event_list.html @@ -57,6 +57,26 @@