paperwork: Add Audits for Waivers and Certifications

This commit is contained in:
Adam Goldsmith 2023-04-10 13:12:45 -04:00
parent b462e6b18f
commit 250c96f1c2
3 changed files with 162 additions and 1 deletions

View File

@ -1,3 +1,4 @@
from django import forms
from django.core import mail from django.core import mail
from django.contrib import admin, messages from django.contrib import admin, messages
from django.db.models import Value from django.db.models import Value
@ -8,15 +9,34 @@ from .models import (
Department, Department,
CertificationDefinition, CertificationDefinition,
Certification, Certification,
CertificationAudit,
CertificationVersion, CertificationVersion,
InstructorOrVendor, InstructorOrVendor,
SpecialProgram, SpecialProgram,
Waiver, Waiver,
WaiverAudit,
) )
from .forms import CertificationForm from .forms import CertificationForm
from .certification_emails import all_certification_emails from .certification_emails import all_certification_emails
class AlwaysChangedModelForm(forms.models.ModelForm):
"""By always returning true even unchanged inlines will get validated and saved."""
def has_changed(self):
return True
class AbstractAuditInline(admin.TabularInline):
extra = 0
form = AlwaysChangedModelForm
def get_formset(self, request, obj=None, **kwargs):
formset = super().get_formset(request, obj, **kwargs)
formset.form.base_fields["user"].initial = request.user
return formset
@admin.register(Department) @admin.register(Department)
class DepartmentAdmin(admin.ModelAdmin): class DepartmentAdmin(admin.ModelAdmin):
search_fields = ["name"] search_fields = ["name"]
@ -64,6 +84,10 @@ class CertificationDefinitionAdmin(admin.ModelAdmin):
return obj.latest_version().semantic_version() return obj.latest_version().semantic_version()
class CertificationAuditInline(AbstractAuditInline):
model = CertificationAudit
@admin.register(Certification) @admin.register(Certification)
class CertificationAdmin(admin.ModelAdmin): class CertificationAdmin(admin.ModelAdmin):
form = CertificationForm form = CertificationForm
@ -74,6 +98,7 @@ class CertificationAdmin(admin.ModelAdmin):
] ]
autocomplete_fields = ["member"] autocomplete_fields = ["member"]
exclude = ["shop_lead_notified"] exclude = ["shop_lead_notified"]
inlines = [CertificationAuditInline]
def get_queryset(self, request): def get_queryset(self, request):
qs = super().get_queryset(request) qs = super().get_queryset(request)
@ -112,6 +137,10 @@ class CertificationAdmin(admin.ModelAdmin):
def certification_department(self, obj): def certification_department(self, obj):
return obj.certification_version.definition.department return obj.certification_version.definition.department
@admin.display(description="Latest Audit")
def latest_audit(self, obj):
return obj.audits.latest()
list_display = [ list_display = [
"certification_name", "certification_name",
"name", "name",
@ -121,6 +150,7 @@ class CertificationAdmin(admin.ModelAdmin):
"date", "date",
"shop_lead_notified", "shop_lead_notified",
"certified_by", "certified_by",
"latest_audit",
] ]
list_display_links = [ list_display_links = [
"certification_name", "certification_name",
@ -129,6 +159,7 @@ class CertificationAdmin(admin.ModelAdmin):
list_filter = [ list_filter = [
"certification_version__definition__department", "certification_version__definition__department",
("shop_lead_notified", admin.EmptyFieldListFilter), ("shop_lead_notified", admin.EmptyFieldListFilter),
("audits", admin.EmptyFieldListFilter),
] ]
actions = ["send_notifications"] actions = ["send_notifications"]
@ -188,10 +219,13 @@ class SpecialProgramAdmin(admin.ModelAdmin):
] ]
class WaiverAuditInline(AbstractAuditInline):
model = WaiverAudit
@admin.register(Waiver) @admin.register(Waiver)
class WaiverAdmin(admin.ModelAdmin): class WaiverAdmin(admin.ModelAdmin):
search_fields = ["name"] search_fields = ["name"]
list_display = [ list_display = [
"name", "name",
"date", "date",
@ -201,7 +235,17 @@ class WaiverAdmin(admin.ModelAdmin):
"guardian_name", "guardian_name",
"guardian_relation", "guardian_relation",
"guardian_date", "guardian_date",
"latest_audit",
] ]
list_filter = [
"waiver_version",
("audits", admin.EmptyFieldListFilter),
]
inlines = [WaiverAuditInline]
@admin.display(description="Latest Audit")
def latest_audit(self, obj):
return obj.audits.latest()
admin.site.register(CmsRedRiverVeteransScholarship) admin.site.register(CmsRedRiverVeteransScholarship)

View File

@ -0,0 +1,90 @@
# Generated by Django 4.2 on 2023-04-10 05:34
import datetime
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("paperwork", "0011_alter_certificationversion_options"),
]
operations = [
migrations.CreateModel(
name="WaiverAudit",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("date", models.DateField(default=datetime.date.today, unique=True)),
("good", models.BooleanField(default=False)),
("notes", models.CharField(blank=True, max_length=255)),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
to=settings.AUTH_USER_MODEL,
),
),
(
"waiver",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="audits",
to="paperwork.waiver",
),
),
],
options={
"ordering": ["date"],
"get_latest_by": ["date"],
"abstract": False,
},
),
migrations.CreateModel(
name="CertificationAudit",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("date", models.DateField(default=datetime.date.today, unique=True)),
("good", models.BooleanField(default=False)),
("notes", models.CharField(blank=True, max_length=255)),
(
"certification",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="audits",
to="paperwork.certification",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"ordering": ["date"],
"get_latest_by": ["date"],
"abstract": False,
},
),
]

View File

@ -1,13 +1,30 @@
import datetime
import re import re
from semver import VersionInfo from semver import VersionInfo
from django.db import models from django.db import models
from django.db.models import OuterRef, Q, ExpressionWrapper, Subquery from django.db.models import OuterRef, Q, ExpressionWrapper, Subquery
from django.conf import settings
from django.core.validators import RegexValidator from django.core.validators import RegexValidator
from membershipworks.models import Member, Flag as MembershipWorksFlag from membershipworks.models import Member, Flag as MembershipWorksFlag
class AbstractAudit(models.Model):
date = models.DateField(default=datetime.date.today, unique=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
good = models.BooleanField(default=False)
notes = models.CharField(max_length=255, blank=True)
def __str__(self):
return f"{'Good' if self.good else 'Bad'} audit at {self.date} by {self.user}"
class Meta:
abstract = True
ordering = ["date"]
get_latest_by = ["date"]
class CmsRedRiverVeteransScholarship(models.Model): class CmsRedRiverVeteransScholarship(models.Model):
serial = models.AutoField(primary_key=True) serial = models.AutoField(primary_key=True)
program_name = models.CharField(db_column="Program Name", max_length=255) program_name = models.CharField(db_column="Program Name", max_length=255)
@ -202,6 +219,12 @@ class Certification(models.Model):
] ]
class CertificationAudit(AbstractAudit):
certification = models.ForeignKey(
Certification, on_delete=models.CASCADE, related_name="audits"
)
class InstructorOrVendor(models.Model): class InstructorOrVendor(models.Model):
serial = models.AutoField(primary_key=True) serial = models.AutoField(primary_key=True)
name = models.CharField(db_column="Name", max_length=255) name = models.CharField(db_column="Name", max_length=255)
@ -282,3 +305,7 @@ class Waiver(models.Model):
class Meta: class Meta:
db_table = "Waivers" db_table = "Waivers"
class WaiverAudit(AbstractAudit):
waiver = models.ForeignKey(Waiver, on_delete=models.CASCADE, related_name="audits")