dnscms: improve events listing view

This commit is contained in:
2026-05-19 20:35:46 +02:00
parent 1073adacbb
commit 6a9fff8917
4 changed files with 150 additions and 11 deletions
+1 -11
View File
@@ -6,7 +6,6 @@ from wagtail import hooks
from wagtail.admin.menu import MenuItem from wagtail.admin.menu import MenuItem
from associations.models import AssociationIndex from associations.models import AssociationIndex
from events.models import EventIndex
from news.models import NewsIndex from news.models import NewsIndex
@@ -15,15 +14,6 @@ def enable_additional_rich_text_features(features):
features.default_features.extend(["h5", "h6", "blockquote"]) features.default_features.extend(["h5", "h6", "blockquote"])
@hooks.register("register_admin_menu_item")
def register_events_menu_item():
page = EventIndex.objects.first()
events_url = "#"
if page:
events_url = reverse("wagtailadmin_explore", args=(quote(page.pk),))
return MenuItem("Arrangementer", events_url, icon_name="date", order=1)
@hooks.register("register_admin_menu_item") @hooks.register("register_admin_menu_item")
def register_associations_menu_item(): def register_associations_menu_item():
page = AssociationIndex.objects.first() page = AssociationIndex.objects.first()
@@ -34,7 +24,7 @@ def register_associations_menu_item():
@hooks.register("register_admin_menu_item") @hooks.register("register_admin_menu_item")
def register_associations_menu_item(): def register_news_menu_item():
page = NewsIndex.objects.first() page = NewsIndex.objects.first()
news_url = "#" news_url = "#"
if page: if page:
+86
View File
@@ -0,0 +1,86 @@
from urllib.parse import urlencode
from django.urls import reverse
from django.utils import timezone
from wagtail.admin.ui.tables import Column, DateColumn
from wagtail.admin.ui.tables.pages import PageStatusColumn, PageTitleColumn
from wagtail.admin.views.pages.choose_parent import ChooseParentView
from wagtail.admin.views.pages.listing import IndexView
from wagtail.admin.viewsets.pages import PageListingViewSet
from events.models import EventPage
class EventDateColumn(Column):
def get_value(self, instance):
occurrences = list(instance.occurrences.order_by("start"))
if not occurrences:
return ""
if len(occurrences) == 1:
local = timezone.localtime(occurrences[0].start)
return local.strftime("%Y-%m-%d kl %H:%M")
return f"{len(occurrences)} forekomster"
class OrganizersColumn(Column):
def get_value(self, instance):
names = list(instance.organizers.values_list("name", flat=True))
if not names:
return ""
if len(names) == 1:
return names[0]
return f"{names[0]} (+{len(names) - 1})"
class EventPageIndexView(IndexView):
def annotate_queryset(self, pages):
pages = super().annotate_queryset(pages)
return pages.prefetch_related(
"occurrences",
"organizer_links__organizer",
)
class EventChooseParentView(ChooseParentView):
"""Redirect newly-created EventPages back to the events listing."""
def _with_next(self, response):
if response.status_code != 302:
return response
url = response["Location"]
sep = "&" if "?" in url else "?"
response["Location"] = f"{url}{sep}{urlencode({'next': reverse('events:index')})}"
return response
def get(self, request, *args, **kwargs):
return self._with_next(super().get(request, *args, **kwargs))
def form_valid(self, form):
return self._with_next(super().form_valid(form))
class EventPageListingViewSet(PageListingViewSet):
model = EventPage
index_view_class = EventPageIndexView
choose_parent_view_class = EventChooseParentView
icon = "date"
menu_label = "Arrangementer"
menu_order = 1
add_to_admin_menu = True
ordering = "-latest_revision_created_at"
columns = [
PageTitleColumn("title", label="Tittel", sort_key="title", classname="title"),
EventDateColumn("event_date", label="Dato", width="13%"),
OrganizersColumn("organizers", label="Arrangører", width="12%"),
DateColumn(
"latest_revision_created_at",
label="Oppdatert",
sort_key="latest_revision_created_at",
width="10%",
),
PageStatusColumn("status", label="Status", sort_key="live", width="10%"),
]
event_page_listing_viewset = EventPageListingViewSet("events")
+6
View File
@@ -1,8 +1,14 @@
from wagtail import hooks from wagtail import hooks
from .admin import event_page_listing_viewset
from .views import event_organizer_chooser_viewset from .views import event_organizer_chooser_viewset
@hooks.register("register_admin_viewset") @hooks.register("register_admin_viewset")
def register_viewset(): def register_viewset():
return event_organizer_chooser_viewset return event_organizer_chooser_viewset
@hooks.register("register_admin_viewset")
def register_event_page_listing_viewset():
return event_page_listing_viewset
+57
View File
@@ -4,6 +4,7 @@ import pytest
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils import timezone from django.utils import timezone
from events.admin import EventDateColumn, OrganizersColumn
from events.models import ( from events.models import (
EventCategory, EventCategory,
EventOccurrence, EventOccurrence,
@@ -212,6 +213,62 @@ def test_graphql_event_index_future_events_ordered_by_next_occurrence(event_inde
assert titles.index("Sooner gig") < titles.index("Later gig") assert titles.index("Sooner gig") < titles.index("Later gig")
def test_event_date_column_no_occurrences(event_index):
event = EventPageFactory(parent=event_index)
column = EventDateColumn("event_date")
assert column.get_value(event) == ""
def test_event_date_column_single_occurrence(event_index):
event = EventPageFactory(parent=event_index)
start = timezone.make_aware(datetime(2025, 7, 22, 19, 30))
EventOccurrence.objects.create(event=event, start=start, venue_custom="X")
column = EventDateColumn("event_date")
assert column.get_value(event) == "2025-07-22 kl 19:30"
def test_event_date_column_multiple_occurrences_shows_count(event_index):
event = EventPageFactory(parent=event_index)
now = timezone.now()
EventOccurrence.objects.create(event=event, start=now, venue_custom="X")
EventOccurrence.objects.create(event=event, start=now + timedelta(days=1), venue_custom="X")
EventOccurrence.objects.create(event=event, start=now + timedelta(days=2), venue_custom="X")
column = EventDateColumn("event_date")
assert column.get_value(event) == "3 forekomster"
def test_organizers_column_no_organizers(event_index):
event = EventPageFactory(parent=event_index)
column = OrganizersColumn("organizers")
assert column.get_value(event) == ""
def test_organizers_column_single_organizer_shows_name(event_index):
org = EventOrganizer.objects.create(name="Forening A", slug="forening-a")
event = EventPageFactory(parent=event_index)
EventOrganizerLink.objects.create(event=event, organizer=org)
column = OrganizersColumn("organizers")
assert column.get_value(event) == "Forening A"
def test_organizers_column_multiple_organizers_truncates_with_count(event_index):
org_a = EventOrganizer.objects.create(name="Forening A", slug="forening-a")
org_b = EventOrganizer.objects.create(name="Forening B", slug="forening-b")
org_c = EventOrganizer.objects.create(name="Forening C", slug="forening-c")
event = EventPageFactory(parent=event_index)
EventOrganizerLink.objects.create(event=event, organizer=org_a, sort_order=0)
EventOrganizerLink.objects.create(event=event, organizer=org_b, sort_order=1)
EventOrganizerLink.objects.create(event=event, organizer=org_c, sort_order=2)
column = OrganizersColumn("organizers")
assert column.get_value(event) == "Forening A (+2)"
@pytest.fixture @pytest.fixture
def comprehensive_event(event_index, venue, association_index): def comprehensive_event(event_index, venue, association_index):
"""A fully-populated paid EventPage exercising every field exposed via GraphQL.""" """A fully-populated paid EventPage exercising every field exposed via GraphQL."""