membershipworks: Add new event attendee email report
All checks were successful
Ruff / ruff (push) Successful in 20s
All checks were successful
Ruff / ruff (push) Successful in 20s
This commit is contained in:
parent
c3f51df7d4
commit
560225cdb3
@ -16,6 +16,7 @@ class MembershipworksDashboardFragment(dashboard.DashboardFragment):
|
|||||||
|
|
||||||
if self.request.user.has_perm("membershipworks.view_event"):
|
if self.request.user.has_perm("membershipworks.view_event"):
|
||||||
links["Event Report"] = reverse("membershipworks:event-index-report")
|
links["Event Report"] = reverse("membershipworks:event-index-report")
|
||||||
|
links["Event Attendees"] = reverse("membershipworks:event-attendees")
|
||||||
|
|
||||||
return {"links": links}
|
return {"links": links}
|
||||||
|
|
||||||
|
26
membershipworks/migrations/0013_eventattendee.py
Normal file
26
membershipworks/migrations/0013_eventattendee.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-02-02 22:07
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
import django_db_views.migration_functions
|
||||||
|
import django_db_views.operations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("membershipworks", "0012_eventattendeestats_eventtickettype"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
django_db_views.operations.ViewRunPython(
|
||||||
|
code=django_db_views.migration_functions.ForwardViewMigration(
|
||||||
|
"SELECT eventext.event_ptr_id as event_id, tkt.uid, tkt.name, tkt.email, tkt.sum\n FROM\n membershipworks_eventext as eventext,\n JSON_TABLE(eventext.details, '$.usr[*]' COLUMNS (\n uid VARCHAR(24) PATH '$.uid',\n name VARCHAR(256) PATH '$.nam',\n email VARCHAR(256) PATH '$.eml',\n sum DOUBLE PATH '$.sum'\n )) as tkt",
|
||||||
|
"membershipworks_eventattendee",
|
||||||
|
engine="django.db.backends.mysql",
|
||||||
|
),
|
||||||
|
reverse_code=django_db_views.migration_functions.BackwardViewMigration(
|
||||||
|
"", "membershipworks_eventattendee", engine="django.db.backends.mysql"
|
||||||
|
),
|
||||||
|
atomic=False,
|
||||||
|
),
|
||||||
|
]
|
@ -688,3 +688,28 @@ class EventAttendeeStats(DBView):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
managed = False
|
managed = False
|
||||||
|
|
||||||
|
|
||||||
|
class EventAttendee(DBView):
|
||||||
|
event = models.ForeignKey(
|
||||||
|
EventExt, on_delete=models.CASCADE, related_name="attendees"
|
||||||
|
)
|
||||||
|
uid = models.ForeignKey(Member, on_delete=models.DO_NOTHING)
|
||||||
|
name = models.CharField(max_length=256)
|
||||||
|
email = models.CharField(max_length=256)
|
||||||
|
sum = models.FloatField()
|
||||||
|
|
||||||
|
view_definition = """
|
||||||
|
SELECT eventext.event_ptr_id as event_id, tkt.uid, tkt.name, tkt.email, tkt.sum
|
||||||
|
FROM
|
||||||
|
membershipworks_eventext as eventext,
|
||||||
|
JSON_TABLE(eventext.details, '$.usr[*]' COLUMNS (
|
||||||
|
uid VARCHAR(24) PATH '$.uid',
|
||||||
|
name VARCHAR(256) PATH '$.nam',
|
||||||
|
email VARCHAR(256) PATH '$.eml',
|
||||||
|
sum DOUBLE PATH '$.sum'
|
||||||
|
)) as tkt
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
managed = False
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
{% extends "base.dj.html" %}
|
||||||
|
|
||||||
|
{% load render_table from django_tables2 %}
|
||||||
|
|
||||||
|
{% block title %}Event Attendees{% endblock %}
|
||||||
|
{% block admin_link %}
|
||||||
|
{% url 'admin:membershipworks_eventext_changelist' %}
|
||||||
|
{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<form method="get" class="container-fluid">
|
||||||
|
<div class="row g-2 align-items-center">
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="date"
|
||||||
|
class="form-control"
|
||||||
|
id="newSince"
|
||||||
|
name="new_since"
|
||||||
|
value="{{ filter.form.new_since.value }}">
|
||||||
|
<label for="newSince">New Since</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary col-auto" type="submit">Filter</button>
|
||||||
|
<div class="col-auto">{% include "cmsmanage/components/download_table.dj.html" %}</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% render_table table %}
|
||||||
|
{% endblock %}
|
@ -1,6 +1,7 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from .views import (
|
from .views import (
|
||||||
|
EventAttendeeListView,
|
||||||
EventIndexReport,
|
EventIndexReport,
|
||||||
EventInvoiceView,
|
EventInvoiceView,
|
||||||
EventMonthReport,
|
EventMonthReport,
|
||||||
@ -42,4 +43,9 @@ urlpatterns = [
|
|||||||
EventInvoiceView.as_view(),
|
EventInvoiceView.as_view(),
|
||||||
name="event-invoice",
|
name="event-invoice",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"event-attendees",
|
||||||
|
EventAttendeeListView.as_view(),
|
||||||
|
name="event-attendees",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
@ -4,24 +4,27 @@ from django.conf import settings
|
|||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import permission_required
|
from django.contrib.auth.decorators import permission_required
|
||||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||||
|
from django.db.models import Subquery
|
||||||
from django.db.models.functions import TruncMonth, TruncYear
|
from django.db.models.functions import TruncMonth, TruncYear
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.template.defaultfilters import floatformat
|
from django.template.defaultfilters import floatformat
|
||||||
from django.views.generic import DetailView
|
from django.views.generic import DetailView, ListView
|
||||||
from django.views.generic.dates import (
|
from django.views.generic.dates import (
|
||||||
ArchiveIndexView,
|
ArchiveIndexView,
|
||||||
MonthArchiveView,
|
MonthArchiveView,
|
||||||
YearArchiveView,
|
YearArchiveView,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import django_filters
|
||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
from dal import autocomplete
|
from dal import autocomplete
|
||||||
|
from django_filters.views import BaseFilterView
|
||||||
from django_tables2 import A, SingleTableMixin
|
from django_tables2 import A, SingleTableMixin
|
||||||
from django_tables2.export.views import ExportMixin
|
from django_tables2.export.views import ExportMixin
|
||||||
|
|
||||||
from membershipworks.membershipworks_api import MembershipWorks
|
from membershipworks.membershipworks_api import MembershipWorks
|
||||||
|
|
||||||
from .models import EventExt, Member
|
from .models import EventAttendee, EventExt, Member
|
||||||
|
|
||||||
|
|
||||||
class MemberAutocomplete(autocomplete.Select2QuerySetView):
|
class MemberAutocomplete(autocomplete.Select2QuerySetView):
|
||||||
@ -329,3 +332,36 @@ class EventInvoiceView(SingleTableMixin, PermissionRequiredMixin, DetailView):
|
|||||||
|
|
||||||
def get_table_kwargs(self):
|
def get_table_kwargs(self):
|
||||||
return {"event": self.object}
|
return {"event": self.object}
|
||||||
|
|
||||||
|
|
||||||
|
class EventAttendeeTable(tables.Table):
|
||||||
|
class Meta:
|
||||||
|
model = EventAttendee
|
||||||
|
fields = ("name", "email")
|
||||||
|
|
||||||
|
|
||||||
|
class EventAttendeeFilters(django_filters.FilterSet):
|
||||||
|
new_since = django_filters.DateFilter(
|
||||||
|
field_name="event__start", method="filter_new_since"
|
||||||
|
)
|
||||||
|
|
||||||
|
def filter_new_since(self, queryset, name, value):
|
||||||
|
return queryset.filter(**{f"{name}__gte": value}).exclude(
|
||||||
|
email__in=Subquery(
|
||||||
|
queryset.filter(**{f"{name}__lt": value}).values("email")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class EventAttendeeListView(
|
||||||
|
BaseFilterView, ExportMixin, SingleTableMixin, PermissionRequiredMixin, ListView
|
||||||
|
):
|
||||||
|
permission_required = "membershipworks.view_eventext"
|
||||||
|
queryset = EventAttendee.objects.all()
|
||||||
|
table_class = EventAttendeeTable
|
||||||
|
template_name = "membershipworks/eventattendee_list.dj.html"
|
||||||
|
export_formats = ("csv", "xlsx", "ods")
|
||||||
|
filterset_class = EventAttendeeFilters
|
||||||
|
|
||||||
|
def get_table_data(self):
|
||||||
|
return super().get_table_data().values("name", "email").distinct()
|
||||||
|
Loading…
Reference in New Issue
Block a user