Skip to content

Commit

Permalink
Merge pull request #2190 from cisagov/nl/2136-CISA-rep-additional-det…
Browse files Browse the repository at this point in the history
…ails

Issue #2136 - Update DISA representative fields on Additional Details page
  • Loading branch information
CocoByte authored Jun 13, 2024
2 parents 710f9bd + 9cb3dc5 commit 23ed947
Show file tree
Hide file tree
Showing 13 changed files with 324 additions and 66 deletions.
4 changes: 4 additions & 0 deletions src/registrar/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1500,6 +1500,8 @@ def custom_election_board(self, obj):
"authorizing_official",
"other_contacts",
"no_other_contacts_rationale",
"cisa_representative_first_name",
"cisa_representative_last_name",
"cisa_representative_email",
]
},
Expand Down Expand Up @@ -1575,6 +1577,8 @@ def custom_election_board(self, obj):
"no_other_contacts_rationale",
"anything_else",
"is_policy_acknowledged",
"cisa_representative_first_name",
"cisa_representative_last_name",
"cisa_representative_email",
]
autocomplete_fields = [
Expand Down
19 changes: 13 additions & 6 deletions src/registrar/forms/domain_request_wizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -648,20 +648,27 @@ class NoOtherContactsForm(BaseDeletableRegistrarForm):


class CisaRepresentativeForm(BaseDeletableRegistrarForm):
cisa_representative_first_name = forms.CharField(
label="First name / given name",
error_messages={"required": "Enter the first name / given name of the CISA regional representative."},
)
cisa_representative_last_name = forms.CharField(
label="Last name / family name",
error_messages={"required": "Enter the last name / family name of the CISA regional representative."},
)
cisa_representative_email = forms.EmailField(
required=True,
label="Your representative’s email (optional)",
max_length=None,
label="Your representative’s email",
required=False,
error_messages={
"invalid": ("Enter your representative’s email address in the required format, like name@example.com."),
},
validators=[
MaxLengthValidator(
320,
message="Response must be less than 320 characters.",
)
],
error_messages={
"invalid": ("Enter your email address in the required format, like name@example.com."),
"required": ("Enter the email address of your CISA regional representative."),
},
)


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Generated by Django 4.2.10 on 2024-06-12 20:50

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("registrar", "0100_domainrequest_action_needed_reason"),
]

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="domaininformation",
name="has_anything_else_text",
field=models.BooleanField(
blank=True, help_text="Determines if the user has a anything_else or not", null=True
),
),
migrations.AddField(
model_name="domaininformation",
name="has_cisa_representative",
field=models.BooleanField(
blank=True, help_text="Determines if the user has a representative email or not", null=True
),
),
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"
),
),
]
59 changes: 58 additions & 1 deletion src/registrar/models/domain_information.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,13 +214,45 @@ class Meta:
verbose_name="Additional details",
)

# This is a drop-in replacement for a has_anything_else_text() function.
# In order to track if the user has clicked the yes/no field (while keeping a none default), we need
# a tertiary state. We should not display this in /admin.
has_anything_else_text = models.BooleanField(
null=True,
blank=True,
help_text="Determines if the user has a anything_else or not",
)

cisa_representative_email = models.EmailField(
null=True,
blank=True,
verbose_name="CISA regional representative",
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.
# In order to track if the user has clicked the yes/no field (while keeping a none default), we need
# a tertiary state. We should not display this in /admin.
has_cisa_representative = models.BooleanField(
null=True,
blank=True,
help_text="Determines if the user has a representative email or not",
)

is_policy_acknowledged = models.BooleanField(
null=True,
blank=True,
Expand All @@ -241,6 +273,30 @@ def __str__(self):
except Exception:
return ""

def sync_yes_no_form_fields(self):
"""Some yes/no forms use a db field to track whether it was checked or not.
We handle that here for def save().
"""
# This ensures that if we have prefilled data, the form is prepopulated
if self.cisa_representative_first_name is not None or self.cisa_representative_last_name is not None:
self.has_cisa_representative = (
self.cisa_representative_first_name != "" and self.cisa_representative_last_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 = (
self.cisa_representative_first_name != "" and self.cisa_representative_first_name is not None
) and (self.cisa_representative_last_name != "" and self.cisa_representative_last_name is not None)

# This ensures that if we have prefilled data, the form is prepopulated
if self.anything_else is not None:
self.has_anything_else_text = self.anything_else != ""

# This check is required to ensure that the form doesn't start out checked.
if self.has_anything_else_text is not None:
self.has_anything_else_text = self.anything_else != "" and self.anything_else is not None

def sync_organization_type(self):
"""
Updates the organization_type (without saving) to match
Expand Down Expand Up @@ -275,6 +331,7 @@ def sync_organization_type(self):

def save(self, *args, **kwargs):
"""Save override for custom properties"""
self.sync_yes_no_form_fields()
self.sync_organization_type()
super().save(*args, **kwargs)

Expand Down
51 changes: 36 additions & 15 deletions src/registrar/models/domain_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,10 +477,24 @@ class ActionNeededReasons(models.TextChoices):
cisa_representative_email = models.EmailField(
null=True,
blank=True,
verbose_name="CISA regional representative",
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.
# In order to track if the user has clicked the yes/no field (while keeping a none default), we need
# a tertiary state. We should not display this in /admin.
Expand Down Expand Up @@ -577,16 +591,17 @@ def sync_yes_no_form_fields(self):
"""Some yes/no forms use a db field to track whether it was checked or not.
We handle that here for def save().
"""

# This ensures that if we have prefilled data, the form is prepopulated
if self.cisa_representative_email is not None:
self.has_cisa_representative = self.cisa_representative_email != ""
if self.cisa_representative_first_name is not None or self.cisa_representative_last_name is not None:
self.has_cisa_representative = (
self.cisa_representative_first_name != "" and self.cisa_representative_last_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 = (
self.cisa_representative_email != "" and self.cisa_representative_email is not None
)
self.cisa_representative_first_name != "" and self.cisa_representative_first_name is not None
) and (self.cisa_representative_last_name != "" and self.cisa_representative_last_name is not None)

# This ensures that if we have prefilled data, the form is prepopulated
if self.anything_else is not None:
Expand Down Expand Up @@ -984,11 +999,12 @@ def has_other_contacts(self) -> bool:
def has_additional_details(self) -> bool:
"""Combines the has_anything_else_text and has_cisa_representative fields,
then returns if this domain request has either of them."""

# Split out for linter
has_details = False
if self.has_anything_else_text or self.has_cisa_representative:
has_details = True
has_details = True

if self.has_anything_else_text is None or self.has_cisa_representative is None:
has_details = False
return has_details

def is_federal(self) -> Union[bool, None]:
Expand Down Expand Up @@ -1097,22 +1113,27 @@ def _is_other_contacts_complete(self):
return True
return False

def _cisa_rep_and_email_check(self):
# Has a CISA rep + email is NOT empty or NOT an empty string OR doesn't have CISA rep
return (
def _cisa_rep_check(self):
# Either does not have a CISA rep, OR has a CISA rep + both first name
# and last name are NOT empty and are NOT an empty string
to_return = (
self.has_cisa_representative is True
and self.cisa_representative_email is not None
and self.cisa_representative_email != ""
and self.cisa_representative_first_name is not None
and self.cisa_representative_first_name != ""
and self.cisa_representative_last_name is not None
and self.cisa_representative_last_name != ""
) or self.has_cisa_representative is False

return to_return

def _anything_else_radio_button_and_text_field_check(self):
# Anything else boolean is True + filled text field and it's not an empty string OR the boolean is No
return (
self.has_anything_else_text is True and self.anything_else is not None and self.anything_else != ""
) or self.has_anything_else_text is False

def _is_additional_details_complete(self):
return self._cisa_rep_and_email_check() and self._anything_else_radio_button_and_text_field_check()
return self._cisa_rep_check() and self._anything_else_radio_button_and_text_field_check()

def _is_policy_acknowledgement_complete(self):
return self.is_policy_acknowledged is not None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
{# commented out so it does not appear at this point on this page #}
{% endblock %}

<!-- TODO-NL: (refactor) Breakup into two separate components-->
{% block form_fields %}
<fieldset class="usa-fieldset margin-top-2">
<legend>
Expand All @@ -22,13 +21,13 @@ <h2>Are you working with a CISA regional representative on your domain request?<
{% input_with_errors forms.0.has_cisa_representative %}
{% endwith %}
{# forms.0 is a small yes/no form that toggles the visibility of "cisa representative" formset #}
<!-- TODO-NL: Hookup forms.0 to yes/no form for cisa representative (backend def)-->
</fieldset>

<div id="cisa-representative" class="cisa-representative-form">
<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 #}
<!-- TODO-NL: Hookup forms.1 to cisa representative form (backend def) -->
</div>


Expand All @@ -42,14 +41,12 @@ <h2>Is there anything else you’d like us to know about your domain request?</h
{% input_with_errors forms.2.has_anything_else_text %}
{% endwith %}
{# forms.2 is a small yes/no form that toggles the visibility of "cisa representative" formset #}
<!-- TODO-NL: Hookup forms.2 to yes/no form for anything else form (backend def)-->
</fieldset>

<div id="anything-else">
{% with attr_maxlength=2000 add_label_class="usa-sr-only" %}
{% input_with_errors forms.3.anything_else %}
{% endwith %}
{# forms.3 is a form for inputting the e-mail of a cisa representative #}
<!-- TODO-NL: Hookup forms.3 to anything else form (backend def) -->
</div>
{% endblock %}
37 changes: 26 additions & 11 deletions src/registrar/templates/domain_request_review.html
Original file line number Diff line number Diff line change
Expand Up @@ -157,18 +157,33 @@ <h3 class="register-form-review-header">Alternative domains</h3>

{% 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:"<span class='text-bold text-secondary-dark'>Incomplete</span>"|safe %}
{% include "includes/summary_item.html" with title=title sub_header_text='CISA regional representative' value=domain_request.cisa_representative_email heading_level=heading_level editable=True edit_link=domain_request_url custom_text_for_value_none='No' %}
{% with title=form_titles|get_item:step %}
{% if domain_request.has_additional_details %}
{% include "includes/summary_item.html" with title="Additional Details" 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 %}
<li>{{domain_request.cisa_representative_first_name}} {{domain_request.cisa_representative_last_name}}</li>
{% if domain_request.cisa_representative_email %}
<li>{{domain_request.cisa_representative_email}}</li>
{% endif %}
{% else %}
No
{% endif %}
</ul>

<h3 class="register-form-review-header">Anything else</h3>
<ul class="usa-list usa-list--unstyled margin-top-0">
{% if domain_request.anything_else %}
{{domain_request.anything_else}}
{% else %}
No
{% endif %}
</ul>
{% else %}
{% include "includes/summary_item.html" with title="Additional Details" value="<span class='text-bold text-secondary-dark'>Incomplete</span>"|safe heading_level=heading_level editable=True edit_link=domain_request_url %}
{% endif %}
{% endwith %}

<h3 class="register-form-review-header">Anything else</h3>
<ul class="usa-list usa-list--unstyled margin-top-0">
{% if domain_request.anything_else %}
{{domain_request.anything_else}}
{% else %}
No
{% endif %}
</ul>
{% endif %}


Expand Down
Loading

0 comments on commit 23ed947

Please sign in to comment.