diff --git a/paperwork/admin.py b/paperwork/admin.py index c7e063f..249ef42 100644 --- a/paperwork/admin.py +++ b/paperwork/admin.py @@ -4,6 +4,7 @@ from django.db.models.functions import Now from .models import ( CmsRedRiverVeteransScholarship, + Department, CertificationDefinition, Certification, CertificationVersion, @@ -14,6 +15,12 @@ from .models import ( from .certification_emails import all_certification_emails +@admin.register(Department) +class Department(admin.ModelAdmin): + search_fields = ["name"] + list_display = ["name", "parent", "list_moderator_flag", "list_reply_to_address"] + + class CertificationVersionInline(admin.TabularInline): model = CertificationVersion extra = 1 diff --git a/paperwork/api.py b/paperwork/api.py index 4ae81eb..a2fc114 100644 --- a/paperwork/api.py +++ b/paperwork/api.py @@ -4,7 +4,23 @@ from rest_framework.decorators import action from rest_framework.response import Response from membershipworks.models import Member -from .models import Certification, CertificationDefinition, CertificationVersion +from .models import ( + Department, + Certification, + CertificationDefinition, + CertificationVersion, +) + + +class DepartmentSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = Department + fields = ["name", "parent", "list_moderator_flag", "list_reply_to_address"] + + +class DepartmentViewSet(viewsets.ModelViewSet): + queryset = Department.objects.all() + serializer_class = DepartmentSerializer class CertificationDefinitionSerializer(serializers.HyperlinkedModelSerializer): @@ -75,6 +91,7 @@ class CertificationViewSet(viewsets.ModelViewSet): router = routers.DefaultRouter() +router.register(r"paperwork/department", DepartmentViewSet) router.register(r"paperwork/certification_definition", CertificationDefinitionViewSet) router.register(r"paperwork/certification_version", CertificationVersionViewSet) router.register(r"paperwork/certification", CertificationViewSet) diff --git a/paperwork/migrations/0006_department_alter_certificationdefinition_department.py b/paperwork/migrations/0006_department_alter_certificationdefinition_department.py new file mode 100644 index 0000000..e925e17 --- /dev/null +++ b/paperwork/migrations/0006_department_alter_certificationdefinition_department.py @@ -0,0 +1,105 @@ +# Generated by Django 4.1.3 on 2023-01-18 03:31 + +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion + + +def link_departments(apps, schema_editor): + CertificationDefinition = apps.get_model("paperwork", "CertificationDefinition") + Department = apps.get_model("paperwork", "Department") + for certification_definition in CertificationDefinition.objects.all(): + department, created = Department.objects.get_or_create( + name=certification_definition.old_department + ) + certification_definition.department = department + certification_definition.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ("membershipworks", "0001_initial"), + ("paperwork", "0005_certificationdefinition_mailing_list"), + ] + + operations = [ + migrations.CreateModel( + name="Department", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField( + help_text="This will also be used to generate the mailing list name", + max_length=64, + validators=[ + django.core.validators.RegexValidator("^[-_ A-Za-z0-9]*$") + ], + ), + ), + ( + "list_reply_to_address", + models.EmailField(blank=True, max_length=254), + ), + ( + "list_moderator_flag", + models.ForeignKey( + blank=True, + null=True, + db_constraint=False, + on_delete=django.db.models.deletion.PROTECT, + to="membershipworks.flag", + ), + ), + ( + "parent", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="children", + to="paperwork.department", + ), + ), + ], + ), + # rename the old field so that we can reuse the name + migrations.RenameField( + model_name="certificationdefinition", + old_name="department", + new_name="old_department", + ), + # Create nullable foreign key to department + migrations.AddField( + model_name="certificationdefinition", + name="department", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + to="paperwork.department", + null=True, + ), + ), + # create new Departments and link them to Certification Definitions + migrations.RunPython(link_departments, atomic=True), + # make department not nullable + migrations.AlterField( + model_name="certificationdefinition", + name="department", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, to="paperwork.department" + ), + ), + migrations.RemoveField( + model_name="certificationdefinition", + name="old_department", + ), + ] diff --git a/paperwork/models.py b/paperwork/models.py index 1748958..7078929 100644 --- a/paperwork/models.py +++ b/paperwork/models.py @@ -1,8 +1,10 @@ -from django.db import models import re -import semver -from membershipworks.models import Member +import semver +from django.db import models +from django.core.validators import RegexValidator + +from membershipworks.models import Member, Flag as MembershipWorksFlag VALID_SEMVER_PATTERN = re.compile( r"(?P\d+\.\d+\.\d+) - (?P\d{4}-\d{2}-\d{2})" @@ -48,6 +50,32 @@ class CmsRedRiverVeteransScholarship(models.Model): db_table = "CMS Red River Veterans Scholarship" +class Department(models.Model): + name = models.CharField( + max_length=64, + validators=[RegexValidator("^[-_ A-Za-z0-9]*$")], + help_text="This will also be used to generate the mailing list name", + ) + parent = models.ForeignKey( + "self", on_delete=models.PROTECT, related_name="children", blank=True, null=True + ) + list_moderator_flag = models.ForeignKey( + MembershipWorksFlag, + on_delete=models.PROTECT, + blank=True, + null=True, + db_constraint=False, + ) + list_reply_to_address = models.EmailField(max_length=254, blank=True) + + def __str__(self): + return self.name + + @property + def list_name(self): + return self.name.replace(" ", "_") + "-info" + + class CertificationDefinition(models.Model): certification_identifier = models.AutoField( db_column="Certification Identifier", primary_key=True @@ -55,9 +83,7 @@ class CertificationDefinition(models.Model): certification_name = models.CharField( db_column="Certification Name", max_length=255, blank=True, null=True ) - department = models.CharField( - db_column="Department", max_length=255, blank=True, null=True - ) + department = models.ForeignKey(Department, models.PROTECT) mailing_list = models.CharField(max_length=128, blank=True) def __str__(self):