reservations: Make ReservationAdmin more useful for subclasses
This commit is contained in:
parent
927e2f4b90
commit
d792efc084
@ -1,5 +1,6 @@
|
||||
from django.contrib import admin
|
||||
from django.contrib.humanize.templatetags.humanize import naturaltime
|
||||
from django.http import HttpRequest
|
||||
from django.utils.html import format_html
|
||||
|
||||
from django_object_actions import (
|
||||
@ -223,3 +224,9 @@ class EventAdmin(DjangoObjectActions, admin.ModelAdmin):
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
return False
|
||||
|
||||
|
||||
@admin.register(EventMeetingTime)
|
||||
class EventMeetingTimeAdmin(admin.ModelAdmin):
|
||||
def has_module_permission(self, request: HttpRequest) -> bool:
|
||||
return False
|
||||
|
@ -626,6 +626,9 @@ class EventMeetingTime(Reservation):
|
||||
EventExt, on_delete=models.CASCADE, related_name="meeting_times"
|
||||
)
|
||||
|
||||
def get_title(self) -> str:
|
||||
return self.event.unescaped_title
|
||||
|
||||
# TODO: should probably do some validation in python to enforce
|
||||
# - uniqueness and non-overlapping (per event)
|
||||
# - min/max start/end time == event start end
|
||||
|
@ -1,6 +1,13 @@
|
||||
from django.contrib import admin
|
||||
from typing import Any
|
||||
|
||||
from .models import Reservation, Resource, UserReservation
|
||||
from django.contrib import admin
|
||||
from django.contrib.admin.views.main import ChangeList
|
||||
from django.db import models
|
||||
from django.db.models import QuerySet
|
||||
from django.http import HttpRequest
|
||||
from django.urls import reverse
|
||||
|
||||
from .models import ExternalReservation, Reservation, Resource, UserReservation
|
||||
|
||||
|
||||
@admin.register(Resource)
|
||||
@ -11,18 +18,83 @@ class ResourceAdmin(admin.ModelAdmin):
|
||||
show_facets = admin.ShowFacets.ALWAYS
|
||||
|
||||
|
||||
class ReservationTypeFilter(admin.SimpleListFilter):
|
||||
"""Filter by subclass"""
|
||||
|
||||
title = "Reservation Type"
|
||||
parameter_name = "type"
|
||||
|
||||
# TODO: this could be automatic
|
||||
_subtypes = {
|
||||
"eventmeetingtime": "Event Meeting Time",
|
||||
"userreservation": "User Reservation",
|
||||
"externalreservation": "External Reservation",
|
||||
}
|
||||
|
||||
def lookups(
|
||||
self, request: HttpRequest, model_admin: admin.ModelAdmin
|
||||
) -> list[tuple[str, str]]:
|
||||
return [
|
||||
("generic", "Generic Reservation"),
|
||||
] + list(self._subtypes.items())
|
||||
|
||||
def queryset(self, request: HttpRequest, queryset: QuerySet) -> QuerySet:
|
||||
if self.value() in self._subtypes:
|
||||
return queryset.filter(**{f"{self.value()}__isnull": False})
|
||||
elif self.value() == "generic":
|
||||
return queryset.filter(
|
||||
**{f"{subtype}__isnull": True for subtype in self._subtypes}
|
||||
)
|
||||
else:
|
||||
return queryset
|
||||
|
||||
|
||||
class SubclassChangeList(ChangeList):
|
||||
def url_for_result(self, result: models.Model) -> str:
|
||||
opts = type(result)._meta
|
||||
return reverse(
|
||||
f"admin:{opts.app_label}_{opts.model_name}_change",
|
||||
args=(result.pk,),
|
||||
current_app=self.model_admin.admin_site.name,
|
||||
)
|
||||
|
||||
|
||||
@admin.register(Reservation)
|
||||
class ReservationAdmin(admin.ModelAdmin):
|
||||
list_display = ["_resources", "start", "end"]
|
||||
list_display = ["_title", "_resources", "start", "end"]
|
||||
readonly_fields = ["google_calendar_event_id"]
|
||||
list_filter = ["resources"]
|
||||
list_filter = ["resources", ReservationTypeFilter]
|
||||
show_facets = admin.ShowFacets.ALWAYS
|
||||
date_hierarchy = "start"
|
||||
|
||||
def get_queryset(self, request):
|
||||
return (
|
||||
super()
|
||||
.get_queryset(request)
|
||||
.select_related("eventmeetingtime__event")
|
||||
.prefetch_related("resources")
|
||||
.select_subclasses()
|
||||
)
|
||||
|
||||
@admin.display()
|
||||
def _title(self, obj: Reservation):
|
||||
return obj.get_title()
|
||||
|
||||
@admin.display()
|
||||
def _resources(self, obj: Reservation):
|
||||
return list(obj.resources.all()) or None
|
||||
|
||||
def get_changelist(self, request: HttpRequest, **kwargs: Any) -> type[ChangeList]:
|
||||
return SubclassChangeList
|
||||
|
||||
|
||||
@admin.register(UserReservation)
|
||||
class UserReservationAdmin(ReservationAdmin):
|
||||
list_display = ["_resources", "user", "start", "end"]
|
||||
class UserReservationAdmin(admin.ModelAdmin):
|
||||
def has_module_permission(self, request: HttpRequest) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
@admin.register(ExternalReservation)
|
||||
class ExternalReservationAdmin(admin.ModelAdmin):
|
||||
def has_module_permission(self, request: HttpRequest) -> bool:
|
||||
return False
|
||||
|
@ -98,6 +98,9 @@ class Reservation(models.Model):
|
||||
resources = ", ".join(str(resource) for resource in self.resources.all())
|
||||
return f"{resources}: {self.start} - {self.end}"
|
||||
|
||||
def get_title(self) -> str:
|
||||
return "Unknown Reservation"
|
||||
|
||||
def make_google_calendar_event(self):
|
||||
event = {
|
||||
"summary": "CMSManage Reservation",
|
||||
@ -128,6 +131,9 @@ class UserReservation(Reservation):
|
||||
def __str__(self) -> str:
|
||||
return f"{self.user} | {super().__str__()}"
|
||||
|
||||
def get_title(self) -> str:
|
||||
return str(self.user)
|
||||
|
||||
def make_google_calendar_event(self):
|
||||
return super().make_google_calendar_event() | {
|
||||
"summary": str(self.user),
|
||||
@ -142,6 +148,9 @@ class ExternalReservation(Reservation):
|
||||
def __str__(self) -> str:
|
||||
return f'External "{self.title}" | {super().__str__()}'
|
||||
|
||||
def get_title(self) -> str:
|
||||
return str(self.title)
|
||||
|
||||
def make_google_calendar_event(self):
|
||||
"""This should never be called, as these are reservations from Google Calendar and shouldn't be synced back"""
|
||||
raise AttributeError(
|
||||
|
Loading…
Reference in New Issue
Block a user