Refactored storage of CISA rep info to NOT use Contacts model

This commit is contained in:
CocoByte 2024-05-31 15:39:40 -06:00
parent 9fecf11558
commit 8af8a8c670
No known key found for this signature in database
GPG key ID: BBFAA2526384C97F
12 changed files with 164 additions and 132 deletions

View file

@ -1376,7 +1376,9 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportModelAdmin):
"authorizing_official",
"other_contacts",
"no_other_contacts_rationale",
"cisa_representative",
"cisa_representative_first_name",
"cisa_representative_last_name",
"cisa_representative_email",
]
},
),
@ -1452,7 +1454,9 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportModelAdmin):
"no_other_contacts_rationale",
"anything_else",
"is_policy_acknowledged",
"cisa_representative",
"cisa_representative_first_name",
"cisa_representative_last_name",
"cisa_representative_email",
]
autocomplete_fields = [
"approved_domain",

View file

@ -647,47 +647,20 @@ class NoOtherContactsForm(BaseDeletableRegistrarForm):
class CisaRepresentativeForm(BaseDeletableRegistrarForm):
JOIN = "cisa_representative"
def to_database(self, obj):
if not self.is_valid():
return
contact = getattr(obj, "cisa_representative", None)
if contact is not None and not contact.has_more_than_one_join("cisa_representative_domain_requests"):
if self.form_data_marked_for_deletion:
# remove the CISA contact from this domain request
obj.cisa_representative = None
# QUESTION - should we also delete the contact object if it is not joined to other entities?
else:
# update existing contact if it is not joined to other enttities
super().to_database(contact)
else:
# no contact exists OR contact exists which is joined also to other entities;
# in either case, create a new contact and update it
contact = Contact()
super().to_database(contact)
obj.cisa_representative = contact
obj.save()
@classmethod
def from_database(cls, obj):
contact = getattr(obj, "cisa_representative", None)
return super().from_database(contact)
first_name = forms.CharField(
cisa_representative_first_name = forms.CharField(
label="First name / given name",
error_messages={"required": "Enter your first name / given name."},
)
last_name = forms.CharField(
cisa_representative_last_name = forms.CharField(
label="Last name / family name",
error_messages={"required": "Enter your last name / family name."},
)
email = forms.EmailField(
label="Email",
cisa_representative_email = forms.EmailField(
label="Your representatives email (optional)",
max_length=None,
required=False,
error_messages={
"invalid": ("Enter your email address in the required format, like name@example.com."),
"invalid": ("Enter your representatives email address in the required format, like name@example.com."),
},
validators=[
MaxLengthValidator(
@ -698,6 +671,7 @@ class CisaRepresentativeForm(BaseDeletableRegistrarForm):
)
class CisaRepresentativeYesNoForm(BaseYesNoForm):
"""Yes/no toggle for the CISA regions question on additional details"""

View file

@ -0,0 +1,55 @@
# Generated by Django 4.2.10 on 2024-05-31 21:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("registrar", "0094_create_groups_v12"),
]
operations = [
migrations.AddField(
model_name="domaininformation",
name="cisa_representative_first_name",
field=models.CharField(
blank=True, db_index=True, null=True, verbose_name="CISA regional representative first name"
),
),
migrations.AddField(
model_name="domaininformation",
name="cisa_representative_last_name",
field=models.CharField(
blank=True, db_index=True, null=True, verbose_name="CISA regional representative last name"
),
),
migrations.AddField(
model_name="domainrequest",
name="cisa_representative_first_name",
field=models.CharField(
blank=True, db_index=True, null=True, verbose_name="CISA regional representative first name"
),
),
migrations.AddField(
model_name="domainrequest",
name="cisa_representative_last_name",
field=models.CharField(
blank=True, db_index=True, null=True, verbose_name="CISA regional representative last name"
),
),
migrations.AlterField(
model_name="domaininformation",
name="cisa_representative_email",
field=models.EmailField(
blank=True, max_length=320, null=True, verbose_name="CISA regional representative email"
),
),
migrations.AlterField(
model_name="domainrequest",
name="cisa_representative_email",
field=models.EmailField(
blank=True, max_length=320, null=True, verbose_name="CISA regional representative email"
),
),
]

View file

@ -1,46 +0,0 @@
# Generated by Django 4.2.10 on 2024-05-16 23:08
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("registrar", "0094_create_groups_v12"),
]
operations = [
migrations.RemoveField(
model_name="domaininformation",
name="cisa_representative_email",
),
migrations.RemoveField(
model_name="domainrequest",
name="cisa_representative_email",
),
migrations.AddField(
model_name="domaininformation",
name="cisa_representative",
field=models.ForeignKey(
blank=True,
help_text='Cisa Representative listed under "additional information" in the request form',
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="cisa_representative_domain_requests_information",
to="registrar.contact",
),
),
migrations.AddField(
model_name="domainrequest",
name="cisa_representative",
field=models.ForeignKey(
blank=True,
help_text='Cisa Representative listed under "additional information" in the request form',
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="cisa_representative_domain_requests",
to="registrar.contact",
),
),
]

View file

@ -206,13 +206,25 @@ class DomainInformation(TimeStampedModel):
verbose_name="Additional details",
)
cisa_representative = models.ForeignKey(
"registrar.Contact",
cisa_representative_email = models.EmailField(
null=True,
blank=True,
related_name="cisa_representative_domain_requests_information",
on_delete=models.PROTECT,
help_text='Cisa Representative listed under "additional information" in the request form',
verbose_name="CISA regional representative email",
max_length=320,
)
cisa_representative_first_name = models.CharField(
null=True,
blank=True,
verbose_name="CISA regional representative first name",
db_index=True,
)
cisa_representative_last_name = models.CharField(
null=True,
blank=True,
verbose_name="CISA regional representative last name",
db_index=True,
)
is_policy_acknowledged = models.BooleanField(

View file

@ -457,13 +457,25 @@ class DomainRequest(TimeStampedModel):
help_text="Determines if the user has a anything_else or not",
)
cisa_representative = models.ForeignKey(
"registrar.Contact",
cisa_representative_email = models.EmailField(
null=True,
blank=True,
related_name="cisa_representative_domain_requests",
on_delete=models.PROTECT,
help_text='Cisa Representative listed under "additional information" in the request form',
verbose_name="CISA regional representative email",
max_length=320,
)
cisa_representative_first_name = models.CharField(
null=True,
blank=True,
verbose_name="CISA regional representative first name",
db_index=True,
)
cisa_representative_last_name = models.CharField(
null=True,
blank=True,
verbose_name="CISA regional representative last name",
db_index=True,
)
# This is a drop-in replacement for an has_cisa_representative() function.
@ -536,17 +548,18 @@ class DomainRequest(TimeStampedModel):
We handle that here for def save().
"""
cisa_rep_is_not_none = self.cisa_representative is not None
cisa_first_name = None
# This ensures that if we have prefilled data, the form is prepopulated
if cisa_rep_is_not_none:
cisa_first_name = self.cisa_representative.first_name
self.has_cisa_representative = cisa_first_name is not None and cisa_first_name != ""
# NOTE: this relies on the fact that the first and last names of a CISA representative
# are required fields. Because of this, we can simplify the check to only look at the
# first name to determine whether or not a CISA representative was provided.
if self.cisa_representative_first_name is not None:
self.has_cisa_representative = self.cisa_representative_first_name != ""
# This check is required to ensure that the form doesn't start out checked
if self.has_cisa_representative is not None:
self.has_cisa_representative = cisa_first_name is not None and cisa_first_name != ""
self.has_cisa_representative = (
self.cisa_representative_first_name != "" and self.cisa_representative_first_name is not None
)
# This ensures that if we have prefilled data, the form is prepopulated
if self.anything_else is not None:

View file

@ -23,10 +23,10 @@
{# forms.0 is a small yes/no form that toggles the visibility of "cisa representative" formset #}
</fieldset>
<div id="cisa-representative" class="cisa-representative-form">
{% input_with_errors forms.1.first_name %}
{% input_with_errors forms.1.last_name %}
{% input_with_errors forms.1.email %}
<div id="cisa-representative" class="cisa-representative-form margin-top-3">
{% input_with_errors forms.1.cisa_representative_first_name %}
{% input_with_errors forms.1.cisa_representative_last_name %}
{% input_with_errors forms.1.cisa_representative_email %}
{# forms.1 is a form for inputting the e-mail of a cisa representative #}
</div>

View file

@ -157,8 +157,16 @@
{% if step == Step.ADDITIONAL_DETAILS %}
{% namespaced_url 'domain-request' step as domain_request_url %}
{% with title=form_titles|get_item:step value=domain_request.requested_domain.name|default:"Incomplete" %}
{% include "includes/summary_item.html" with title=title sub_header_text='CISA regional representative' value=domain_request.cisa_representative contact='true' heading_level=heading_level editable=True edit_link=domain_request_url custom_text_for_value_none='No' %}
{% with title=form_titles|get_item:step value=domain_request.requested_domain.has_additional_details|default:"Incomplete" %}
{% include "includes/summary_item.html" with title="Additional Details" value=value heading_level=heading_level editable=True edit_link=domain_request_url %}
<h3 class="register-form-review-header">CISA Regional Representative</h3>
<ul class="usa-list usa-list--unstyled margin-top-0">
{% if domain_request.cisa_representative_first_name %}
{{domain_request.cisa_representative_first_name}} {{domain_request.cisa_representative_last_name}}
{% else %}
No
{% endif %}
</ul>
{% endwith %}
<h3 class="register-form-review-header">Anything else</h3>

View file

@ -118,7 +118,15 @@
{# We always show this field even if None #}
{% if DomainRequest %}
{% include "includes/summary_item.html" with title='Additional details' sub_header_text='CISA regional representative' value=DomainRequest.cisa_representative contact='true' custom_text_for_value_none='No' heading_level=heading_level %}
<h3 class="register-form-review-header">CISA Regional Representative</h3>
<ul class="usa-list usa-list--unstyled margin-top-0">
{% if domain_request.cisa_representative_first_name %}
{{domain_request.cisa_representative_first_name}} {{domain_request.cisa_representative_last_name}}
{% else %}
No
{% endif %}
</ul>
<h3 class="register-form-review-header">Anything else</h3>
<ul class="usa-list usa-list--unstyled margin-top-0">
{% if DomainRequest.anything_else %}
@ -128,7 +136,6 @@
{% endif %}
</ul>
{% endif %}
{% endwith %}
</div>

View file

@ -895,12 +895,9 @@ def completed_domain_request( # noqa
if has_alternative_gov_domain:
domain_request.alternative_domains.add(alt)
if has_cisa_representative:
cisa_representative, _ = Contact.objects.get_or_create(
first_name="CISA-first-name",
last_name="CISA-last-name",
email="cisaRep@igorville.gov",
)
domain_request.cisa_representative = cisa_representative
domain_request.cisa_representative_first_name="CISA-first-name"
domain_request.cisa_representative_last_name="CISA-last-name"
domain_request.cisa_representative_email="cisaRep@igorville.gov"
return domain_request

View file

@ -2263,7 +2263,9 @@ class TestDomainRequestAdmin(MockEppLib):
"no_other_contacts_rationale",
"anything_else",
"has_anything_else_text",
"cisa_representative",
"cisa_representative_first_name",
"cisa_representative_last_name,"
"cisa_representative_email",
"has_cisa_representative",
"is_policy_acknowledged",
"submission_date",
@ -2296,7 +2298,9 @@ class TestDomainRequestAdmin(MockEppLib):
"no_other_contacts_rationale",
"anything_else",
"is_policy_acknowledged",
"cisa_representative",
"cisa_representative_first_name",
"cisa_representative_last_name,"
"cisa_representative_email",
]
self.assertEqual(readonly_fields, expected_fields)

View file

@ -379,9 +379,9 @@ class DomainRequestTests(TestWithUser, WebTest):
additional_details_result = additional_details_form.submit()
# validate that data from this step are being saved
domain_request = DomainRequest.objects.get() # there's only one
self.assertEqual(domain_request.cisa_representative.first_name, "CISA-first-name")
self.assertEqual(domain_request.cisa_representative.last_name, "CISA-last-name")
self.assertEqual(domain_request.cisa_representative.email, "FakeEmail@gmail.com")
self.assertEqual(domain_request.cisa_representative_first_name, "CISA-first-name")
self.assertEqual(domain_request.cisa_representative_last_name, "CISA-last-name")
self.assertEqual(domain_request.cisa_representative_email, "FakeEmail@gmail.com")
self.assertEqual(domain_request.anything_else, "Nothing else.")
# the post request should return a redirect to the next form in
# the domain request page
@ -815,7 +815,7 @@ class DomainRequestTests(TestWithUser, WebTest):
def test_yes_no_form_inits_yes_for_cisa_representative_and_anything_else(self):
"""On the Additional Details page, the yes/no form gets initialized with YES selected
for both yes/no radios if the domain request has a value for cisa_representative and
for both yes/no radios if the domain request has a values for cisa_representative_first_name and
anything_else"""
domain_request = completed_domain_request(user=self.user, has_anything_else=True, has_cisa_representative=True)
@ -905,16 +905,16 @@ class DomainRequestTests(TestWithUser, WebTest):
"""When a user submits the Additional Details form with no selected for all fields,
the domain request's data gets wiped when submitted"""
domain_request = completed_domain_request(name="nocisareps.gov", user=self.user)
domain_request.cisa_representative.first_name = "cisa-firstname1"
domain_request.cisa_representative.last_name = "cisa-lastname1"
domain_request.cisa_representative.email = "fake@faketown.gov"
domain_request.cisa_representative_first_name = "cisa-firstname1"
domain_request.cisa_representative_last_name = "cisa-lastname1"
domain_request.cisa_representative_email = "fake@faketown.gov"
domain_request.save()
# Make sure we have the data we need for the test
self.assertEqual(domain_request.anything_else, "There is more")
self.assertEqual(domain_request.cisa_representative.first_name, "cisa-firstname1")
self.assertEqual(domain_request.cisa_representative.last_name, "cisa-lastname1")
self.assertEqual(domain_request.cisa_representative.email, "fake@faketown.gov")
self.assertEqual(domain_request.cisa_representative_first_name, "cisa-firstname1")
self.assertEqual(domain_request.cisa_representative_last_name, "cisa-lastname1")
self.assertEqual(domain_request.cisa_representative_email, "fake@faketown.gov")
# prime the form by visiting /edit
self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk}))
@ -947,16 +947,20 @@ class DomainRequestTests(TestWithUser, WebTest):
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# Verify that the anything_else and cisa_representative have been deleted from the DB
# Verify that the anything_else and cisa_representative information have been deleted from the DB
domain_request = DomainRequest.objects.get(requested_domain__name="nocisareps.gov")
# Check that our data has been cleared
self.assertEqual(domain_request.anything_else, None)
self.assertEqual(domain_request.cisa_representative, None)
self.assertEqual(domain_request.cisa_representative_first_name, None)
self.assertEqual(domain_request.cisa_representative_last_name, None)
self.assertEqual(domain_request.cisa_representative_email, None)
# Double check the yes/no fields
self.assertEqual(domain_request.has_anything_else_text, False)
self.assertEqual(domain_request.has_cisa_representative, False)
self.assertEqual(domain_request.cisa_representative_first_name, None)
self.assertEqual(domain_request.cisa_representative_last_name, None)
self.assertEqual(domain_request.cisa_representative_email, None)
def test_submitting_additional_details_populates_cisa_representative_and_anything_else(self):
"""When a user submits the Additional Details form,
@ -967,7 +971,7 @@ class DomainRequestTests(TestWithUser, WebTest):
# Make sure we have the data we need for the test
self.assertEqual(domain_request.anything_else, None)
self.assertEqual(domain_request.cisa_representative, None)
self.assertEqual(domain_request.cisa_representative_first_name, None)
# These fields should not be selected at all, since we haven't initialized the form yet
self.assertEqual(domain_request.has_anything_else_text, None)
@ -1000,13 +1004,13 @@ class DomainRequestTests(TestWithUser, WebTest):
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# Verify that the anything_else and cisa_representative exist in the db
# Verify that the anything_else and cisa_representative information exist in the db
domain_request = DomainRequest.objects.get(requested_domain__name="cisareps.gov")
self.assertEqual(domain_request.anything_else, "redandblue")
self.assertEqual(domain_request.cisa_representative.first_name, "cisa-firstname")
self.assertEqual(domain_request.cisa_representative.last_name, "cisa-lastname")
self.assertEqual(domain_request.cisa_representative.email, "test@faketest.gov")
self.assertEqual(domain_request.cisa_representative_first_name, "cisa-firstname")
self.assertEqual(domain_request.cisa_representative_last_name, "cisa-lastname")
self.assertEqual(domain_request.cisa_representative_email, "test@faketest.gov")
self.assertEqual(domain_request.has_cisa_representative, True)
self.assertEqual(domain_request.has_anything_else_text, True)