paperwork: Add access verification report

This commit is contained in:
Adam Goldsmith 2024-02-07 21:03:19 -05:00
parent 43f992e2c3
commit c07e3ac07a
4 changed files with 161 additions and 1 deletions

View File

@ -24,6 +24,11 @@ class PaperworkDashboardFragment(dashboard.LinksCardDashboardFragment):
reverse("paperwork:instructors-and-vendor-report"), reverse("paperwork:instructors-and-vendor-report"),
permission="paperwork.view_instructororvendor", permission="paperwork.view_instructororvendor",
), ),
Link(
"Access Verification",
reverse("paperwork:access-verification-report"),
permission="paperwork.view_certification",
),
] ]
member = Member.from_user(self.request.user) member = Member.from_user(self.request.user)

View File

@ -0,0 +1,12 @@
{% extends "base.dj.html" %}
{% load render_table from django_tables2 %}
{% block title %}Access Verification{% endblock %}
{% block admin_link %}
{% url 'admin:paperwork_certification_changelist' %}
{% endblock %}
{% block content %}
{% include "cmsmanage/components/download_table.dj.html" %}
{% render_table table %}
{% endblock %}

View File

@ -35,4 +35,9 @@ urlpatterns = [
views.InstructorOrVendorReport.as_view(), views.InstructorOrVendorReport.as_view(),
name="instructors-and-vendor-report", name="instructors-and-vendor-report",
), ),
path(
"access-verification",
views.AccessVerificationReport.as_view(),
name="access-verification-report",
),
] ]

View File

@ -2,6 +2,9 @@ from django.conf import settings
from django.contrib import staticfiles from django.contrib import staticfiles
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db import models
from django.db.models import Case, Q, Value, When
from django.db.models.functions import Concat
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseNotFound from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseNotFound
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
@ -15,7 +18,13 @@ from django_tables2.export.views import ExportMixin
from membershipworks.models import Member from membershipworks.models import Member
from .models import Certification, Department, InstructorOrVendor, Waiver from .models import (
Certification,
CertificationVersion,
Department,
InstructorOrVendor,
Waiver,
)
WIKI_URL = settings.WIKI_URL WIKI_URL = settings.WIKI_URL
@ -186,3 +195,132 @@ 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(
ExportMixin,
SingleTableMixin,
PermissionRequiredMixin,
ListView,
):
permission_required = "paperwork.view_certification"
template_name = "paperwork/access_verification_report.dj.html"
table_class = AccessVerificationTable
export_formats = ("csv", "xlsx", "ods")
def get_queryset(self):
# TODO: could be done with subqueries if membershipworks was not a separate DB
def shop_error(access_field: str, shop_name: str):
member_list = list(
CertificationVersion.objects.filter(
is_current=True,
definition__department__name=shop_name,
certification__member__pk__isnull=False,
)
.values_list(
"certification__member__pk",
flat=True,
)
.distinct()
)
return Case(
When(
Q(**{access_field: True}) & ~Q(uid__in=member_list),
Value("Has access but no cert"),
),
When(
Q(**{access_field: False}) & Q(uid__in=member_list),
Value("Has cert but no access"),
),
default=None,
)
# TODO: could be a lot cleaner if membershipworks was not a separate DB
storage_closet_members = (
Member.objects.filter(
Member.objects.has_flag("label", "Volunteer: Desker")
| Q(billing_method__startswith="Desker")
)
.union(
*[
department.shop_lead_flag.members.all()
for department in Department.objects.filter(
shop_lead_flag__isnull=False
)
]
)
.values_list("pk", flat=True)
)
qs = (
Member.objects.with_is_active()
.filter(is_active=True)
.values(
"account_name",
"billing_method",
"join_date",
"renewal_date",
"access_front_door",
"access_studio_space",
access_card=Concat(
"access_card_facility_code",
Value("-", models.TextField()),
"access_card_number",
),
wood_shop_error=shop_error("access_wood_shop", "Wood Shop"),
metal_shop_error=shop_error("access_metal_shop", "Metal Shop"),
extended_hours_error=shop_error(
"access_front_door_and_studio_space_during_extended_hours",
"Closure/Lock-Up",
),
extended_hours_shops_error=shop_error(
"access_permitted_shops_during_extended_hours",
"Closure/Lock-Up",
),
storage_closet_error=Case(
When(
Q(access_storage_closet=True)
& ~Q(uid__in=storage_closet_members),
Value("Has access but not shop lead or desker"),
),
default=None,
),
)
.filter(
Q(access_front_door=False)
| Q(access_studio_space=False)
| Q(wood_shop_error__isnull=False)
| Q(metal_shop_error__isnull=False)
| Q(extended_hours_error__isnull=False)
| Q(extended_hours_shops_error__isnull=False)
| Q(storage_closet_error__isnull=False)
)
)
return qs