Split tables out of view
modules into tables
This commit is contained in:
parent
0ce441336f
commit
029b4dff28
63
doorcontrol/tables.py
Normal file
63
doorcontrol/tables.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import calendar
|
||||||
|
|
||||||
|
import django_tables2 as tables
|
||||||
|
|
||||||
|
from .models import HIDEvent
|
||||||
|
|
||||||
|
|
||||||
|
class UnitTimeTable(tables.Table):
|
||||||
|
members = tables.columns.Column()
|
||||||
|
members_delta = tables.columns.TemplateColumn(
|
||||||
|
"{{ value|floatformat:2}}%", verbose_name="Δ Members"
|
||||||
|
)
|
||||||
|
access_count = tables.columns.Column()
|
||||||
|
access_count_delta = tables.columns.TemplateColumn(
|
||||||
|
"{{ value|floatformat:2}}%", verbose_name="Δ Access Count"
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
fields = ("members", "members_delta", "access_count", "access_count_delta")
|
||||||
|
|
||||||
|
|
||||||
|
class DeniedAccessTable(tables.Table):
|
||||||
|
name = tables.TemplateColumn(
|
||||||
|
"{{ record.forename|default:'' }} {{ record.surname|default:'' }}"
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = HIDEvent
|
||||||
|
|
||||||
|
fields = (
|
||||||
|
"timestamp",
|
||||||
|
"door",
|
||||||
|
"event_type",
|
||||||
|
"name",
|
||||||
|
"raw_card_number",
|
||||||
|
"decoded_card_number",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MostActiveMembersTable(tables.Table):
|
||||||
|
name = tables.Column()
|
||||||
|
access_count = tables.Column()
|
||||||
|
|
||||||
|
|
||||||
|
class DetailByDayTable(tables.Table):
|
||||||
|
timestamp__date = tables.DateColumn(verbose_name="Date")
|
||||||
|
name = tables.Column()
|
||||||
|
access_count = tables.Column()
|
||||||
|
|
||||||
|
|
||||||
|
class BusiestDayOfWeekTable(tables.Table):
|
||||||
|
timestamp__week_day = tables.Column("Week Day")
|
||||||
|
events = tables.Column()
|
||||||
|
members = tables.Column()
|
||||||
|
|
||||||
|
def render_timestamp__week_day(self, value):
|
||||||
|
return calendar.day_name[(value - 2) % 7]
|
||||||
|
|
||||||
|
|
||||||
|
class BusiestTimeOfDayTable(tables.Table):
|
||||||
|
timestamp__hour = tables.TemplateColumn("{{ value }}:00", verbose_name="Hour")
|
||||||
|
events = tables.Column()
|
||||||
|
members = tables.Column()
|
@ -1,4 +1,3 @@
|
|||||||
import calendar
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||||
@ -19,6 +18,14 @@ from django_tables2 import SingleTableMixin
|
|||||||
from django_tables2.export.views import ExportMixin
|
from django_tables2.export.views import ExportMixin
|
||||||
|
|
||||||
from .models import Door, HIDEvent
|
from .models import Door, HIDEvent
|
||||||
|
from .tables import (
|
||||||
|
BusiestDayOfWeekTable,
|
||||||
|
BusiestTimeOfDayTable,
|
||||||
|
DeniedAccessTable,
|
||||||
|
DetailByDayTable,
|
||||||
|
MostActiveMembersTable,
|
||||||
|
UnitTimeTable,
|
||||||
|
)
|
||||||
|
|
||||||
REPORTS = []
|
REPORTS = []
|
||||||
|
|
||||||
@ -96,20 +103,6 @@ class BaseAccessReport(
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class UnitTimeTable(tables.Table):
|
|
||||||
members = tables.columns.Column()
|
|
||||||
members_delta = tables.columns.TemplateColumn(
|
|
||||||
"{{ value|floatformat:2}}%", verbose_name="Δ Members"
|
|
||||||
)
|
|
||||||
access_count = tables.columns.Column()
|
|
||||||
access_count_delta = tables.columns.TemplateColumn(
|
|
||||||
"{{ value|floatformat:2}}%", verbose_name="Δ Access Count"
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
fields = ("members", "members_delta", "access_count", "access_count_delta")
|
|
||||||
|
|
||||||
|
|
||||||
@register_report
|
@register_report
|
||||||
class AccessPerUnitTime(BaseAccessReport):
|
class AccessPerUnitTime(BaseAccessReport):
|
||||||
table_class = UnitTimeTable
|
table_class = UnitTimeTable
|
||||||
@ -209,24 +202,6 @@ class AccessPerUnitTime(BaseAccessReport):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DeniedAccessTable(tables.Table):
|
|
||||||
name = tables.TemplateColumn(
|
|
||||||
"{{ record.forename|default:'' }} {{ record.surname|default:'' }}"
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = HIDEvent
|
|
||||||
|
|
||||||
fields = (
|
|
||||||
"timestamp",
|
|
||||||
"door",
|
|
||||||
"event_type",
|
|
||||||
"name",
|
|
||||||
"raw_card_number",
|
|
||||||
"decoded_card_number",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@register_report
|
@register_report
|
||||||
class DeniedAccess(BaseAccessReport):
|
class DeniedAccess(BaseAccessReport):
|
||||||
_report_name = "Denied Access"
|
_report_name = "Denied Access"
|
||||||
@ -244,11 +219,6 @@ class DeniedAccess(BaseAccessReport):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class MostActiveMembersTable(tables.Table):
|
|
||||||
name = tables.Column()
|
|
||||||
access_count = tables.Column()
|
|
||||||
|
|
||||||
|
|
||||||
@register_report
|
@register_report
|
||||||
class MostActiveMembers(BaseAccessReport):
|
class MostActiveMembers(BaseAccessReport):
|
||||||
_report_name = "Most Active Members"
|
_report_name = "Most Active Members"
|
||||||
@ -271,12 +241,6 @@ class MostActiveMembers(BaseAccessReport):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DetailByDayTable(tables.Table):
|
|
||||||
timestamp__date = tables.DateColumn(verbose_name="Date")
|
|
||||||
name = tables.Column()
|
|
||||||
access_count = tables.Column()
|
|
||||||
|
|
||||||
|
|
||||||
@register_report
|
@register_report
|
||||||
class DetailByDay(BaseAccessReport):
|
class DetailByDay(BaseAccessReport):
|
||||||
_report_name = "Detail by Day"
|
_report_name = "Detail by Day"
|
||||||
@ -299,15 +263,6 @@ class DetailByDay(BaseAccessReport):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class BusiestDayOfWeekTable(tables.Table):
|
|
||||||
timestamp__week_day = tables.Column("Week Day")
|
|
||||||
events = tables.Column()
|
|
||||||
members = tables.Column()
|
|
||||||
|
|
||||||
def render_timestamp__week_day(self, value):
|
|
||||||
return calendar.day_name[(value - 2) % 7]
|
|
||||||
|
|
||||||
|
|
||||||
@register_report
|
@register_report
|
||||||
class BusiestDayOfWeek(BaseAccessReport):
|
class BusiestDayOfWeek(BaseAccessReport):
|
||||||
_report_name = "Busiest Day of the Week"
|
_report_name = "Busiest Day of the Week"
|
||||||
@ -326,12 +281,6 @@ class BusiestDayOfWeek(BaseAccessReport):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class BusiestTimeOfDayTable(tables.Table):
|
|
||||||
timestamp__hour = tables.TemplateColumn("{{ value }}:00", verbose_name="Hour")
|
|
||||||
events = tables.Column()
|
|
||||||
members = tables.Column()
|
|
||||||
|
|
||||||
|
|
||||||
@register_report
|
@register_report
|
||||||
class BusiestTimeOfDay(BaseAccessReport):
|
class BusiestTimeOfDay(BaseAccessReport):
|
||||||
_report_name = "Busiest Time of Day"
|
_report_name = "Busiest Time of Day"
|
||||||
|
168
membershipworks/tables.py
Normal file
168
membershipworks/tables.py
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from django.template.defaultfilters import floatformat
|
||||||
|
from django.utils.html import format_html
|
||||||
|
from django.utils.safestring import SafeString
|
||||||
|
|
||||||
|
import django_tables2 as tables
|
||||||
|
|
||||||
|
from .models import EventAttendee, EventExt, Member
|
||||||
|
|
||||||
|
|
||||||
|
class DurationColumn(tables.Column):
|
||||||
|
def render(self, value: timedelta):
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
return floatformat(value.total_seconds() / 60 / 60, -2)
|
||||||
|
|
||||||
|
def value(self, value: timedelta):
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
return value.total_seconds() / 60 / 60
|
||||||
|
|
||||||
|
|
||||||
|
class EventTable(tables.Table):
|
||||||
|
title = tables.TemplateColumn(
|
||||||
|
template_code=(
|
||||||
|
'<a title="MembershipWorks" href="https://membershipworks.com/admin/#!event/admin/{{ record.url }}">{{ value }}</a> '
|
||||||
|
'<a title="Admin" href="{% url "admin:membershipworks_eventext_change" record.pk %}"><i class="bi bi-pencil-square"></i></a> '
|
||||||
|
'<a title="Details" href="{% url "membershipworks:event-detail" record.pk %}"><i class="bi bi-receipt"></i></a> '
|
||||||
|
),
|
||||||
|
accessor="unescaped_title",
|
||||||
|
)
|
||||||
|
occurred = tables.BooleanColumn(visible=False)
|
||||||
|
start = tables.DateColumn("N d, Y")
|
||||||
|
duration = DurationColumn()
|
||||||
|
person_hours = DurationColumn()
|
||||||
|
meetings = tables.Column()
|
||||||
|
gross_revenue = tables.Column()
|
||||||
|
total_due_to_instructor = tables.Column()
|
||||||
|
net_revenue = tables.Column()
|
||||||
|
invoice__date_submitted = tables.DateColumn(verbose_name="Invoice Submitted")
|
||||||
|
invoice__date_paid = tables.DateColumn(verbose_name="Invoice Paid")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = EventExt
|
||||||
|
fields = (
|
||||||
|
"title",
|
||||||
|
"occurred",
|
||||||
|
"start",
|
||||||
|
"instructor",
|
||||||
|
"category",
|
||||||
|
"count",
|
||||||
|
"cap",
|
||||||
|
"meetings",
|
||||||
|
"duration",
|
||||||
|
"person_hours",
|
||||||
|
"gross_revenue",
|
||||||
|
"total_due_to_instructor",
|
||||||
|
"net_revenue",
|
||||||
|
)
|
||||||
|
row_attrs = {
|
||||||
|
"class": lambda record: (
|
||||||
|
"" if record.occurred else "text-decoration-line-through table-danger"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class EventSummaryTable(tables.Table):
|
||||||
|
event_count = tables.Column("Events")
|
||||||
|
canceled_event_count = tables.Column("Canceled Events")
|
||||||
|
count__sum = tables.Column("Tickets")
|
||||||
|
instructor__count = tables.Column("Unique Instructors")
|
||||||
|
meetings__sum = tables.Column("Meetings")
|
||||||
|
duration__sum = DurationColumn("Class Hours")
|
||||||
|
person_hours__sum = DurationColumn("Person Hours")
|
||||||
|
gross_revenue__sum = tables.Column("Gross Revenue")
|
||||||
|
total_due_to_instructor__sum = tables.Column("Total Due to Instructor")
|
||||||
|
net_revenue__sum = tables.Column("Net Revenue")
|
||||||
|
|
||||||
|
|
||||||
|
class UserEventTable(EventTable):
|
||||||
|
title = tables.Column(linkify=True, accessor="unescaped_title")
|
||||||
|
instructor = None
|
||||||
|
person_hours = None
|
||||||
|
gross_revenue = None
|
||||||
|
net_revenue = None
|
||||||
|
|
||||||
|
|
||||||
|
class InvoiceMoneyColumn(tables.columns.Column):
|
||||||
|
def render(self, value):
|
||||||
|
return f"${super().render(value):.2f}"
|
||||||
|
|
||||||
|
|
||||||
|
class InvoiceMoneyFooterColumn(InvoiceMoneyColumn):
|
||||||
|
def render_footer(self, bound_column, table):
|
||||||
|
value = getattr(table.event, bound_column.accessor)
|
||||||
|
if value is not None:
|
||||||
|
return f"${value:.2f}"
|
||||||
|
else:
|
||||||
|
return bound_column.default
|
||||||
|
|
||||||
|
|
||||||
|
class InvoiceTable(tables.Table):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.event = kwargs.pop("event")
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _math_header(name: str, formula: str) -> SafeString:
|
||||||
|
return format_html(
|
||||||
|
'{} <div class="text-nowrap font-monospace fw-light">[{}]</div>',
|
||||||
|
name,
|
||||||
|
formula,
|
||||||
|
)
|
||||||
|
|
||||||
|
label = tables.Column("Ticket Type", footer="Subtotals")
|
||||||
|
list_price = InvoiceMoneyColumn("Ticket Price")
|
||||||
|
actual_price = InvoiceMoneyColumn(_math_header("Actual Price", "P"))
|
||||||
|
quantity = tables.Column(
|
||||||
|
_math_header("Quantity", "Q"),
|
||||||
|
footer=lambda table: table.event.quantity,
|
||||||
|
)
|
||||||
|
amount = InvoiceMoneyFooterColumn(_math_header("Amount", "A=P*Q"))
|
||||||
|
materials = InvoiceMoneyFooterColumn(
|
||||||
|
_math_header("CMS Collected Materials Fee", "M=m*Q")
|
||||||
|
)
|
||||||
|
amount_without_materials = InvoiceMoneyFooterColumn(
|
||||||
|
_math_header("Event Revenue Base", "B=A-M")
|
||||||
|
)
|
||||||
|
instructor_revenue = InvoiceMoneyFooterColumn(
|
||||||
|
_math_header("Instructor Percentage Revenue", "R=B*I")
|
||||||
|
)
|
||||||
|
instructor_amount = InvoiceMoneyFooterColumn(
|
||||||
|
_math_header("Amount Due to Instructor", "R+M")
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
attrs = {
|
||||||
|
"class": "table table-sm mx-auto w-auto",
|
||||||
|
"tbody": {"class": "table-group-divider"},
|
||||||
|
"tfoot": {"class": "table-group-divider"},
|
||||||
|
}
|
||||||
|
orderable = False
|
||||||
|
|
||||||
|
|
||||||
|
class EventAttendeeTable(tables.Table):
|
||||||
|
class Meta:
|
||||||
|
model = EventAttendee
|
||||||
|
fields = ("name", "email")
|
||||||
|
|
||||||
|
|
||||||
|
class MissingPaperworkTable(tables.Table):
|
||||||
|
policy_agreement = tables.BooleanColumn()
|
||||||
|
authorize_charge = tables.BooleanColumn()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Member
|
||||||
|
fields = [
|
||||||
|
"first_name",
|
||||||
|
"last_name",
|
||||||
|
"membership",
|
||||||
|
"billing_method",
|
||||||
|
"join_date",
|
||||||
|
"membership_agreement_signed_and_on_file_date",
|
||||||
|
"waiver_form_signed_and_on_file_date",
|
||||||
|
"policy_agreement",
|
||||||
|
"authorize_charge",
|
||||||
|
]
|
@ -1,5 +1,5 @@
|
|||||||
import uuid
|
import uuid
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -15,12 +15,9 @@ from django.db.models import OuterRef, Q, Subquery
|
|||||||
from django.db.models.functions import TruncMonth, TruncYear
|
from django.db.models.functions import TruncMonth, TruncYear
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.template.defaultfilters import floatformat
|
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.html import format_html
|
|
||||||
from django.utils.safestring import SafeString
|
|
||||||
from django.views.generic import DetailView, ListView
|
from django.views.generic import DetailView, ListView
|
||||||
from django.views.generic.dates import (
|
from django.views.generic.dates import (
|
||||||
ArchiveIndexView,
|
ArchiveIndexView,
|
||||||
@ -46,6 +43,14 @@ from membershipworks.membershipworks_api import MembershipWorks
|
|||||||
from .forms import EventInvoiceForm
|
from .forms import EventInvoiceForm
|
||||||
from .invoice_email import make_invoice_emails
|
from .invoice_email import make_invoice_emails
|
||||||
from .models import EventAttendee, EventExt, EventInvoice, Member
|
from .models import EventAttendee, EventExt, EventInvoice, Member
|
||||||
|
from .tables import (
|
||||||
|
EventAttendeeTable,
|
||||||
|
EventSummaryTable,
|
||||||
|
EventTable,
|
||||||
|
InvoiceTable,
|
||||||
|
MissingPaperworkTable,
|
||||||
|
UserEventTable,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class MemberAutocomplete(autocomplete.Select2QuerySetView):
|
class MemberAutocomplete(autocomplete.Select2QuerySetView):
|
||||||
@ -138,75 +143,6 @@ def upcoming_events(request):
|
|||||||
return render(request, "membershipworks/upcoming_events.dj.html", context)
|
return render(request, "membershipworks/upcoming_events.dj.html", context)
|
||||||
|
|
||||||
|
|
||||||
class DurationColumn(tables.Column):
|
|
||||||
def render(self, value: timedelta):
|
|
||||||
if value is None:
|
|
||||||
return None
|
|
||||||
return floatformat(value.total_seconds() / 60 / 60, -2)
|
|
||||||
|
|
||||||
def value(self, value: timedelta):
|
|
||||||
if value is None:
|
|
||||||
return None
|
|
||||||
return value.total_seconds() / 60 / 60
|
|
||||||
|
|
||||||
|
|
||||||
class EventTable(tables.Table):
|
|
||||||
title = tables.TemplateColumn(
|
|
||||||
template_code=(
|
|
||||||
'<a title="MembershipWorks" href="https://membershipworks.com/admin/#!event/admin/{{ record.url }}">{{ value }}</a> '
|
|
||||||
'<a title="Admin" href="{% url "admin:membershipworks_eventext_change" record.pk %}"><i class="bi bi-pencil-square"></i></a> '
|
|
||||||
'<a title="Details" href="{% url "membershipworks:event-detail" record.pk %}"><i class="bi bi-receipt"></i></a> '
|
|
||||||
),
|
|
||||||
accessor="unescaped_title",
|
|
||||||
)
|
|
||||||
occurred = tables.BooleanColumn(visible=False)
|
|
||||||
start = tables.DateColumn("N d, Y")
|
|
||||||
duration = DurationColumn()
|
|
||||||
person_hours = DurationColumn()
|
|
||||||
meetings = tables.Column()
|
|
||||||
gross_revenue = tables.Column()
|
|
||||||
total_due_to_instructor = tables.Column()
|
|
||||||
net_revenue = tables.Column()
|
|
||||||
invoice__date_submitted = tables.DateColumn(verbose_name="Invoice Submitted")
|
|
||||||
invoice__date_paid = tables.DateColumn(verbose_name="Invoice Paid")
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = EventExt
|
|
||||||
fields = (
|
|
||||||
"title",
|
|
||||||
"occurred",
|
|
||||||
"start",
|
|
||||||
"instructor",
|
|
||||||
"category",
|
|
||||||
"count",
|
|
||||||
"cap",
|
|
||||||
"meetings",
|
|
||||||
"duration",
|
|
||||||
"person_hours",
|
|
||||||
"gross_revenue",
|
|
||||||
"total_due_to_instructor",
|
|
||||||
"net_revenue",
|
|
||||||
)
|
|
||||||
row_attrs = {
|
|
||||||
"class": lambda record: (
|
|
||||||
"" if record.occurred else "text-decoration-line-through table-danger"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class EventSummaryTable(tables.Table):
|
|
||||||
event_count = tables.Column("Events")
|
|
||||||
canceled_event_count = tables.Column("Canceled Events")
|
|
||||||
count__sum = tables.Column("Tickets")
|
|
||||||
instructor__count = tables.Column("Unique Instructors")
|
|
||||||
meetings__sum = tables.Column("Meetings")
|
|
||||||
duration__sum = DurationColumn("Class Hours")
|
|
||||||
person_hours__sum = DurationColumn("Person Hours")
|
|
||||||
gross_revenue__sum = tables.Column("Gross Revenue")
|
|
||||||
total_due_to_instructor__sum = tables.Column("Total Due to Instructor")
|
|
||||||
net_revenue__sum = tables.Column("Net Revenue")
|
|
||||||
|
|
||||||
|
|
||||||
class EventIndexReport(
|
class EventIndexReport(
|
||||||
ExportMixin, SingleTableMixin, PermissionRequiredMixin, ArchiveIndexView
|
ExportMixin, SingleTableMixin, PermissionRequiredMixin, ArchiveIndexView
|
||||||
):
|
):
|
||||||
@ -303,14 +239,6 @@ class EventMonthReport(
|
|||||||
return f"mw_events_{self.get_year()}-{self.get_month():02}.{export_format}"
|
return f"mw_events_{self.get_year()}-{self.get_month():02}.{export_format}"
|
||||||
|
|
||||||
|
|
||||||
class UserEventTable(EventTable):
|
|
||||||
title = tables.Column(linkify=True, accessor="unescaped_title")
|
|
||||||
instructor = None
|
|
||||||
person_hours = None
|
|
||||||
gross_revenue = None
|
|
||||||
net_revenue = None
|
|
||||||
|
|
||||||
|
|
||||||
class UserEventView(SingleTableMixin, ListView):
|
class UserEventView(SingleTableMixin, ListView):
|
||||||
model = EventExt
|
model = EventExt
|
||||||
table_class = UserEventTable
|
table_class = UserEventTable
|
||||||
@ -339,63 +267,6 @@ class UserEventView(SingleTableMixin, ListView):
|
|||||||
return f"my_events_{self.member.uid if self.member is not None else 'no-uid'}.{export_format}"
|
return f"my_events_{self.member.uid if self.member is not None else 'no-uid'}.{export_format}"
|
||||||
|
|
||||||
|
|
||||||
class InvoiceMoneyColumn(tables.columns.Column):
|
|
||||||
def render(self, value):
|
|
||||||
return f"${super().render(value):.2f}"
|
|
||||||
|
|
||||||
|
|
||||||
class InvoiceMoneyFooterColumn(InvoiceMoneyColumn):
|
|
||||||
def render_footer(self, bound_column, table):
|
|
||||||
value = getattr(table.event, bound_column.accessor)
|
|
||||||
if value is not None:
|
|
||||||
return f"${value:.2f}"
|
|
||||||
else:
|
|
||||||
return bound_column.default
|
|
||||||
|
|
||||||
|
|
||||||
class InvoiceTable(tables.Table):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.event = kwargs.pop("event")
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _math_header(name: str, formula: str) -> SafeString:
|
|
||||||
return format_html(
|
|
||||||
'{} <div class="text-nowrap font-monospace fw-light">[{}]</div>',
|
|
||||||
name,
|
|
||||||
formula,
|
|
||||||
)
|
|
||||||
|
|
||||||
label = tables.Column("Ticket Type", footer="Subtotals")
|
|
||||||
list_price = InvoiceMoneyColumn("Ticket Price")
|
|
||||||
actual_price = InvoiceMoneyColumn(_math_header("Actual Price", "P"))
|
|
||||||
quantity = tables.Column(
|
|
||||||
_math_header("Quantity", "Q"),
|
|
||||||
footer=lambda table: table.event.quantity,
|
|
||||||
)
|
|
||||||
amount = InvoiceMoneyFooterColumn(_math_header("Amount", "A=P*Q"))
|
|
||||||
materials = InvoiceMoneyFooterColumn(
|
|
||||||
_math_header("CMS Collected Materials Fee", "M=m*Q")
|
|
||||||
)
|
|
||||||
amount_without_materials = InvoiceMoneyFooterColumn(
|
|
||||||
_math_header("Event Revenue Base", "B=A-M")
|
|
||||||
)
|
|
||||||
instructor_revenue = InvoiceMoneyFooterColumn(
|
|
||||||
_math_header("Instructor Percentage Revenue", "R=B*I")
|
|
||||||
)
|
|
||||||
instructor_amount = InvoiceMoneyFooterColumn(
|
|
||||||
_math_header("Amount Due to Instructor", "R+M")
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
attrs = {
|
|
||||||
"class": "table table-sm mx-auto w-auto",
|
|
||||||
"tbody": {"class": "table-group-divider"},
|
|
||||||
"tfoot": {"class": "table-group-divider"},
|
|
||||||
}
|
|
||||||
orderable = False
|
|
||||||
|
|
||||||
|
|
||||||
class EventDetailView(
|
class EventDetailView(
|
||||||
SingleTableMixin, FormMixin, AccessMixin, DetailView, ProcessFormView
|
SingleTableMixin, FormMixin, AccessMixin, DetailView, ProcessFormView
|
||||||
):
|
):
|
||||||
@ -527,12 +398,6 @@ class EventInvoicePDFView(AccessMixin, BaseDetailView):
|
|||||||
return self.handle_no_permission()
|
return self.handle_no_permission()
|
||||||
|
|
||||||
|
|
||||||
class EventAttendeeTable(tables.Table):
|
|
||||||
class Meta:
|
|
||||||
model = EventAttendee
|
|
||||||
fields = ("name", "email")
|
|
||||||
|
|
||||||
|
|
||||||
class EventAttendeeFilters(django_filters.FilterSet):
|
class EventAttendeeFilters(django_filters.FilterSet):
|
||||||
new_since = django_filters.DateFilter(
|
new_since = django_filters.DateFilter(
|
||||||
field_name="event__start", method="filter_new_since"
|
field_name="event__start", method="filter_new_since"
|
||||||
@ -560,25 +425,6 @@ class EventAttendeeListView(
|
|||||||
return super().get_table_data().values("name", "email").distinct()
|
return super().get_table_data().values("name", "email").distinct()
|
||||||
|
|
||||||
|
|
||||||
class MissingPaperworkTable(tables.Table):
|
|
||||||
policy_agreement = tables.BooleanColumn()
|
|
||||||
authorize_charge = tables.BooleanColumn()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Member
|
|
||||||
fields = [
|
|
||||||
"first_name",
|
|
||||||
"last_name",
|
|
||||||
"membership",
|
|
||||||
"billing_method",
|
|
||||||
"join_date",
|
|
||||||
"membership_agreement_signed_and_on_file_date",
|
|
||||||
"waiver_form_signed_and_on_file_date",
|
|
||||||
"policy_agreement",
|
|
||||||
"authorize_charge",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class MissingPaperworkReport(
|
class MissingPaperworkReport(
|
||||||
ExportMixin,
|
ExportMixin,
|
||||||
SingleTableMixin,
|
SingleTableMixin,
|
||||||
|
92
paperwork/tables.py
Normal file
92
paperwork/tables.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import django_tables2 as tables
|
||||||
|
|
||||||
|
from .models import (
|
||||||
|
InstructorOrVendor,
|
||||||
|
Waiver,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class WarnEmptyColumn(tables.Column):
|
||||||
|
attrs = {
|
||||||
|
"td": {
|
||||||
|
"class": lambda value, bound_column: (
|
||||||
|
"table-danger" if value == bound_column.default else ""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class WaiverReportTable(tables.Table):
|
||||||
|
emergency_contact_name = WarnEmptyColumn()
|
||||||
|
emergency_contact_number = WarnEmptyColumn()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Waiver
|
||||||
|
fields = [
|
||||||
|
"name",
|
||||||
|
"date",
|
||||||
|
"emergency_contact_name",
|
||||||
|
"emergency_contact_number",
|
||||||
|
"waiver_version",
|
||||||
|
"guardian_name",
|
||||||
|
"guardian_relation",
|
||||||
|
"guardian_date",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class InstructorOrVendorTable(tables.Table):
|
||||||
|
instructor_agreement_date = WarnEmptyColumn(
|
||||||
|
"Instructor Agreement Date(s)", default="Missing"
|
||||||
|
)
|
||||||
|
w9_date = WarnEmptyColumn(default="Missing")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = InstructorOrVendor
|
||||||
|
fields = [
|
||||||
|
"name",
|
||||||
|
"instructor_agreement_date",
|
||||||
|
"w9_date",
|
||||||
|
"phone",
|
||||||
|
"email_address",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ShopAccessErrorColumn(tables.Column):
|
||||||
|
def td_class(value):
|
||||||
|
if value.startswith("Has access but"):
|
||||||
|
return "table-danger"
|
||||||
|
elif value.startswith("Has cert but"):
|
||||||
|
return "table-warning"
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
attrs = {"td": {"class": td_class}}
|
||||||
|
|
||||||
|
|
||||||
|
class AccessVerificationTable(tables.Table):
|
||||||
|
account_name = tables.Column()
|
||||||
|
access_card = tables.Column()
|
||||||
|
billing_method = tables.Column()
|
||||||
|
join_date = tables.DateColumn()
|
||||||
|
renewal_date = tables.DateColumn()
|
||||||
|
access_front_door = tables.BooleanColumn(verbose_name="Front Door")
|
||||||
|
access_studio_space = tables.BooleanColumn(verbose_name="Studio Space")
|
||||||
|
wood_shop_error = ShopAccessErrorColumn()
|
||||||
|
metal_shop_error = ShopAccessErrorColumn()
|
||||||
|
extended_hours_error = ShopAccessErrorColumn()
|
||||||
|
extended_hours_shops_error = ShopAccessErrorColumn()
|
||||||
|
storage_closet_error = ShopAccessErrorColumn()
|
||||||
|
|
||||||
|
|
||||||
|
class CertifiersTable(tables.Table):
|
||||||
|
certified_by = tables.Column()
|
||||||
|
certification_version__definition__name = tables.Column("Certification")
|
||||||
|
certification_version__definition__department__name = tables.Column("Department")
|
||||||
|
number_issued_on_this_tool = tables.Column()
|
||||||
|
last_issued_certification_date = tables.Column()
|
||||||
|
|
||||||
|
|
||||||
|
class CertificationCountTable(tables.Table):
|
||||||
|
certification_version__definition__name = tables.Column("Certification")
|
||||||
|
certification_version__definition__department__name = tables.Column("Department")
|
||||||
|
total_issued = tables.Column()
|
@ -19,7 +19,6 @@ from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseNotFou
|
|||||||
from django.shortcuts import get_object_or_404, render
|
from django.shortcuts import get_object_or_404, render
|
||||||
from django.views.generic import ListView
|
from django.views.generic import ListView
|
||||||
|
|
||||||
import django_tables2 as tables
|
|
||||||
import requests
|
import requests
|
||||||
import weasyprint
|
import weasyprint
|
||||||
from django_mysql.models import GroupConcat
|
from django_mysql.models import GroupConcat
|
||||||
@ -35,6 +34,13 @@ from .models import (
|
|||||||
InstructorOrVendor,
|
InstructorOrVendor,
|
||||||
Waiver,
|
Waiver,
|
||||||
)
|
)
|
||||||
|
from .tables import (
|
||||||
|
AccessVerificationTable,
|
||||||
|
CertificationCountTable,
|
||||||
|
CertifiersTable,
|
||||||
|
InstructorOrVendorTable,
|
||||||
|
WaiverReportTable,
|
||||||
|
)
|
||||||
|
|
||||||
WIKI_URL = settings.WIKI_URL
|
WIKI_URL = settings.WIKI_URL
|
||||||
|
|
||||||
@ -120,34 +126,6 @@ def certification_pdf(request, cert_name):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class WarnEmptyColumn(tables.Column):
|
|
||||||
attrs = {
|
|
||||||
"td": {
|
|
||||||
"class": lambda value, bound_column: "table-danger"
|
|
||||||
if value == bound_column.default
|
|
||||||
else ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class WaiverReportTable(tables.Table):
|
|
||||||
emergency_contact_name = WarnEmptyColumn()
|
|
||||||
emergency_contact_number = WarnEmptyColumn()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Waiver
|
|
||||||
fields = [
|
|
||||||
"name",
|
|
||||||
"date",
|
|
||||||
"emergency_contact_name",
|
|
||||||
"emergency_contact_number",
|
|
||||||
"waiver_version",
|
|
||||||
"guardian_name",
|
|
||||||
"guardian_relation",
|
|
||||||
"guardian_date",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class WaiverReport(ExportMixin, SingleTableMixin, PermissionRequiredMixin, ListView):
|
class WaiverReport(ExportMixin, SingleTableMixin, PermissionRequiredMixin, ListView):
|
||||||
permission_required = "paperwork.view_waiver"
|
permission_required = "paperwork.view_waiver"
|
||||||
template_name = "paperwork/waiver_report.dj.html"
|
template_name = "paperwork/waiver_report.dj.html"
|
||||||
@ -156,23 +134,6 @@ class WaiverReport(ExportMixin, SingleTableMixin, PermissionRequiredMixin, ListV
|
|||||||
table_pagination = False
|
table_pagination = False
|
||||||
|
|
||||||
|
|
||||||
class InstructorOrVendorTable(tables.Table):
|
|
||||||
instructor_agreement_date = WarnEmptyColumn(
|
|
||||||
"Instructor Agreement Date(s)", default="Missing"
|
|
||||||
)
|
|
||||||
w9_date = WarnEmptyColumn(default="Missing")
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = InstructorOrVendor
|
|
||||||
fields = [
|
|
||||||
"name",
|
|
||||||
"instructor_agreement_date",
|
|
||||||
"w9_date",
|
|
||||||
"phone",
|
|
||||||
"email_address",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class InstructorOrVendorReport(
|
class InstructorOrVendorReport(
|
||||||
ExportMixin,
|
ExportMixin,
|
||||||
SingleTableMixin,
|
SingleTableMixin,
|
||||||
@ -203,33 +164,6 @@ class InstructorOrVendorReport(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ShopAccessErrorColumn(tables.Column):
|
|
||||||
def td_class(value):
|
|
||||||
if value.startswith("Has access but"):
|
|
||||||
return "table-danger"
|
|
||||||
elif value.startswith("Has cert but"):
|
|
||||||
return "table-warning"
|
|
||||||
else:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
attrs = {"td": {"class": td_class}}
|
|
||||||
|
|
||||||
|
|
||||||
class AccessVerificationTable(tables.Table):
|
|
||||||
account_name = tables.Column()
|
|
||||||
access_card = tables.Column()
|
|
||||||
billing_method = tables.Column()
|
|
||||||
join_date = tables.DateColumn()
|
|
||||||
renewal_date = tables.DateColumn()
|
|
||||||
access_front_door = tables.BooleanColumn(verbose_name="Front Door")
|
|
||||||
access_studio_space = tables.BooleanColumn(verbose_name="Studio Space")
|
|
||||||
wood_shop_error = ShopAccessErrorColumn()
|
|
||||||
metal_shop_error = ShopAccessErrorColumn()
|
|
||||||
extended_hours_error = ShopAccessErrorColumn()
|
|
||||||
extended_hours_shops_error = ShopAccessErrorColumn()
|
|
||||||
storage_closet_error = ShopAccessErrorColumn()
|
|
||||||
|
|
||||||
|
|
||||||
class AccessVerificationReport(
|
class AccessVerificationReport(
|
||||||
ExportMixin,
|
ExportMixin,
|
||||||
SingleTableMixin,
|
SingleTableMixin,
|
||||||
@ -317,14 +251,6 @@ class AccessVerificationReport(
|
|||||||
return qs
|
return qs
|
||||||
|
|
||||||
|
|
||||||
class CertifiersTable(tables.Table):
|
|
||||||
certified_by = tables.Column()
|
|
||||||
certification_version__definition__name = tables.Column("Certification")
|
|
||||||
certification_version__definition__department__name = tables.Column("Department")
|
|
||||||
number_issued_on_this_tool = tables.Column()
|
|
||||||
last_issued_certification_date = tables.Column()
|
|
||||||
|
|
||||||
|
|
||||||
class CertifiersReport(
|
class CertifiersReport(
|
||||||
ExportMixin,
|
ExportMixin,
|
||||||
SingleTableMixin,
|
SingleTableMixin,
|
||||||
@ -356,12 +282,6 @@ class CertifiersReport(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class CertificationCountTable(tables.Table):
|
|
||||||
certification_version__definition__name = tables.Column("Certification")
|
|
||||||
certification_version__definition__department__name = tables.Column("Department")
|
|
||||||
total_issued = tables.Column()
|
|
||||||
|
|
||||||
|
|
||||||
class CertificationCountReport(
|
class CertificationCountReport(
|
||||||
ExportMixin,
|
ExportMixin,
|
||||||
SingleTableMixin,
|
SingleTableMixin,
|
||||||
|
Loading…
Reference in New Issue
Block a user