"""Registers admin interfaces for the events module"""
from django.contrib import admin
from django.core.exceptions import PermissionDenied
from django.db.models import Max, Min
from django.forms import Field
from django.template.defaultfilters import date as _date
from django.urls import reverse, path
from django.utils import timezone
from django.utils.datetime_safe import date
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _
import events.admin_views as admin_views
from activemembers.models import MemberGroup
from events import services
from events.forms import RegistrationAdminForm
from members.models import Member
from payments.widgets import PaymentWidget
from pizzas.models import PizzaEvent
from utils.admin import DoNextTranslatedModelAdmin
from utils.snippets import datetime_to_lectureyear
from . import forms, models
[docs]class PizzaEventInline(admin.StackedInline):
"""The inline for pizza events in the Event admin"""
model = PizzaEvent
exclude = ("end_reminder",)
extra = 0
max_num = 1
[docs]class LectureYearFilter(admin.SimpleListFilter):
"""Filter the events on those started or ended in a lecture year"""
title = _("lecture year")
parameter_name = "lecture_year"
[docs] def lookups(self, request, model_admin):
objects_end = models.Event.objects.aggregate(Max("end"))
objects_start = models.Event.objects.aggregate(Min("start"))
if objects_end["end__max"] and objects_start["start__min"]:
year_end = datetime_to_lectureyear(objects_end["end__max"])
year_start = datetime_to_lectureyear(objects_start["start__min"])
return [
(year, "{}-{}".format(year, year + 1))
for year in range(year_end, year_start - 1, -1)
]
return []
[docs] def queryset(self, request, queryset):
if not self.value():
return queryset
year = int(self.value())
year_start = date(year=year, month=9, day=1)
year_end = date(year=year + 1, month=9, day=1)
return queryset.filter(start__gte=year_start, end__lte=year_end)
[docs]@admin.register(models.Event)
class EventAdmin(DoNextTranslatedModelAdmin):
"""Manage the events"""
inlines = (
RegistrationInformationFieldInline,
PizzaEventInline,
)
list_display = (
"overview_link",
"event_date",
"registration_date",
"num_participants",
"organiser",
"category",
"published",
"edit_link",
)
list_display_links = ("edit_link",)
list_filter = (LectureYearFilter, "start", "published", "category")
actions = ("make_published", "make_unpublished")
date_hierarchy = "start"
search_fields = ("title", "description")
prepopulated_fields = {"map_location": ("location",)}
filter_horizontal = ("documents",)
fieldsets = (
(_("General"), {"fields": ("title", "published", "organiser",)}),
(
_("Detail"),
{
"fields": (
"category",
"start",
"end",
"description",
"location",
"map_location",
),
"classes": ("collapse", "start-open"),
},
),
(
_("Registrations"),
{
"fields": (
"price",
"fine",
"max_participants",
"registration_start",
"registration_end",
"cancel_deadline",
"send_cancel_email",
"no_registration_message",
),
"classes": ("collapse",),
},
),
(_("Extra"), {"fields": ("slide", "documents"), "classes": ("collapse",)}),
)
[docs] def overview_link(self, obj):
return format_html(
'<a href="{link}">{title}</a>',
link=reverse("admin:events_event_details", kwargs={"pk": obj.pk}),
title=obj.title,
)
[docs] def has_change_permission(self, request, event=None):
"""Only allow access to the change form if the user is an organiser"""
if event is not None and not services.is_organiser(request.member, event):
return False
return super().has_change_permission(request, event)
[docs] def event_date(self, obj):
event_date = timezone.make_naive(obj.start)
return _date(event_date, "l d b Y, G:i")
event_date.short_description = _("Event Date")
event_date.admin_order_field = "start"
[docs] def registration_date(self, obj):
if obj.registration_start is not None:
start_date = timezone.make_naive(obj.registration_start)
else:
start_date = obj.registration_start
return _date(start_date, "l d b Y, G:i")
registration_date.short_description = _("Registration Start")
registration_date.admin_order_field = "registration_start"
[docs] def edit_link(self, obj):
return _("Edit")
edit_link.short_description = ""
[docs] def num_participants(self, obj):
"""Pretty-print the number of participants"""
num = obj.eventregistration_set.exclude(
date_cancelled__lt=timezone.now()
).count()
if not obj.max_participants:
return "{}/∞".format(num)
return "{}/{}".format(num, obj.max_participants)
num_participants.short_description = _("Number of participants")
[docs] def make_published(self, request, queryset):
"""Action to change the status of the event"""
self._change_published(request, queryset, True)
make_published.short_description = _("Publish selected events")
[docs] def make_unpublished(self, request, queryset):
"""Action to change the status of the event"""
self._change_published(request, queryset, False)
make_unpublished.short_description = _("Unpublish selected events")
@staticmethod
def _change_published(request, queryset, published):
if not request.user.is_superuser:
queryset = queryset.filter(organiser__in=request.member.get_member_groups())
queryset.update(published=published)
[docs] def get_actions(self, request):
actions = super().get_actions(request)
if "delete_selected" in actions:
del actions["delete_selected"]
return actions
[docs] def get_urls(self):
urls = super().get_urls()
custom_urls = [
path(
"<int:pk>/details/",
self.admin_site.admin_view(admin_views.EventAdminDetails.as_view()),
name="events_event_details",
),
path(
"<int:pk>/export/",
self.admin_site.admin_view(
admin_views.EventRegistrationsExport.as_view()
),
name="events_event_export",
),
path(
"<int:pk>/export-email/",
self.admin_site.admin_view(
admin_views.EventRegistrationEmailsExport.as_view()
),
name="events_event_export_email",
),
path(
"<int:pk>/message/",
self.admin_site.admin_view(
admin_views.EventMessage.as_view(admin=self)
),
name="events_event_message",
),
]
return custom_urls + urls
[docs]@admin.register(models.EventRegistration)
class RegistrationAdmin(DoNextTranslatedModelAdmin):
"""Custom admin for registrations"""
form = RegistrationAdminForm
[docs] def save_model(self, request, registration, form, change):
if not services.is_organiser(request.member, registration.event):
raise PermissionDenied
return super().save_model(request, registration, form, change)
[docs] def has_view_permission(self, request, registration=None):
"""Only give view permission if the user is an organiser"""
if registration is not None and not services.is_organiser(
request.member, registration.event
):
return False
return super().has_view_permission(request, registration)
[docs] def has_change_permission(self, request, registration=None):
"""Only give change permission if the user is an organiser"""
if registration is not None and not services.is_organiser(
request.member, registration.event
):
return False
return super().has_change_permission(request, registration)
[docs] def has_delete_permission(self, request, registration=None):
"""Only give delete permission if the user is an organiser"""
if registration is not None and not services.is_organiser(
request.member, registration.event
):
return False
return super().has_delete_permission(request, registration)
[docs] def get_urls(self):
urls = super().get_urls()
custom_urls = [
path(
"<int:registration>/fields/",
self.admin_site.admin_view(
admin_views.RegistrationAdminFields.as_view(admin=self)
),
name="events_registration_fields",
),
]
return custom_urls + urls