Add signals

This commit is contained in:
zandercymatics 2024-03-29 13:06:53 -06:00
parent 9e4b48a84b
commit ed8535b695
No known key found for this signature in database
GPG key ID: FF4636ABEC9682B7
6 changed files with 193 additions and 11 deletions

View file

@ -1095,8 +1095,7 @@ class DomainRequestAdmin(ListHeaderAdmin):
"Type of organization",
{
"fields": [
"generic_org_type",
"is_election_board",
"organization_type",
"federal_type",
"federal_agency",
"tribe_name",

View file

@ -98,6 +98,8 @@ class DomainRequestFixture:
def _set_non_foreign_key_fields(cls, da: DomainRequest, app: dict):
"""Helper method used by `load`."""
da.status = app["status"] if "status" in app else "started"
# TODO for a future ticket: Allow for more than just "federal" here
da.generic_org_type = app["generic_org_type"] if "generic_org_type" in app else "federal"
da.federal_agency = (
app["federal_agency"]
@ -235,9 +237,6 @@ class DomainFixture(DomainRequestFixture):
).last()
logger.debug(f"Approving {domain_request} for {user}")
# We don't want fixtures sending out real emails to
# fake email addresses, so we just skip that and log it instead
# All approvals require an investigator, so if there is none,
# assign one.
if domain_request.investigator is None:

View file

@ -0,0 +1,38 @@
# Generated by Django 4.2.10 on 2024-03-29 15:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("registrar", "0080_create_groups_v09"),
]
operations = [
migrations.AddField(
model_name="domainrequest",
name="organization_type",
field=models.CharField(
blank=True,
choices=[
("federal", "Federal"),
("interstate", "Interstate"),
("state_or_territory", "State or territory"),
("tribal", "Tribal"),
("county", "County"),
("city", "City"),
("special_district", "Special district"),
("school_district", "School district"),
("state_or_territory_election", "State or territory - Election"),
("tribal_election", "Tribal - Election"),
("county_election", "County - Election"),
("city_election", "City - Election"),
("special_district_election", "Special district - Election"),
],
help_text="Type of organization - Election office",
max_length=255,
null=True,
),
),
]

View file

@ -198,7 +198,7 @@ class Domain(TimeStampedModel, DomainHelper):
is called in the validate function on the request/domain page
throws- RegistryError or InvalidDomainError"""
return True
if not cls.string_could_be_domain(domain):
logger.warning("Not a valid domain: %s" % str(domain))
# throw invalid domain error so that it can be caught in

View file

@ -100,8 +100,8 @@ class DomainRequest(TimeStampedModel):
class OrganizationChoices(models.TextChoices):
"""
Primary organization choices:
For use in django admin
Keys need to match OrganizationChoicesVerbose
For use in the request experience
Keys need to match OrganizationChoicesElectionOffice and OrganizationChoicesVerbose
"""
FEDERAL = "federal", "Federal"
@ -113,9 +113,38 @@ class DomainRequest(TimeStampedModel):
SPECIAL_DISTRICT = "special_district", "Special district"
SCHOOL_DISTRICT = "school_district", "School district"
class OrganizationChoicesElectionOffice(models.TextChoices):
"""
Primary organization choices for Django admin:
Keys need to match OrganizationChoices and OrganizationChoicesVerbose.
The enums here come in two variants:
Regular (matches the choices from OrganizationChoices)
Election (Appends " - Election" to the string)
When adding the election variant, you must append "_election" to the end of the string.
"""
# We can't inherit OrganizationChoices due to models.TextChoices being an enum.
# We can redefine these values instead.
FEDERAL = "federal", "Federal"
INTERSTATE = "interstate", "Interstate"
STATE_OR_TERRITORY = "state_or_territory", "State or territory"
TRIBAL = "tribal", "Tribal"
COUNTY = "county", "County"
CITY = "city", "City"
SPECIAL_DISTRICT = "special_district", "Special district"
SCHOOL_DISTRICT = "school_district", "School district"
# Election variants
STATE_OR_TERRITORY_ELECTION = "state_or_territory_election", "State or territory - Election"
TRIBAL_ELECTION = "tribal_election", "Tribal - Election"
COUNTY_ELECTION = "county_election", "County - Election"
CITY_ELECTION = "city_election", "City - Election"
SPECIAL_DISTRICT_ELECTION = "special_district_election", "Special district - Election"
class OrganizationChoicesVerbose(models.TextChoices):
"""
Secondary organization choices
Tertiary organization choices
For use in the domain request form and on the templates
Keys need to match OrganizationChoices
"""
@ -406,6 +435,14 @@ class DomainRequest(TimeStampedModel):
help_text="Type of organization",
)
organization_type = models.CharField(
max_length=255,
choices=OrganizationChoicesElectionOffice.choices,
null=True,
blank=True,
help_text="Type of organization - Election office",
)
federally_recognized_tribe = models.BooleanField(
null=True,
help_text="Is the tribe federally recognized",
@ -449,6 +486,7 @@ class DomainRequest(TimeStampedModel):
help_text="Organization name",
db_index=True,
)
address_line1 = models.CharField(
null=True,
blank=True,
@ -525,6 +563,7 @@ class DomainRequest(TimeStampedModel):
related_name="domain_request",
on_delete=models.PROTECT,
)
alternative_domains = models.ManyToManyField(
"registrar.Website",
blank=True,

View file

@ -1,14 +1,121 @@
import logging
from django.db.models.signals import post_save
from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver
from .models import User, Contact
from .models import User, Contact, DomainRequest
logger = logging.getLogger(__name__)
@receiver(pre_save, sender=DomainRequest)
def create_or_update_organization_type(sender, instance, **kwargs):
"""The organization_type field on DomainRequest is consituted from the
generic_org_type and is_election_board fields. To keep the organization_type
field up to date, we need to update it before save based off of those field
values.
If the instance is marked as an election board and the generic_org_type is not
one of the excluded types (FEDERAL, INTERSTATE, SCHOOL_DISTRICT), the
organization_type is set to a corresponding election variant. Otherwise, it directly
mirrors the generic_org_type value.
"""
if not isinstance(instance, DomainRequest):
# I don't see how this could possibly happen - but its still a good check to have.
# Lets force a fail condition rather than wait for one to happen, if this occurs.
raise ValueError("Type mismatch. The instance was not DomainRequest.")
# == Init variables == #
# We can't grab the election variant if it is in federal, interstate, or school_district.
# The "election variant" is just the org name, with " - Election" appended to the end.
# For example, "School district - Election".
invalid_types = [
DomainRequest.OrganizationChoices.FEDERAL,
DomainRequest.OrganizationChoices.INTERSTATE,
DomainRequest.OrganizationChoices.SCHOOL_DISTRICT,
]
# TODO - maybe we need a check here for .filter then .get
is_new_instance = instance.id is None
# A new record is added with organization_type not defined.
# This happens from the regular domain request flow.
if is_new_instance:
# == Check for invalid conditions before proceeding == #
if instance.organization_type and instance.generic_org_type:
# Since organization type is linked with generic_org_type and election board,
# we have to update one or the other, not both.
raise ValueError("Cannot update organization_type and generic_org_type simultaneously.")
# If no changes occurred, do nothing
if not instance.organization_type and not instance.generic_org_type:
return None
# == Program flow will halt here if there is no reason to update == #
# == Update the linked values == #
# Find out which field needs updating
organization_type_needs_update = instance.organization_type is None
generic_org_type_needs_update = instance.generic_org_type is None
# Update that field
if organization_type_needs_update:
_update_org_type_from_generic_org_and_election(instance, invalid_types)
elif generic_org_type_needs_update:
_update_generic_org_and_election_from_org_type(instance)
else:
# Instance is already in the database, fetch its current state
current_instance = DomainRequest.objects.get(id=instance.id)
# Check the new and old values
generic_org_type_changed = instance.generic_org_type != current_instance.generic_org_type
is_election_board_changed = instance.is_election_board != current_instance.is_election_board
organization_type_changed = instance.organization_type != current_instance.organization_type
# == Check for invalid conditions before proceeding == #
if organization_type_changed and (generic_org_type_changed or is_election_board_changed):
# Since organization type is linked with generic_org_type and election board,
# we have to update one or the other, not both.
raise ValueError("Cannot update organization_type and generic_org_type simultaneously.")
# If no changes occured, do nothing
if not organization_type_changed and (not generic_org_type_changed and not is_election_board_changed):
return None
# == Program flow will halt here if there is no reason to update == #
# == Update the linked values == #
# Find out which field needs updating
organization_type_needs_update = generic_org_type_changed or is_election_board_changed
generic_org_type_needs_update = organization_type_changed
# Update that field
if organization_type_needs_update:
_update_org_type_from_generic_org_and_election(instance, invalid_types)
elif generic_org_type_needs_update:
_update_generic_org_and_election_from_org_type(instance)
def _update_org_type_from_generic_org_and_election(instance, invalid_types):
# TODO handle if generic_org_type is None
if instance.generic_org_type not in invalid_types and instance.is_election_board:
instance.organization_type = f"{instance.generic_org_type}_election"
else:
instance.organization_type = str(instance.generic_org_type)
def _update_generic_org_and_election_from_org_type(instance):
"""Given a value for organization_type, update the
generic_org_type and is_election_board values."""
# TODO find a better solution than this
current_org_type = str(instance.organization_type)
if "_election" in current_org_type:
instance.generic_org_type = current_org_type.split("_election")[0]
instance.is_election_board = True
else:
instance.organization_type = str(instance.generic_org_type)
instance.is_election_board = False
@receiver(post_save, sender=User)
def handle_profile(sender, instance, **kwargs):
"""Method for when a User is saved.