From 3b61061e4c20a25d43cc9fc4e102d67c7ea4249d Mon Sep 17 00:00:00 2001 From: Adam Goldsmith Date: Sat, 1 Apr 2023 11:37:49 -0400 Subject: [PATCH] paperwork: Add two stage definition/version selection in Certification admin --- paperwork/admin.py | 2 + paperwork/autocomplete_views.py | 42 +++++++++++++++ paperwork/forms.py | 51 +++++++++++++++++++ .../certification-form-clear-autocomplete.js | 10 ++++ paperwork/urls.py | 6 +++ 5 files changed, 111 insertions(+) create mode 100644 paperwork/autocomplete_views.py create mode 100644 paperwork/forms.py create mode 100644 paperwork/static/paperwork/certification-form-clear-autocomplete.js diff --git a/paperwork/admin.py b/paperwork/admin.py index b0ac9b6..ebc6f10 100644 --- a/paperwork/admin.py +++ b/paperwork/admin.py @@ -13,6 +13,7 @@ from .models import ( SpecialProgram, Waiver, ) +from .forms import CertificationForm from .certification_emails import all_certification_emails @@ -65,6 +66,7 @@ class CertificationDefinitionAdmin(admin.ModelAdmin): @admin.register(Certification) class CertificationAdmin(admin.ModelAdmin): + form = CertificationForm search_fields = [ "name", "certification_version__definition__certification_name", diff --git a/paperwork/autocomplete_views.py b/paperwork/autocomplete_views.py new file mode 100644 index 0000000..b9bdc73 --- /dev/null +++ b/paperwork/autocomplete_views.py @@ -0,0 +1,42 @@ +from dal import autocomplete +from django.db.models.functions import Concat +from django.db.models import Q, Value, CharField + +from .models import CertificationVersion + + +class CertificationVersionAutocomplete(autocomplete.Select2QuerySetView): + def get_queryset(self): + if not self.request.user.has_perm("membershipworks.view_certification_version"): + return CertificationVersion.objects.none() + + qs = CertificationVersion.objects.alias( + version_str=Concat( + "major", + Value("."), + "minor", + Value("."), + "patch", + output_field=CharField(), + ), + ) + + certification_definition = self.forwarded.get("certification_definition", None) + + if certification_definition: + qs = qs.filter(definition=certification_definition) + + if self.q: + qs = qs.filter( + Q(version_str__istartswith=self.q) + | Q(prerelease__istartswith=self.q) + | Q(approval_date__istartswith=self.q) + ) + + return qs + + def get_result_label(self, result: CertificationVersion) -> str: + return str(result.semantic_version()) + + def get_selected_result_label(self, result: CertificationVersion) -> str: + return str(result) diff --git a/paperwork/forms.py b/paperwork/forms.py new file mode 100644 index 0000000..5869b18 --- /dev/null +++ b/paperwork/forms.py @@ -0,0 +1,51 @@ +from dal import autocomplete + +from django import forms +from django.core.exceptions import ValidationError + +from .models import CertificationDefinition, Certification + + +class CertificationForm(forms.ModelForm): + certification_definition = forms.ModelChoiceField( + queryset=CertificationDefinition.objects.all() + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + instance = kwargs.get("instance") + if instance: + self.fields[ + "certification_definition" + ].initial = instance.certification_version.definition + + def clean(self): + cleaned_data = super().clean() + certification_version = cleaned_data.get("certification_version") + certification_definition = cleaned_data.get("certification_definition") + + if certification_version.definition != certification_definition: + raise ValidationError( + "Certification Version did not match Certification Definition!" + ) + + class Media: + js = ("paperwork/certification-form-clear-autocomplete.js",) + + class Meta: + model = Certification + fields = [ + "certification_definition", + "certification_version", + "name", + "member", + "certified_by", + "date", + "notes", + ] + widgets = { + "certification_version": autocomplete.ModelSelect2( + url="paperwork:certification_version_autocomplete", + forward=["certification_definition"], + ) + } diff --git a/paperwork/static/paperwork/certification-form-clear-autocomplete.js b/paperwork/static/paperwork/certification-form-clear-autocomplete.js new file mode 100644 index 0000000..2bd2f34 --- /dev/null +++ b/paperwork/static/paperwork/certification-form-clear-autocomplete.js @@ -0,0 +1,10 @@ +window.addEventListener('load', function() { + // Bind on certification_definition field change + django.jQuery(':input[name$=certification_definition]').on('change', function() { + // Get the field prefix, ie. if this comes from a formset form + var prefix = django.jQuery(this).getFormPrefix(); + + // Clear the autocomplete with the same prefix + django.jQuery(':input[name=' + prefix + 'certification_version]').val(null).trigger('change'); + }); +}); diff --git a/paperwork/urls.py b/paperwork/urls.py index 0acd7ac..f23531f 100644 --- a/paperwork/urls.py +++ b/paperwork/urls.py @@ -1,6 +1,7 @@ from django.urls import path from . import views +from . import autocomplete_views app_name = "paperwork" @@ -20,4 +21,9 @@ urlpatterns = [ views.certification_pdf, name="certification_pdf", ), + path( + "certifications/version_autocomplete", + autocomplete_views.CertificationVersionAutocomplete.as_view(), + name="certification_version_autocomplete", + ), ]