diff --git a/membershipworks/dashboard.py b/membershipworks/dashboard.py new file mode 100644 index 0000000..22d8850 --- /dev/null +++ b/membershipworks/dashboard.py @@ -0,0 +1,29 @@ +from typing import Any +from datetime import datetime + +from django.urls import reverse + +import dashboard + + +@dashboard.register +class MembershipworksDashboardFragment(dashboard.DashboardFragment): + name = "MembershipWorks" + template = "dashboard/links_card.dj.html" + + @property + def context(self) -> Any: + links = {} + + if self.request.user.has_perm("membershipworks.view_event"): + now = datetime.now() + links["Event Report"] = reverse( + "membershipworks:event-report", + kwargs={"year": now.year, "month": now.month}, + ) + + return {"links": links} + + @property + def visible(self) -> bool: + return self.request.user.has_perm("doorcontrol.view_hidevent") diff --git a/membershipworks/models.py b/membershipworks/models.py index d0aaeda..db02c97 100644 --- a/membershipworks/models.py +++ b/membershipworks/models.py @@ -436,6 +436,14 @@ class EventExt(Event): max_digits=13, decimal_places=4, default=0 ) + # TODO: ideally this would be a generated column or annotation, + # but I couldn't get the types to work out + @property + def person_hours(self): + if self.duration is None: + return None + return self.count * self.duration + class Meta: verbose_name = "event" diff --git a/membershipworks/templates/membershipworks/event_report.dj.html b/membershipworks/templates/membershipworks/event_report.dj.html new file mode 100644 index 0000000..6324a2c --- /dev/null +++ b/membershipworks/templates/membershipworks/event_report.dj.html @@ -0,0 +1,66 @@ +{% extends "base.dj.html" %} + +{% load membershipworks_tags %} + +{% block title %}Event Report{% endblock %} +{% block content %} +
+ + + + + + + + + + + + + + + + {% for event in object_list %} + + + + + + + + + + + + {% endfor %} + +
TitleDateInstructorCategoryTicket CountTicket CapMeetingsTotal DurationPerson Hours
+ {{ event.title }} + {{ event.start|date }}{{ event.instructor }}{{ event.category }}{{ event.count }}{{ event.cap }}{{ event.meeting_times.count }}{{ event.duration|duration_as_hours }}{{ event.person_hours|duration_as_hours }}
+
+ +{% endblock %} diff --git a/membershipworks/templatetags/__init__.py b/membershipworks/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/membershipworks/templatetags/membershipworks_tags.py b/membershipworks/templatetags/membershipworks_tags.py new file mode 100644 index 0000000..454e000 --- /dev/null +++ b/membershipworks/templatetags/membershipworks_tags.py @@ -0,0 +1,12 @@ +from datetime import timedelta + +from django import template + +register = template.Library() + + +@register.filter +def duration_as_hours(td: timedelta) -> str | None: + if td is None: + return None + return td.total_seconds() / 60 / 60 diff --git a/membershipworks/urls.py b/membershipworks/urls.py index f36170d..71f7400 100644 --- a/membershipworks/urls.py +++ b/membershipworks/urls.py @@ -1,6 +1,6 @@ from django.urls import path -from .views import MemberAutocomplete, upcoming_events +from .views import MemberAutocomplete, upcoming_events, EventReport app_name = "membershipworks" @@ -15,4 +15,9 @@ urlpatterns = [ upcoming_events, name="upcoming-events", ), + path( + "event-report///", + EventReport.as_view(month_format="%m"), + name="event-report", + ), ] diff --git a/membershipworks/views.py b/membershipworks/views.py index 645e411..4a36fe0 100644 --- a/membershipworks/views.py +++ b/membershipworks/views.py @@ -3,12 +3,13 @@ from datetime import datetime from django.conf import settings from django.contrib import messages from django.contrib.auth.decorators import login_required -from django.http import HttpResponse +from django.contrib.auth.mixins import PermissionRequiredMixin from django.shortcuts import render +from django.views.generic.dates import MonthArchiveView from dal import autocomplete -from .models import Member +from .models import Member, EventExt from membershipworks import MembershipWorks @@ -101,3 +102,10 @@ def upcoming_events(request): ] } return render(request, "membershipworks/upcoming_events.dj.html", context) + + +class EventReport(PermissionRequiredMixin, MonthArchiveView): + permission_required = "membershipworks.view_eventext" + queryset = EventExt.objects.select_related("category", "instructor").all() + date_field = "start" + template_name = "membershipworks/event_report.dj.html"