dnscms: improve events listing view
This commit is contained in:
@@ -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:
|
||||||
|
|||||||
@@ -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")
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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."""
|
||||||
|
|||||||
Reference in New Issue
Block a user