Show in-progress multi-day events on the calendar (#33)
Unit Tests / test (push) Successful in 5s
Unit Tests / test (push) Successful in 5s
## Summary Closes #32. - Keep an event on Upcoming until its end date is over. If `end_time` is set, it drops off once that time passes on the end date, so an in-progress multi-day event stays visible. - Render the Past tab from `past_events` instead of `future_events`. - Show the start–end date range when the dates differ. ## Test plan - [ ] Multi-day event that started yesterday and ends tomorrow appears under Upcoming - [ ] Event whose end time has already passed appears under Past - [ ] Future single-day event still appears under Upcoming - [ ] Past tab lists finished events only - [ ] `uv run python manage.py test schasite.tests.CalendarEventTests` Reviewed-on: #33
This commit was merged in pull request #33.
This commit is contained in:
1 parent
531d97f3fc
commit
783712972b
3 files changed
+81
-15
No files matched your search
+21
-6
@@ -77,17 +77,32 @@ class CalendarEvent(TimeInfoBase):
|
||||
def has_date(self):
|
||||
return not self.start_date is None
|
||||
|
||||
def _has_ended(self, now=None):
|
||||
"""True once the event is over. In-progress multi-day events stay open
|
||||
through end_date, and through end_time on that date when one is set.
|
||||
"""
|
||||
if not self.has_date():
|
||||
return None
|
||||
if now is None:
|
||||
now = datetime.datetime.now()
|
||||
|
||||
end_date = self.end_date or self.start_date
|
||||
if self.end_time:
|
||||
return now >= datetime.datetime.combine(end_date, self.end_time)
|
||||
return now.date() > end_date
|
||||
|
||||
def past_event(self):
|
||||
if self.has_date():
|
||||
return self.start_date <= datetime.datetime.now().date()
|
||||
else:
|
||||
ended = self._has_ended()
|
||||
if ended is None:
|
||||
return False
|
||||
return ended
|
||||
|
||||
def future_event(self):
|
||||
if self.has_date():
|
||||
return self.start_date > datetime.datetime.now().date()
|
||||
else:
|
||||
"""Upcoming or currently in progress."""
|
||||
ended = self._has_ended()
|
||||
if ended is None:
|
||||
return False
|
||||
return not ended
|
||||
|
||||
def no_date(self):
|
||||
return not self.has_date()
|
||||
|
||||
@@ -114,7 +114,7 @@
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="mb-3">
|
||||
<p class="mb-1"><i class="bi bi-calendar-event text-success me-2"></i><strong>Date:</strong> {{ event.start_date }}</p>
|
||||
<p class="mb-1"><i class="bi bi-calendar-event text-success me-2"></i><strong>Date:</strong> {% if event.end_date and event.end_date != event.start_date %}{{ event.start_date }} – {{ event.end_date }}{% else %}{{ event.start_date }}{% endif %}</p>
|
||||
{% if event.time_range_display %}
|
||||
<p class="mb-1"><i class="bi bi-clock text-success me-2"></i><strong>Time:</strong> {{ event.time_range_display }}</p>
|
||||
{% endif %}
|
||||
@@ -144,7 +144,7 @@
|
||||
<div class="tab-pane fade" id="past" role="tabpanel">
|
||||
<div class="row g-4">
|
||||
<!-- Past Event 1 -->
|
||||
{% for event in future_events %}
|
||||
{% for event in past_events %}
|
||||
<div class="col-lg-6">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<div class="card-header bg-secondary text-white">
|
||||
@@ -160,7 +160,7 @@
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="mb-3">
|
||||
<p class="mb-1"><i class="bi bi-calendar-event text-secondary me-2"></i><strong>Date:</strong> {{ event.start_date }}</p>
|
||||
<p class="mb-1"><i class="bi bi-calendar-event text-secondary me-2"></i><strong>Date:</strong> {% if event.end_date and event.end_date != event.start_date %}{{ event.start_date }} – {{ event.end_date }}{% else %}{{ event.start_date }}{% endif %}</p>
|
||||
{% if event.time_range_display %}
|
||||
<p class="mb-1"><i class="bi bi-clock text-secondary me-2"></i><strong>Time:</strong> {{ event.time_range_display }}</p>
|
||||
{% endif %}
|
||||
|
||||
+57
-6
@@ -326,6 +326,47 @@ class CalendarEventTests(TestCase):
|
||||
self.assertFalse(undated.past_event())
|
||||
self.assertFalse(undated.future_event())
|
||||
|
||||
def test_in_progress_multiday_event_counts_as_upcoming(self):
|
||||
today = datetime.date.today()
|
||||
ongoing = CalendarEvent.objects.create(
|
||||
event_name="Block Party",
|
||||
start_date=today - datetime.timedelta(days=1),
|
||||
end_date=today + datetime.timedelta(days=1),
|
||||
)
|
||||
ended = CalendarEvent.objects.create(
|
||||
event_name="Finished Sale",
|
||||
start_date=today - datetime.timedelta(days=4),
|
||||
end_date=today - datetime.timedelta(days=1),
|
||||
)
|
||||
|
||||
self.assertTrue(ongoing.future_event())
|
||||
self.assertFalse(ongoing.past_event())
|
||||
self.assertTrue(ended.past_event())
|
||||
self.assertFalse(ended.future_event())
|
||||
|
||||
def test_end_time_hides_event_only_after_it_finishes(self):
|
||||
now = datetime.datetime.now()
|
||||
still_going = now + datetime.timedelta(hours=2)
|
||||
already_over = now - datetime.timedelta(hours=1)
|
||||
|
||||
current = CalendarEvent.objects.create(
|
||||
event_name="Still Going",
|
||||
start_date=(now - datetime.timedelta(days=1)).date(),
|
||||
end_date=still_going.date(),
|
||||
end_time=still_going.time().replace(microsecond=0),
|
||||
)
|
||||
finished = CalendarEvent.objects.create(
|
||||
event_name="Already Over",
|
||||
start_date=(already_over - datetime.timedelta(days=1)).date(),
|
||||
end_date=already_over.date(),
|
||||
end_time=already_over.time().replace(microsecond=0),
|
||||
)
|
||||
|
||||
self.assertTrue(current.future_event())
|
||||
self.assertFalse(current.past_event())
|
||||
self.assertTrue(finished.past_event())
|
||||
self.assertFalse(finished.future_event())
|
||||
|
||||
def test_time_range_display(self):
|
||||
event = CalendarEvent.objects.create(
|
||||
event_name="Timed Event",
|
||||
@@ -346,23 +387,33 @@ class CalendarEventTests(TestCase):
|
||||
def test_calendar_page_splits_past_and_future(self):
|
||||
today = datetime.date.today()
|
||||
CalendarEvent.objects.create(
|
||||
event_name="Past Event",
|
||||
event_name="Archived Picnic",
|
||||
start_date=today - datetime.timedelta(days=3),
|
||||
)
|
||||
CalendarEvent.objects.create(
|
||||
event_name="Future Event",
|
||||
start_date=today + datetime.timedelta(days=3),
|
||||
)
|
||||
CalendarEvent.objects.create(
|
||||
event_name="Ongoing Event",
|
||||
start_date=today - datetime.timedelta(days=1),
|
||||
end_date=today + datetime.timedelta(days=1),
|
||||
)
|
||||
|
||||
response = self.client.get(reverse("calendar2"))
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.context["past_events"]), 1)
|
||||
self.assertEqual(len(response.context["future_events"]), 1)
|
||||
self.assertEqual(response.context["past_events"][0].event_name, "Past Event")
|
||||
self.assertEqual(
|
||||
response.context["future_events"][0].event_name, "Future Event"
|
||||
)
|
||||
self.assertEqual(len(response.context["future_events"]), 2)
|
||||
self.assertEqual(response.context["past_events"][0].event_name, "Archived Picnic")
|
||||
future_names = {event.event_name for event in response.context["future_events"]}
|
||||
self.assertEqual(future_names, {"Future Event", "Ongoing Event"})
|
||||
content = response.content.decode()
|
||||
upcoming_html, past_html = content.split('id="past" role="tabpanel"', 1)
|
||||
self.assertIn("Ongoing Event", upcoming_html)
|
||||
self.assertNotIn("Ongoing Event", past_html)
|
||||
self.assertIn("Archived Picnic", past_html)
|
||||
self.assertNotIn("Archived Picnic", upcoming_html)
|
||||
|
||||
|
||||
@override_settings(
|
||||
|
||||
Reference in new issue
Block a user