From 8f27aa101044bf5024e2875a3181ccbb1adcd7e6 Mon Sep 17 00:00:00 2001 From: CocoByte Date: Wed, 10 Apr 2024 16:20:57 -0600 Subject: [PATCH 01/85] Updated Anything Else page to Additional Details page --- src/registrar/assets/js/get-gov-admin.js | 2 +- src/registrar/assets/js/get-gov.js | 123 +++++++++++------- src/registrar/config/urls.py | 2 +- src/registrar/forms/domain_request_wizard.py | 91 ++++++++++--- ...tion_cisa_representative_email_and_more.py | 23 ++++ src/registrar/models/domain_information.py | 6 + src/registrar/models/domain_request.py | 14 ++ .../domain_request_additional_details.html | 56 ++++++++ src/registrar/tests/test_forms.py | 4 +- src/registrar/views/domain_request.py | 64 ++++++++- 10 files changed, 314 insertions(+), 71 deletions(-) create mode 100644 src/registrar/migrations/0082_domaininformation_cisa_representative_email_and_more.py create mode 100644 src/registrar/templates/domain_request_additional_details.html diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js index 9a92542b1..0ecec92c6 100644 --- a/src/registrar/assets/js/get-gov-admin.js +++ b/src/registrar/assets/js/get-gov-admin.js @@ -369,7 +369,7 @@ function enableRelatedWidgetButtons(changeLink, deleteLink, viewLink, elementPk, } /** An IIFE for admin in DjangoAdmin to listen to changes on the domain request - * status select amd to show/hide the rejection reason + * status select and to show/hide the rejection reason */ (function (){ let rejectionReasonFormGroup = document.querySelector('.field-rejection_reason') diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js index 587b95305..a8e6008ef 100644 --- a/src/registrar/assets/js/get-gov.js +++ b/src/registrar/assets/js/get-gov.js @@ -193,6 +193,65 @@ function clearValidators(el) { toggleInputValidity(el, true); } +/** Hookup listeners for yes/no togglers for form fields + * Parameters: + * - radioButtonName: The "name=" value for the radio buttons being used as togglers + * - elementIdToShowIfYes: The Id of the element (eg. a div) to show if selected value of the given + * radio button is true (hides this element if false) + * - elementIdToShowIfNo: The Id of the element (eg. a div) to show if selected value of the given + * radio button is false (hides this element if true) + * **/ +function HookupYesNoListener(radioButtonName, elementIdToShowIfYes, elementIdToShowIfNo) { + // Get the radio buttons + let radioButtons = document.querySelectorAll('input[name="'+radioButtonName+'"]'); + + function handleRadioButtonChange() { + // Check the value of the selected radio button + // Attempt to find the radio button element that is checked + let radioButtonChecked = document.querySelector('input[name="'+radioButtonName+'"]:checked'); + + // Check if the element exists before accessing its value + let selectedValue = radioButtonChecked ? radioButtonChecked.value : null; + + switch (selectedValue) { + case 'True': + toggleTwoDomElements(elementIdToShowIfYes, elementIdToShowIfNo, 1); + break; + + case 'False': + toggleTwoDomElements(elementIdToShowIfYes, elementIdToShowIfNo, 2); + break; + + default: + toggleTwoDomElements(elementIdToShowIfYes, elementIdToShowIfNo, 0); + } + } + + if (radioButtons.length) { + // Add event listener to each radio button + radioButtons.forEach(function (radioButton) { + radioButton.addEventListener('change', handleRadioButtonChange); + }); + + // initialize + handleRadioButtonChange(); + } +} + +// A generic display none/block toggle function that takes an integer param to indicate how the elements toggle +function toggleTwoDomElements(ele1, ele2, index) { + let element1 = document.getElementById(ele1); + let element2 = document.getElementById(ele2); + if (element1 || element2) { + // Toggle display based on the index + if (element1) {element1.style.display = index === 1 ? 'block' : 'none';} + if (element2) {element2.style.display = index === 2 ? 'block' : 'none';} + } + else { + console.error('Unable to find elements to toggle'); + } +} + // <<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>> // Event handlers. @@ -712,57 +771,29 @@ function hideDeletedForms() { } })(); -// A generic display none/block toggle function that takes an integer param to indicate how the elements toggle -function toggleTwoDomElements(ele1, ele2, index) { - let element1 = document.getElementById(ele1); - let element2 = document.getElementById(ele2); - if (element1 && element2) { - // Toggle display based on the index - element1.style.display = index === 1 ? 'block' : 'none'; - element2.style.display = index === 2 ? 'block' : 'none'; - } else { - console.error('One or both elements not found.'); - } -} /** * An IIFE that listens to the other contacts radio form on DAs and toggles the contacts/no other contacts forms * */ (function otherContactsFormListener() { - // Get the radio buttons - let radioButtons = document.querySelectorAll('input[name="other_contacts-has_other_contacts"]'); - - function handleRadioButtonChange() { - // Check the value of the selected radio button - // Attempt to find the radio button element that is checked - let radioButtonChecked = document.querySelector('input[name="other_contacts-has_other_contacts"]:checked'); - - // Check if the element exists before accessing its value - let selectedValue = radioButtonChecked ? radioButtonChecked.value : null; - - switch (selectedValue) { - case 'True': - toggleTwoDomElements('other-employees', 'no-other-employees', 1); - break; - - case 'False': - toggleTwoDomElements('other-employees', 'no-other-employees', 2); - break; - - default: - toggleTwoDomElements('other-employees', 'no-other-employees', 0); - } - } - - if (radioButtons.length) { - // Add event listener to each radio button - radioButtons.forEach(function (radioButton) { - radioButton.addEventListener('change', handleRadioButtonChange); - }); - - // initialize - handleRadioButtonChange(); - } + HookupYesNoListener("other_contacts-has_other_contacts",'other-employees', 'no-other-employees') })(); + +/** + * An IIFE that listens to the yes/no radio buttons on the anything else form and toggles form field visibility accordingly + * + */ +(function anythingElseFormListener() { + HookupYesNoListener("anything_else-has_anything_else_text",'anything-else', null) +})(); + + +/** + * An IIFE that listens to the yes/no radio buttons on the CISA representatives form and toggles form field visibility accordingly + * + */ +(function cisaRepresentativesFormListener() { + HookupYesNoListener("anything_else-has_cisa_representative",'cisa-representative', null) +})(); \ No newline at end of file diff --git a/src/registrar/config/urls.py b/src/registrar/config/urls.py index 3918fa087..4cc044771 100644 --- a/src/registrar/config/urls.py +++ b/src/registrar/config/urls.py @@ -46,7 +46,7 @@ for step, view in [ (Step.PURPOSE, views.Purpose), (Step.YOUR_CONTACT, views.YourContact), (Step.OTHER_CONTACTS, views.OtherContacts), - (Step.ANYTHING_ELSE, views.AnythingElse), + (Step.ANYTHING_ELSE, views.AdditionalDetails), (Step.REQUIREMENTS, views.Requirements), (Step.REVIEW, views.Review), ]: diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py index 1efc028f6..9193015e4 100644 --- a/src/registrar/forms/domain_request_wizard.py +++ b/src/registrar/forms/domain_request_wizard.py @@ -757,28 +757,15 @@ OtherContactsFormSet = forms.formset_factory( formset=BaseOtherContactsFormSet, ) - -class NoOtherContactsForm(RegistrarForm): - no_other_contacts_rationale = forms.CharField( - required=True, - # label has to end in a space to get the label_suffix to show - label=("No other employees rationale"), - widget=forms.Textarea(), - validators=[ - MaxLengthValidator( - 1000, - message="Response must be less than 1000 characters.", - ) - ], - error_messages={"required": ("Rationale for no other employees is required.")}, - ) - +class BaseDeletableRegistrarForm(RegistrarForm): + """Adds special validation and delete functionality. + Used by forms that are tied to a Yes/No form.""" def __init__(self, *args, **kwargs): self.form_data_marked_for_deletion = False super().__init__(*args, **kwargs) def mark_form_for_deletion(self): - """Marks no_other_contacts form for deletion. + """Marks this form for deletion. This changes behavior of validity checks and to_database methods.""" self.form_data_marked_for_deletion = True @@ -822,8 +809,53 @@ class NoOtherContactsForm(RegistrarForm): setattr(obj, name, value) obj.save() +class NoOtherContactsForm(BaseDeletableRegistrarForm): + no_other_contacts_rationale = forms.CharField( + required=True, + # label has to end in a space to get the label_suffix to show + label=("No other employees rationale"), + widget=forms.Textarea(), + validators=[ + MaxLengthValidator( + 1000, + message="Response must be less than 1000 characters.", + ) + ], + error_messages={"required": ("Rationale for no other employees is required.")}, + ) -class AnythingElseForm(RegistrarForm): +class CisaRepresentativeForm(BaseDeletableRegistrarForm): + cisa_representative_email = forms.EmailField( + required=False, + label="Are you working with a CISA representative?", #TODO-NL: (design check) - is this the right label? + ) + +class CisaRepresentativeYesNoForm(RegistrarForm): + def __init__(self, *args, **kwargs): + """Extend the initialization of the form from RegistrarForm __init__""" + super().__init__(*args, **kwargs) + # set the initial value based on attributes of domain request + if self.domain_request: + if self.domain_request.has_cisa_representative(): + initial_value = True + else: + initial_value = False + else: + # No pre-selection for new domain requests + initial_value = None + + self.fields["has_cisa_representative"] = forms.TypedChoiceField( + coerce=lambda x: x.lower() == "true" if x is not None else None, # coerce strings to bool, excepting None + choices=((True, "Yes"), (False, "No")), + initial=initial_value, + widget=forms.RadioSelect, + error_messages={ + "required": "This question is required.", + }, + ) + + +class AdditionalDetailsForm(BaseDeletableRegistrarForm): anything_else = forms.CharField( required=False, label="Anything else?", @@ -836,6 +868,29 @@ class AnythingElseForm(RegistrarForm): ], ) +class AdditionalDetailsYesNoForm(RegistrarForm): + def __init__(self, *args, **kwargs): + """Extend the initialization of the form from RegistrarForm __init__""" + super().__init__(*args, **kwargs) + # set the initial value based on attributes of domain request + if self.domain_request: + if self.domain_request.has_anything_else_text(): + initial_value = True + else: + initial_value = False + else: + # No pre-selection for new domain requests + initial_value = None + + self.fields["has_anything_else_text"] = forms.TypedChoiceField( + coerce=lambda x: x.lower() == "true" if x is not None else None, # coerce strings to bool, excepting None + choices=((True, "Yes"), (False, "No")), + initial=initial_value, + widget=forms.RadioSelect, + error_messages={ + "required": "This question is required.", #TODO-NL: (design check) - is this required? + }, + ) class RequirementsForm(RegistrarForm): is_policy_acknowledged = forms.BooleanField( diff --git a/src/registrar/migrations/0082_domaininformation_cisa_representative_email_and_more.py b/src/registrar/migrations/0082_domaininformation_cisa_representative_email_and_more.py new file mode 100644 index 000000000..b6be96eaa --- /dev/null +++ b/src/registrar/migrations/0082_domaininformation_cisa_representative_email_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.10 on 2024-04-10 22:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("registrar", "0081_create_groups_v10"), + ] + + operations = [ + migrations.AddField( + model_name="domaininformation", + name="cisa_representative_email", + field=models.EmailField(blank=True, db_index=True, max_length=254, null=True), + ), + migrations.AddField( + model_name="domainrequest", + name="cisa_representative_email", + field=models.EmailField(blank=True, db_index=True, max_length=254, null=True), + ), + ] diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index b5755a3c9..689aadc8a 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -198,6 +198,12 @@ class DomainInformation(TimeStampedModel): help_text="Anything else?", ) + cisa_representative_email = models.EmailField( + null=True, + blank=True, + db_index=True, + ) + is_policy_acknowledged = models.BooleanField( null=True, blank=True, diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index f4581de93..a9c4164d6 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -566,6 +566,12 @@ class DomainRequest(TimeStampedModel): help_text="Anything else?", ) + cisa_representative_email = models.EmailField( + null=True, + blank=True, + db_index=True, + ) + is_policy_acknowledged = models.BooleanField( null=True, blank=True, @@ -923,6 +929,14 @@ class DomainRequest(TimeStampedModel): def has_other_contacts(self) -> bool: """Does this domain request have other contacts listed?""" return self.other_contacts.exists() + + def has_anything_else_text(self) -> bool: + """Does this domain request have an 'anything else?' entry""" + return self.anything_else != "" and self.anything_else != None #TODO-NL: how to handle falsy strings again? + + def has_cisa_representative(self) -> bool: + """Does this domain request have cisa representative?""" + return self.cisa_representative_email != "" and self.cisa_representative_email != None def is_federal(self) -> Union[bool, None]: """Is this domain request for a federal agency? diff --git a/src/registrar/templates/domain_request_additional_details.html b/src/registrar/templates/domain_request_additional_details.html new file mode 100644 index 000000000..430d47472 --- /dev/null +++ b/src/registrar/templates/domain_request_additional_details.html @@ -0,0 +1,56 @@ +{% extends 'domain_request_form.html' %} +{% load static field_helpers %} + +{% block form_instructions %} +

These questions are required (*).

+{% endblock %} + +{% block form_required_fields_help_text %} +{# commented out so it does not appear at this point on this page #} +{% endblock %} + + +{% block form_fields %} +
+ +

Are you working with anyone from CISA regions on your domain request?

+

.gov is managed by the Cybersecurity and Infrastructure Security Agency. CISA has 10 regions that some organizations choose to work with. Regional representatives use titles like protective security advisors, cyber security advisors, or election security advisors.

+
+ + + {% with add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" %} + {% 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 #} + +
+ +
+

Your representative’s email (*)

+ {% input_with_errors forms.1.cisa_representative_email %} + {# forms.1 is a form for inputting the e-mail of a cisa representative #} + +
+ + +
+ +

Is there anything else you’d like us to know about your domain request?

+
+ + + {% with add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" %} + {% 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 #} + +
+ +
+ {% 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 #} + +
+{% endblock %} diff --git a/src/registrar/tests/test_forms.py b/src/registrar/tests/test_forms.py index 4b8904f0c..c72c70e98 100644 --- a/src/registrar/tests/test_forms.py +++ b/src/registrar/tests/test_forms.py @@ -15,7 +15,7 @@ from registrar.forms.domain_request_wizard import ( RequirementsForm, TribalGovernmentForm, PurposeForm, - AnythingElseForm, + AdditionalDetailsForm, AboutYourOrganizationForm, ) from registrar.forms.domain import ContactForm @@ -274,7 +274,7 @@ class TestFormValidation(MockEppLib): def test_anything_else_form_about_your_organization_character_count_invalid(self): """Response must be less than 2000 characters.""" - form = AnythingElseForm( + form = AdditionalDetailsForm( data={ "anything_else": "Bacon ipsum dolor amet fatback strip steak pastrami" "shankle, drumstick doner chicken landjaeger turkey andouille." diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py index 244b47602..9889b9b8a 100644 --- a/src/registrar/views/domain_request.py +++ b/src/registrar/views/domain_request.py @@ -580,10 +580,68 @@ class OtherContacts(DomainRequestWizard): all_forms_valid = False return all_forms_valid +#DONE-NL: rename this to "Additional Details" (note: this is a find-replace job. VS will not refactor properly) +class AdditionalDetails(DomainRequestWizard): -class AnythingElse(DomainRequestWizard): - template_name = "domain_request_anything_else.html" - forms = [forms.AnythingElseForm] + # TODO-NL: Delete this old (original code for anything else) + # template_name = "domain_request_anything_else.html" + # forms = [forms.AdditionalDetailsForm] + + template_name = "domain_request_additional_details.html" + # OLD: forms = [forms.OtherContactsYesNoForm, forms.OtherContactsFormSet, forms.NoOtherContactsForm] + # TODO-NL: (refactor) -- move form hookups into respective areas + forms = [forms.CisaRepresentativeYesNoForm, forms.CisaRepresentativeForm, forms.AdditionalDetailsYesNoForm, forms.AdditionalDetailsForm] + + # TODO-NL: (refactor) -- move validation into respective areas + def is_valid(self, forms: list) -> bool: + + # Validate Cisa Representative + """Overrides default behavior defined in DomainRequestWizard. + Depending on value in yes_no forms, marks corresponding data + for deletion. Then validates all forms. + """ + cisa_representative_email_yes_no_form = forms[0] + cisa_representative_email_form = forms[1] + anything_else_yes_no_form = forms[2] + anything_else_form = forms[3] + + # ------- Validate cisa representative ------- + cisa_rep_portion_is_valid = True + # test first for yes_no_form validity + if cisa_representative_email_yes_no_form.is_valid(): + # test for existing data + if not cisa_representative_email_yes_no_form.cleaned_data.get("has_cisa_representative"): + # mark the cisa_representative_email_form for deletion + cisa_representative_email_form.mark_form_for_deletion() + else: + cisa_rep_portion_is_valid = cisa_representative_email_form.is_valid() + else: + # if yes no form is invalid, no choice has been made + # mark the cisa_representative_email_form for deletion + cisa_representative_email_form.mark_form_for_deletion() + cisa_rep_portion_is_valid = False + + + # ------- Validate anything else ------- + anything_else_portion_is_valid = True + # test first for yes_no_form validity + if anything_else_yes_no_form.is_valid(): + # test for existing data + if not anything_else_yes_no_form.cleaned_data.get("has_anything_else_text"): + # mark the anything_else_form for deletion + anything_else_form.mark_form_for_deletion() + else: + anything_else_portion_is_valid = cisa_representative_email_form.is_valid() + else: + # if yes no form is invalid, no choice has been made + # mark the anything_else_form for deletion + anything_else_form.mark_form_for_deletion() + anything_else_portion_is_valid = False + + + # ------- Return combined validation result ------- + all_forms_valid = cisa_rep_portion_is_valid and anything_else_portion_is_valid + return all_forms_valid class Requirements(DomainRequestWizard): From e2f975f04fc3deee057df6e35fb101b6850fa361 Mon Sep 17 00:00:00 2001 From: CocoByte Date: Wed, 10 Apr 2024 19:07:28 -0600 Subject: [PATCH 02/85] Fix migrations. Update Steps (side menu) --- src/registrar/config/urls.py | 2 +- ...domaininformation_cisa_representative_email_and_more.py} | 4 ++-- src/registrar/templates/domain_request_review.html | 2 +- src/registrar/views/domain_request.py | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) rename src/registrar/migrations/{0082_domaininformation_cisa_representative_email_and_more.py => 0084_domaininformation_cisa_representative_email_and_more.py} (82%) diff --git a/src/registrar/config/urls.py b/src/registrar/config/urls.py index 4cc044771..720034150 100644 --- a/src/registrar/config/urls.py +++ b/src/registrar/config/urls.py @@ -46,7 +46,7 @@ for step, view in [ (Step.PURPOSE, views.Purpose), (Step.YOUR_CONTACT, views.YourContact), (Step.OTHER_CONTACTS, views.OtherContacts), - (Step.ANYTHING_ELSE, views.AdditionalDetails), + (Step.ADDITIONAL_DETAILS, views.AdditionalDetails), (Step.REQUIREMENTS, views.Requirements), (Step.REVIEW, views.Review), ]: diff --git a/src/registrar/migrations/0082_domaininformation_cisa_representative_email_and_more.py b/src/registrar/migrations/0084_domaininformation_cisa_representative_email_and_more.py similarity index 82% rename from src/registrar/migrations/0082_domaininformation_cisa_representative_email_and_more.py rename to src/registrar/migrations/0084_domaininformation_cisa_representative_email_and_more.py index b6be96eaa..83b717b00 100644 --- a/src/registrar/migrations/0082_domaininformation_cisa_representative_email_and_more.py +++ b/src/registrar/migrations/0084_domaininformation_cisa_representative_email_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.10 on 2024-04-10 22:19 +# Generated by Django 4.2.10 on 2024-04-11 00:43 from django.db import migrations, models @@ -6,7 +6,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("registrar", "0081_create_groups_v10"), + ("registrar", "0083_alter_contact_email_alter_publiccontact_email"), ] operations = [ diff --git a/src/registrar/templates/domain_request_review.html b/src/registrar/templates/domain_request_review.html index 227fc0cea..aeba9955c 100644 --- a/src/registrar/templates/domain_request_review.html +++ b/src/registrar/templates/domain_request_review.html @@ -155,7 +155,7 @@ {% endif %} - {% if step == Step.ANYTHING_ELSE %} + {% if step == Step.ADDITIONAL_DETAILS %} {% namespaced_url 'domain-request' step as domain_request_url %} {% with title=form_titles|get_item:step value=domain_request.anything_else|default:"No" %} {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url %} diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py index 9889b9b8a..12043903b 100644 --- a/src/registrar/views/domain_request.py +++ b/src/registrar/views/domain_request.py @@ -45,7 +45,7 @@ class Step(StrEnum): PURPOSE = "purpose" YOUR_CONTACT = "your_contact" OTHER_CONTACTS = "other_contacts" - ANYTHING_ELSE = "anything_else" + ADDITIONAL_DETAILS = "anything_else" REQUIREMENTS = "requirements" REVIEW = "review" @@ -91,7 +91,7 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView): Step.PURPOSE: _("Purpose of your domain"), Step.YOUR_CONTACT: _("Your contact information"), Step.OTHER_CONTACTS: _("Other employees from your organization"), - Step.ANYTHING_ELSE: _("Anything else?"), + Step.ADDITIONAL_DETAILS: _("Additional Details"), Step.REQUIREMENTS: _("Requirements for operating a .gov domain"), Step.REVIEW: _("Review and submit your domain request"), } @@ -366,7 +366,7 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView): or self.domain_request.no_other_contacts_rationale is not None ), "anything_else": ( - self.domain_request.anything_else is not None or self.domain_request.is_policy_acknowledged is not None + (self.domain_request.anything_else is not None and self.domain_request.cisa_representative_email) or self.domain_request.is_policy_acknowledged is not None ), "requirements": self.domain_request.is_policy_acknowledged is not None, "review": self.domain_request.is_policy_acknowledged is not None, From 3dfdb3bfa1621b51e1eef7b58c8b415967c26c3d Mon Sep 17 00:00:00 2001 From: CocoByte Date: Thu, 11 Apr 2024 13:24:17 -0600 Subject: [PATCH 03/85] Fixed summary sections to meet design requirements --- .../templates/domain_request_review.html | 5 +- .../templates/domain_request_status.html | 4 +- .../includes/summary_additional_details.html | 50 +++++++++++++++++++ 3 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 src/registrar/templates/includes/summary_additional_details.html diff --git a/src/registrar/templates/domain_request_review.html b/src/registrar/templates/domain_request_review.html index aeba9955c..09a930710 100644 --- a/src/registrar/templates/domain_request_review.html +++ b/src/registrar/templates/domain_request_review.html @@ -156,10 +156,7 @@ {% if step == Step.ADDITIONAL_DETAILS %} - {% namespaced_url 'domain-request' step as domain_request_url %} - {% with title=form_titles|get_item:step value=domain_request.anything_else|default:"No" %} - {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=domain_request_url %} - {% endwith %} + {% include "includes/summary_additional_details.html" with domainRequest=DomainRequest %} {% endif %} diff --git a/src/registrar/templates/domain_request_status.html b/src/registrar/templates/domain_request_status.html index d3c1eab6d..cf9e7ffe3 100644 --- a/src/registrar/templates/domain_request_status.html +++ b/src/registrar/templates/domain_request_status.html @@ -115,8 +115,8 @@ {% else %} {% include "includes/summary_item.html" with title='Other employees from your organization' value=DomainRequest.no_other_contacts_rationale heading_level=heading_level %} {% endif %} - - {% include "includes/summary_item.html" with title='Anything else?' value=DomainRequest.anything_else|default:"No" heading_level=heading_level %} + + {% include "includes/summary_additional_details.html" with domainRequest=DomainRequest %} {% endwith %} diff --git a/src/registrar/templates/includes/summary_additional_details.html b/src/registrar/templates/includes/summary_additional_details.html new file mode 100644 index 000000000..e10b90384 --- /dev/null +++ b/src/registrar/templates/includes/summary_additional_details.html @@ -0,0 +1,50 @@ +{% load static url_helpers %} + +{# TODO (future ticket?): create a template that allows us to easily display nested forms. +It is a little complex because we will have to formulate how to aggregate form pairings. +(eg. yes/no radio forms plus the information they toggle need to be linked somehow +and condense down into one subsection)} + +
+ +
+
+

+ Additional Details +

+ + {% if domainRequest %} +
+
+ CISA regions representative +
+
+ {% if domainRequest.has_cisa_representative %} + domainRequest.cisa_representative_email + {% else %} + (none) + {% endif %} +
+
+ Anything else +
+
+ {% if domainRequest.has_anything_else_text %} + domainRequest.anything_else + {% else %} + No + {% endif %} +
+
+ {% else %} + ERROR Please contact technical support/dev + {% endif %} +
+
+
From 44c74c7c867015ad1ff09ca8a43fda47c5aea751 Mon Sep 17 00:00:00 2001 From: CocoByte Date: Thu, 11 Apr 2024 14:04:15 -0600 Subject: [PATCH 04/85] Linted --- src/registrar/forms/domain_request_wizard.py | 13 ++++++++++--- src/registrar/models/domain_request.py | 8 ++++---- src/registrar/views/domain_request.py | 17 +++++++++++------ 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py index 6f27876eb..6ddb68a74 100644 --- a/src/registrar/forms/domain_request_wizard.py +++ b/src/registrar/forms/domain_request_wizard.py @@ -778,9 +778,11 @@ OtherContactsFormSet = forms.formset_factory( formset=BaseOtherContactsFormSet, ) + class BaseDeletableRegistrarForm(RegistrarForm): """Adds special validation and delete functionality. Used by forms that are tied to a Yes/No form.""" + def __init__(self, *args, **kwargs): self.form_data_marked_for_deletion = False super().__init__(*args, **kwargs) @@ -830,6 +832,7 @@ class BaseDeletableRegistrarForm(RegistrarForm): setattr(obj, name, value) obj.save() + class NoOtherContactsForm(BaseDeletableRegistrarForm): no_other_contacts_rationale = forms.CharField( required=True, @@ -845,12 +848,14 @@ class NoOtherContactsForm(BaseDeletableRegistrarForm): error_messages={"required": ("Rationale for no other employees is required.")}, ) + class CisaRepresentativeForm(BaseDeletableRegistrarForm): cisa_representative_email = forms.EmailField( required=False, - label="Are you working with a CISA representative?", #TODO-NL: (design check) - is this the right label? + label="Are you working with a CISA representative?", # TODO-NL: (design check) - is this the right label? ) + class CisaRepresentativeYesNoForm(RegistrarForm): def __init__(self, *args, **kwargs): """Extend the initialization of the form from RegistrarForm __init__""" @@ -871,7 +876,7 @@ class CisaRepresentativeYesNoForm(RegistrarForm): initial=initial_value, widget=forms.RadioSelect, error_messages={ - "required": "This question is required.", + "required": "This question is required.", }, ) @@ -889,6 +894,7 @@ class AdditionalDetailsForm(BaseDeletableRegistrarForm): ], ) + class AdditionalDetailsYesNoForm(RegistrarForm): def __init__(self, *args, **kwargs): """Extend the initialization of the form from RegistrarForm __init__""" @@ -909,10 +915,11 @@ class AdditionalDetailsYesNoForm(RegistrarForm): initial=initial_value, widget=forms.RadioSelect, error_messages={ - "required": "This question is required.", #TODO-NL: (design check) - is this required? + "required": "This question is required.", # TODO-NL: (design check) - is this required? }, ) + class RequirementsForm(RegistrarForm): is_policy_acknowledged = forms.BooleanField( label="I read and agree to the requirements for operating a .gov domain.", diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index 56dec0e44..13d2ae725 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -1037,14 +1037,14 @@ class DomainRequest(TimeStampedModel): def has_other_contacts(self) -> bool: """Does this domain request have other contacts listed?""" return self.other_contacts.exists() - + def has_anything_else_text(self) -> bool: """Does this domain request have an 'anything else?' entry""" - return self.anything_else != "" and self.anything_else != None #TODO-NL: how to handle falsy strings again? - + return self.anything_else != "" and self.anything_else is not None + def has_cisa_representative(self) -> bool: """Does this domain request have cisa representative?""" - return self.cisa_representative_email != "" and self.cisa_representative_email != None + return self.cisa_representative_email != "" and self.cisa_representative_email is not None def is_federal(self) -> Union[bool, None]: """Is this domain request for a federal agency? diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py index 12043903b..07f3f77f1 100644 --- a/src/registrar/views/domain_request.py +++ b/src/registrar/views/domain_request.py @@ -366,7 +366,8 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView): or self.domain_request.no_other_contacts_rationale is not None ), "anything_else": ( - (self.domain_request.anything_else is not None and self.domain_request.cisa_representative_email) or self.domain_request.is_policy_acknowledged is not None + (self.domain_request.anything_else is not None and self.domain_request.cisa_representative_email) + or self.domain_request.is_policy_acknowledged is not None ), "requirements": self.domain_request.is_policy_acknowledged is not None, "review": self.domain_request.is_policy_acknowledged is not None, @@ -580,7 +581,8 @@ class OtherContacts(DomainRequestWizard): all_forms_valid = False return all_forms_valid -#DONE-NL: rename this to "Additional Details" (note: this is a find-replace job. VS will not refactor properly) + +# DONE-NL: rename this to "Additional Details" (note: this is a find-replace job. VS will not refactor properly) class AdditionalDetails(DomainRequestWizard): # TODO-NL: Delete this old (original code for anything else) @@ -590,7 +592,12 @@ class AdditionalDetails(DomainRequestWizard): template_name = "domain_request_additional_details.html" # OLD: forms = [forms.OtherContactsYesNoForm, forms.OtherContactsFormSet, forms.NoOtherContactsForm] # TODO-NL: (refactor) -- move form hookups into respective areas - forms = [forms.CisaRepresentativeYesNoForm, forms.CisaRepresentativeForm, forms.AdditionalDetailsYesNoForm, forms.AdditionalDetailsForm] + forms = [ + forms.CisaRepresentativeYesNoForm, + forms.CisaRepresentativeForm, + forms.AdditionalDetailsYesNoForm, + forms.AdditionalDetailsForm, + ] # TODO-NL: (refactor) -- move validation into respective areas def is_valid(self, forms: list) -> bool: @@ -605,7 +612,7 @@ class AdditionalDetails(DomainRequestWizard): anything_else_yes_no_form = forms[2] anything_else_form = forms[3] - # ------- Validate cisa representative ------- + # ------- Validate cisa representative ------- cisa_rep_portion_is_valid = True # test first for yes_no_form validity if cisa_representative_email_yes_no_form.is_valid(): @@ -621,7 +628,6 @@ class AdditionalDetails(DomainRequestWizard): cisa_representative_email_form.mark_form_for_deletion() cisa_rep_portion_is_valid = False - # ------- Validate anything else ------- anything_else_portion_is_valid = True # test first for yes_no_form validity @@ -638,7 +644,6 @@ class AdditionalDetails(DomainRequestWizard): anything_else_form.mark_form_for_deletion() anything_else_portion_is_valid = False - # ------- Return combined validation result ------- all_forms_valid = cisa_rep_portion_is_valid and anything_else_portion_is_valid return all_forms_valid From 9d872d065cdf93a254b60133133d719bf31f48ba Mon Sep 17 00:00:00 2001 From: CocoByte Date: Thu, 11 Apr 2024 15:46:04 -0600 Subject: [PATCH 05/85] Cleanup. Admin updates --- src/registrar/admin.py | 2 +- ...tion_cisa_representative_email_and_more.py | 24 ++++++++++++++++--- src/registrar/models/domain_information.py | 2 ++ src/registrar/models/domain_request.py | 5 ++++ .../domain_request_anything_else.html | 19 --------------- .../includes/summary_additional_details.html | 6 ++--- 6 files changed, 32 insertions(+), 26 deletions(-) delete mode 100644 src/registrar/templates/domain_request_anything_else.html diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 8e483ddb8..e1d344458 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1132,7 +1132,7 @@ class DomainRequestAdmin(ListHeaderAdmin): }, ), (".gov domain", {"fields": ["requested_domain", "alternative_domains"]}), - ("Contacts", {"fields": ["authorizing_official", "other_contacts", "no_other_contacts_rationale"]}), + ("Contacts", {"fields": ["authorizing_official", "other_contacts", "no_other_contacts_rationale", "cisa_representative_email"]}), ("Background info", {"fields": ["purpose", "anything_else", "current_websites"]}), ( "Type of organization", diff --git a/src/registrar/migrations/0084_domaininformation_cisa_representative_email_and_more.py b/src/registrar/migrations/0084_domaininformation_cisa_representative_email_and_more.py index 83b717b00..7f0d0b71e 100644 --- a/src/registrar/migrations/0084_domaininformation_cisa_representative_email_and_more.py +++ b/src/registrar/migrations/0084_domaininformation_cisa_representative_email_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.10 on 2024-04-11 00:43 +# Generated by Django 4.2.10 on 2024-04-11 21:40 from django.db import migrations, models @@ -13,11 +13,29 @@ class Migration(migrations.Migration): migrations.AddField( model_name="domaininformation", name="cisa_representative_email", - field=models.EmailField(blank=True, db_index=True, max_length=254, null=True), + field=models.EmailField( + blank=True, db_index=True, max_length=254, null=True, verbose_name="CISA region representative" + ), ), migrations.AddField( model_name="domainrequest", name="cisa_representative_email", - field=models.EmailField(blank=True, db_index=True, max_length=254, null=True), + field=models.EmailField( + blank=True, db_index=True, max_length=254, null=True, verbose_name="CISA region representative" + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="anything_else", + field=models.TextField( + blank=True, help_text="Anything else?", null=True, verbose_name="Additional Details" + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="anything_else", + field=models.TextField( + blank=True, help_text="Anything else?", null=True, verbose_name="Additional Details" + ), ), ] diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index 1aa9ea6d2..dc7cae9fd 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -213,12 +213,14 @@ class DomainInformation(TimeStampedModel): null=True, blank=True, help_text="Anything else?", + verbose_name="Additional Details", ) cisa_representative_email = models.EmailField( null=True, blank=True, db_index=True, + verbose_name="CISA region representative", ) is_policy_acknowledged = models.BooleanField( diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index 13d2ae725..731034606 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -644,12 +644,14 @@ class DomainRequest(TimeStampedModel): null=True, blank=True, help_text="Anything else?", + verbose_name="Additional Details", ) cisa_representative_email = models.EmailField( null=True, blank=True, db_index=True, + verbose_name="CISA region representative", ) is_policy_acknowledged = models.BooleanField( @@ -1045,6 +1047,9 @@ class DomainRequest(TimeStampedModel): def has_cisa_representative(self) -> bool: """Does this domain request have cisa representative?""" return self.cisa_representative_email != "" and self.cisa_representative_email is not None + + def has_additional_details(self) -> bool: + return self.has_anything_else_text() or self.has_cisa_representative() def is_federal(self) -> Union[bool, None]: """Is this domain request for a federal agency? diff --git a/src/registrar/templates/domain_request_anything_else.html b/src/registrar/templates/domain_request_anything_else.html deleted file mode 100644 index dbeb10cac..000000000 --- a/src/registrar/templates/domain_request_anything_else.html +++ /dev/null @@ -1,19 +0,0 @@ -{% extends 'domain_request_form.html' %} -{% load field_helpers %} - -{% block form_instructions %} -

Is there anything else you’d like us to know about your domain request?

- -

This question is optional.

-{% endblock %} - -{% block form_required_fields_help_text %} -{# commented out so it does not appear on this page #} -{% endblock %} - - -{% block form_fields %} - {% with attr_maxlength=2000 add_label_class="usa-sr-only" %} - {% input_with_errors forms.0.anything_else %} - {% endwith %} -{% endblock %} diff --git a/src/registrar/templates/includes/summary_additional_details.html b/src/registrar/templates/includes/summary_additional_details.html index e10b90384..1009f4ff0 100644 --- a/src/registrar/templates/includes/summary_additional_details.html +++ b/src/registrar/templates/includes/summary_additional_details.html @@ -1,9 +1,9 @@ {% load static url_helpers %} -{# TODO (future ticket?): create a template that allows us to easily display nested forms. +
@@ -19,7 +19,7 @@ and condense down into one subsection)} Additional Details - {% if domainRequest %} + {% if domainRequest is not none %}
CISA regions representative From 30eed60413536a2ee579691652637c81c5f3a8fe Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 12 Apr 2024 15:34:01 -0600 Subject: [PATCH 06/85] Update help text (most the way there) --- src/registrar/models/domain.py | 11 ++++----- src/registrar/models/domain_information.py | 24 ++++--------------- src/registrar/models/domain_request.py | 27 +++++----------------- src/registrar/models/host.py | 3 +-- src/registrar/models/host_ip.py | 3 +-- 5 files changed, 18 insertions(+), 50 deletions(-) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 079fce3bc..e4026be87 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -992,20 +992,19 @@ class Domain(TimeStampedModel, DomainHelper): blank=False, default=None, # prevent saving without a value unique=True, - help_text="Fully qualified domain name", ) state = FSMField( max_length=21, choices=State.choices, default=State.UNKNOWN, - protected=True, # cannot change state directly, particularly in Django admin - help_text="Very basic info about the lifecycle of this domain object", + # cannot change state directly, particularly in Django admin + protected=True, ) expiration_date = DateField( null=True, - help_text=("Duplication of registry's expiration date saved for ease of reporting"), + help_text=("Date the domain expires in the registry"), ) security_contact_registry_id = TextField( @@ -1017,13 +1016,13 @@ class Domain(TimeStampedModel, DomainHelper): deleted = DateField( null=True, editable=False, - help_text="Deleted at date", + help_text="Will appear blank unless the domain is in \"deleted\" state", ) first_ready = DateField( null=True, editable=False, - help_text="The last time this domain moved into the READY state", + help_text="Date when this domain first moved into \"ready\" state; date will never change", ) def isActive(self): diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index 2ed27504c..103e5068a 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -37,6 +37,7 @@ class DomainInformation(TimeStampedModel): "registrar.User", on_delete=models.PROTECT, related_name="information_created", + help_text="Person who submitted the domain request", ) domain_request = models.OneToOneField( @@ -45,7 +46,7 @@ class DomainInformation(TimeStampedModel): blank=True, null=True, related_name="DomainRequest_info", - help_text="Associated domain request", + help_text="Request associated with this domain", unique=True, ) @@ -62,7 +63,6 @@ class DomainInformation(TimeStampedModel): is_election_board = models.BooleanField( null=True, blank=True, - help_text="Is your organization an election office?", ) # TODO - Ticket #1911: stub this data from DomainRequest @@ -76,25 +76,21 @@ class DomainInformation(TimeStampedModel): federally_recognized_tribe = models.BooleanField( null=True, - help_text="Is the tribe federally recognized", ) state_recognized_tribe = models.BooleanField( null=True, - help_text="Is the tribe recognized by a state", ) tribe_name = models.CharField( null=True, blank=True, - help_text="Name of tribe", ) federal_agency = models.CharField( choices=AGENCY_CHOICES, null=True, blank=True, - help_text="Federal agency", ) federal_type = models.CharField( @@ -102,7 +98,6 @@ class DomainInformation(TimeStampedModel): choices=BranchChoices.choices, null=True, blank=True, - help_text="Federal government branch", ) is_election_board = models.BooleanField( @@ -114,52 +109,45 @@ class DomainInformation(TimeStampedModel): organization_name = models.CharField( null=True, blank=True, - help_text="Organization name", db_index=True, ) address_line1 = models.CharField( null=True, blank=True, - help_text="Street address", verbose_name="Street address", ) address_line2 = models.CharField( null=True, blank=True, - help_text="Street address line 2 (optional)", verbose_name="Street address line 2 (optional)", ) city = models.CharField( null=True, blank=True, - help_text="City", ) state_territory = models.CharField( max_length=2, choices=StateTerritoryChoices.choices, null=True, blank=True, - help_text="State, territory, or military post", verbose_name="State, territory, or military post", ) zipcode = models.CharField( max_length=10, null=True, blank=True, - help_text="Zip code", db_index=True, ) urbanization = models.CharField( null=True, blank=True, - help_text="Urbanization (required for Puerto Rico only)", + help_text="Required for Puerto Rico only", verbose_name="Urbanization (required for Puerto Rico only)", ) about_your_organization = models.TextField( null=True, blank=True, - help_text="Information about your organization", ) authorizing_official = models.ForeignKey( @@ -177,7 +165,6 @@ class DomainInformation(TimeStampedModel): null=True, # Access this information via Domain as "domain.domain_info" related_name="domain_info", - help_text="Domain to which this information belongs", ) # This is the contact information provided by the domain requestor. The @@ -188,6 +175,7 @@ class DomainInformation(TimeStampedModel): blank=True, related_name="submitted_domain_requests_information", on_delete=models.PROTECT, + help_text="Person listed under \"your contact information\" in the request form", ) purpose = models.TextField( @@ -206,13 +194,12 @@ class DomainInformation(TimeStampedModel): no_other_contacts_rationale = models.TextField( null=True, blank=True, - help_text="Reason for listing no additional contacts", + help_text="Required if creator does not list other employees", ) anything_else = models.TextField( null=True, blank=True, - help_text="Anything else?", ) is_policy_acknowledged = models.BooleanField( @@ -224,7 +211,6 @@ class DomainInformation(TimeStampedModel): notes = models.TextField( null=True, blank=True, - help_text="Notes about the request", ) def __str__(self): diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index bd529f7e6..1cd66b464 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -455,6 +455,7 @@ class DomainRequest(TimeStampedModel): "registrar.User", on_delete=models.PROTECT, related_name="domain_requests_created", + help_text="Person who submitted the domain request; will not receive email updates", ) investigator = models.ForeignKey( @@ -472,13 +473,11 @@ class DomainRequest(TimeStampedModel): choices=OrganizationChoices.choices, null=True, blank=True, - help_text="Type of organization", ) is_election_board = models.BooleanField( null=True, blank=True, - help_text="Is your organization an election office?", ) # TODO - Ticket #1911: stub this data from DomainRequest @@ -492,25 +491,21 @@ class DomainRequest(TimeStampedModel): federally_recognized_tribe = models.BooleanField( null=True, - help_text="Is the tribe federally recognized", ) state_recognized_tribe = models.BooleanField( null=True, - help_text="Is the tribe recognized by a state", ) tribe_name = models.CharField( null=True, blank=True, - help_text="Name of tribe", ) federal_agency = models.CharField( choices=AGENCY_CHOICES, null=True, blank=True, - help_text="Federal agency", ) federal_type = models.CharField( @@ -518,57 +513,49 @@ class DomainRequest(TimeStampedModel): choices=BranchChoices.choices, null=True, blank=True, - help_text="Federal government branch", ) organization_name = models.CharField( null=True, blank=True, - help_text="Organization name", db_index=True, ) address_line1 = models.CharField( null=True, blank=True, - help_text="Street address", verbose_name="Address line 1", ) address_line2 = models.CharField( null=True, blank=True, - help_text="Street address line 2 (optional)", verbose_name="Address line 2", ) city = models.CharField( null=True, blank=True, - help_text="City", ) state_territory = models.CharField( max_length=2, choices=StateTerritoryChoices.choices, null=True, blank=True, - help_text="State, territory, or military post", ) zipcode = models.CharField( max_length=10, null=True, blank=True, - help_text="Zip code", db_index=True, ) urbanization = models.CharField( null=True, blank=True, - help_text="Urbanization (required for Puerto Rico only)", + help_text="Required for Puetro Rico only", ) about_your_organization = models.TextField( null=True, blank=True, - help_text="Information about your organization", ) authorizing_official = models.ForeignKey( @@ -591,7 +578,7 @@ class DomainRequest(TimeStampedModel): "Domain", null=True, blank=True, - help_text="The approved domain", + help_text="Domain associated with this request; will be blank until request is approved", related_name="domain_request", on_delete=models.SET_NULL, ) @@ -600,7 +587,6 @@ class DomainRequest(TimeStampedModel): "DraftDomain", null=True, blank=True, - help_text="The requested domain", related_name="domain_request", on_delete=models.PROTECT, ) @@ -609,6 +595,7 @@ class DomainRequest(TimeStampedModel): "registrar.Website", blank=True, related_name="alternatives+", + help_text="Other domain names the creator provided for consideration", ) # This is the contact information provided by the domain requestor. The @@ -619,12 +606,12 @@ class DomainRequest(TimeStampedModel): blank=True, related_name="submitted_domain_requests", on_delete=models.PROTECT, + help_text="Person listed under \"your contact information\" in the request form; will receive email updates" ) purpose = models.TextField( null=True, blank=True, - help_text="Purpose of your domain", ) other_contacts = models.ManyToManyField( @@ -637,13 +624,12 @@ class DomainRequest(TimeStampedModel): no_other_contacts_rationale = models.TextField( null=True, blank=True, - help_text="Reason for listing no additional contacts", + help_text="Required if creator does not list other employees", ) anything_else = models.TextField( null=True, blank=True, - help_text="Anything else?", ) is_policy_acknowledged = models.BooleanField( @@ -663,7 +649,6 @@ class DomainRequest(TimeStampedModel): notes = models.TextField( null=True, blank=True, - help_text="Notes about this request", ) def save(self, *args, **kwargs): diff --git a/src/registrar/models/host.py b/src/registrar/models/host.py index 3b966832f..642027592 100644 --- a/src/registrar/models/host.py +++ b/src/registrar/models/host.py @@ -21,14 +21,13 @@ class Host(TimeStampedModel): blank=False, default=None, # prevent saving without a value unique=False, - help_text="Fully qualified domain name", ) domain = models.ForeignKey( "registrar.Domain", on_delete=models.PROTECT, related_name="host", # access this Host via the Domain as `domain.host` - help_text="Domain to which this host belongs", + help_text="Domain associated with this hosts", ) def __str__(self): diff --git a/src/registrar/models/host_ip.py b/src/registrar/models/host_ip.py index 777d14430..42d4892db 100644 --- a/src/registrar/models/host_ip.py +++ b/src/registrar/models/host_ip.py @@ -20,12 +20,11 @@ class HostIP(TimeStampedModel): blank=False, default=None, # prevent saving without a value validators=[validate_ipv46_address], - help_text="IP address", ) host = models.ForeignKey( "registrar.Host", on_delete=models.PROTECT, related_name="ip", # access this HostIP via the Host as `host.ip` - help_text="Host to which this IP address belongs", + help_text="IP associated with this host", ) From 98dbca3983a1559a696d1792cdff7fa3a0489aeb Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 15 Apr 2024 09:55:19 -0600 Subject: [PATCH 07/85] Add migration --- ...d_alter_domain_expiration_date_and_more.py | 1176 +++++++++++++++++ src/registrar/models/domain.py | 4 +- src/registrar/models/domain_information.py | 2 +- src/registrar/models/domain_request.py | 2 +- src/registrar/models/host.py | 2 +- src/registrar/models/user.py | 1 + src/registrar/models/verified_by_staff.py | 3 +- src/registrar/models/website.py | 2 +- 8 files changed, 1184 insertions(+), 8 deletions(-) create mode 100644 src/registrar/migrations/0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py diff --git a/src/registrar/migrations/0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py b/src/registrar/migrations/0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py new file mode 100644 index 000000000..523fc522d --- /dev/null +++ b/src/registrar/migrations/0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py @@ -0,0 +1,1176 @@ +# Generated by Django 4.2.10 on 2024-04-15 15:55 + +from django.conf import settings +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import django_fsm +import registrar.models.utility.domain_field + + +class Migration(migrations.Migration): + + dependencies = [ + ("registrar", "0084_create_groups_v11"), + ] + + operations = [ + migrations.AlterField( + model_name="domain", + name="deleted", + field=models.DateField( + editable=False, help_text='Will appear blank unless the domain is in "deleted" state', null=True + ), + ), + migrations.AlterField( + model_name="domain", + name="expiration_date", + field=models.DateField(help_text="Date the domain expires in the registry", null=True), + ), + migrations.AlterField( + model_name="domain", + name="first_ready", + field=models.DateField( + editable=False, + help_text='Date when this domain first moved into "ready" state; date will never change', + null=True, + ), + ), + migrations.AlterField( + model_name="domain", + name="name", + field=registrar.models.utility.domain_field.DomainField(default=None, max_length=253, unique=True), + ), + migrations.AlterField( + model_name="domain", + name="state", + field=django_fsm.FSMField( + choices=[ + ("unknown", "Unknown"), + ("dns needed", "Dns needed"), + ("ready", "Ready"), + ("on hold", "On hold"), + ("deleted", "Deleted"), + ], + default="unknown", + max_length=21, + protected=True, + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="about_your_organization", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="address_line1", + field=models.CharField(blank=True, null=True, verbose_name="Street address"), + ), + migrations.AlterField( + model_name="domaininformation", + name="address_line2", + field=models.CharField(blank=True, null=True, verbose_name="Street address line 2 (optional)"), + ), + migrations.AlterField( + model_name="domaininformation", + name="anything_else", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="city", + field=models.CharField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="creator", + field=models.ForeignKey( + help_text="Person who submitted the domain request", + on_delete=django.db.models.deletion.PROTECT, + related_name="information_created", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="domain", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="domain_info", + to="registrar.domain", + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="domain_request", + field=models.OneToOneField( + blank=True, + help_text="Request associated with this domain", + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="DomainRequest_info", + to="registrar.domainrequest", + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="federal_agency", + field=models.CharField( + blank=True, + choices=[ + ( + "Administrative Conference of the United States", + "Administrative Conference of the United States", + ), + ("Advisory Council on Historic Preservation", "Advisory Council on Historic Preservation"), + ("American Battle Monuments Commission", "American Battle Monuments Commission"), + ("AMTRAK", "AMTRAK"), + ("Appalachian Regional Commission", "Appalachian Regional Commission"), + ( + "Appraisal Subcommittee of the Federal Financial Institutions Examination Council", + "Appraisal Subcommittee of the Federal Financial Institutions Examination Council", + ), + ("Appraisal Subcommittee", "Appraisal Subcommittee"), + ("Architect of the Capitol", "Architect of the Capitol"), + ("Armed Forces Retirement Home", "Armed Forces Retirement Home"), + ( + "Barry Goldwater Scholarship and Excellence in Education Foundation", + "Barry Goldwater Scholarship and Excellence in Education Foundation", + ), + ( + "Barry Goldwater Scholarship and Excellence in Education Program", + "Barry Goldwater Scholarship and Excellence in Education Program", + ), + ("Central Intelligence Agency", "Central Intelligence Agency"), + ("Chemical Safety Board", "Chemical Safety Board"), + ("Christopher Columbus Fellowship Foundation", "Christopher Columbus Fellowship Foundation"), + ("Civil Rights Cold Case Records Review Board", "Civil Rights Cold Case Records Review Board"), + ( + "Commission for the Preservation of America's Heritage Abroad", + "Commission for the Preservation of America's Heritage Abroad", + ), + ("Commission of Fine Arts", "Commission of Fine Arts"), + ( + "Committee for Purchase From People Who Are Blind or Severely Disabled", + "Committee for Purchase From People Who Are Blind or Severely Disabled", + ), + ("Commodity Futures Trading Commission", "Commodity Futures Trading Commission"), + ("Congressional Budget Office", "Congressional Budget Office"), + ("Consumer Financial Protection Bureau", "Consumer Financial Protection Bureau"), + ("Consumer Product Safety Commission", "Consumer Product Safety Commission"), + ("Corporation for National & Community Service", "Corporation for National & Community Service"), + ( + "Corporation for National and Community Service", + "Corporation for National and Community Service", + ), + ( + "Council of Inspectors General on Integrity and Efficiency", + "Council of Inspectors General on Integrity and Efficiency", + ), + ("Court Services and Offender Supervision", "Court Services and Offender Supervision"), + ("Cyberspace Solarium Commission", "Cyberspace Solarium Commission"), + ( + "DC Court Services and Offender Supervision Agency", + "DC Court Services and Offender Supervision Agency", + ), + ("DC Pre-trial Services", "DC Pre-trial Services"), + ("Defense Nuclear Facilities Safety Board", "Defense Nuclear Facilities Safety Board"), + ("Delta Regional Authority", "Delta Regional Authority"), + ("Denali Commission", "Denali Commission"), + ("Department of Agriculture", "Department of Agriculture"), + ("Department of Commerce", "Department of Commerce"), + ("Department of Defense", "Department of Defense"), + ("Department of Education", "Department of Education"), + ("Department of Energy", "Department of Energy"), + ("Department of Health and Human Services", "Department of Health and Human Services"), + ("Department of Homeland Security", "Department of Homeland Security"), + ("Department of Housing and Urban Development", "Department of Housing and Urban Development"), + ("Department of Justice", "Department of Justice"), + ("Department of Labor", "Department of Labor"), + ("Department of State", "Department of State"), + ("Department of the Interior", "Department of the Interior"), + ("Department of the Treasury", "Department of the Treasury"), + ("Department of Transportation", "Department of Transportation"), + ("Department of Veterans Affairs", "Department of Veterans Affairs"), + ("Director of National Intelligence", "Director of National Intelligence"), + ("Dwight D. Eisenhower Memorial Commission", "Dwight D. Eisenhower Memorial Commission"), + ("Election Assistance Commission", "Election Assistance Commission"), + ("Environmental Protection Agency", "Environmental Protection Agency"), + ("Equal Employment Opportunity Commission", "Equal Employment Opportunity Commission"), + ("Executive Office of the President", "Executive Office of the President"), + ("Export-Import Bank of the United States", "Export-Import Bank of the United States"), + ("Export/Import Bank of the U.S.", "Export/Import Bank of the U.S."), + ("Farm Credit Administration", "Farm Credit Administration"), + ("Farm Credit System Insurance Corporation", "Farm Credit System Insurance Corporation"), + ("Federal Communications Commission", "Federal Communications Commission"), + ("Federal Deposit Insurance Corporation", "Federal Deposit Insurance Corporation"), + ("Federal Election Commission", "Federal Election Commission"), + ("Federal Energy Regulatory Commission", "Federal Energy Regulatory Commission"), + ( + "Federal Financial Institutions Examination Council", + "Federal Financial Institutions Examination Council", + ), + ("Federal Housing Finance Agency", "Federal Housing Finance Agency"), + ("Federal Judiciary", "Federal Judiciary"), + ("Federal Labor Relations Authority", "Federal Labor Relations Authority"), + ("Federal Maritime Commission", "Federal Maritime Commission"), + ("Federal Mediation and Conciliation Service", "Federal Mediation and Conciliation Service"), + ( + "Federal Mine Safety and Health Review Commission", + "Federal Mine Safety and Health Review Commission", + ), + ( + "Federal Permitting Improvement Steering Council", + "Federal Permitting Improvement Steering Council", + ), + ("Federal Reserve Board of Governors", "Federal Reserve Board of Governors"), + ("Federal Reserve System", "Federal Reserve System"), + ("Federal Trade Commission", "Federal Trade Commission"), + ("General Services Administration", "General Services Administration"), + ("gov Administration", "gov Administration"), + ("Government Accountability Office", "Government Accountability Office"), + ("Government Publishing Office", "Government Publishing Office"), + ("Gulf Coast Ecosystem Restoration Council", "Gulf Coast Ecosystem Restoration Council"), + ("Harry S Truman Scholarship Foundation", "Harry S Truman Scholarship Foundation"), + ("Harry S. Truman Scholarship Foundation", "Harry S. Truman Scholarship Foundation"), + ("Institute of Museum and Library Services", "Institute of Museum and Library Services"), + ("Institute of Peace", "Institute of Peace"), + ("Inter-American Foundation", "Inter-American Foundation"), + ( + "International Boundary and Water Commission: United States and Mexico", + "International Boundary and Water Commission: United States and Mexico", + ), + ( + "International Boundary Commission: United States and Canada", + "International Boundary Commission: United States and Canada", + ), + ( + "International Joint Commission: United States and Canada", + "International Joint Commission: United States and Canada", + ), + ("James Madison Memorial Fellowship Foundation", "James Madison Memorial Fellowship Foundation"), + ("Japan-United States Friendship Commission", "Japan-United States Friendship Commission"), + ("Japan-US Friendship Commission", "Japan-US Friendship Commission"), + ("John F. Kennedy Center for Performing Arts", "John F. Kennedy Center for Performing Arts"), + ( + "John F. Kennedy Center for the Performing Arts", + "John F. Kennedy Center for the Performing Arts", + ), + ("Legal Services Corporation", "Legal Services Corporation"), + ("Legislative Branch", "Legislative Branch"), + ("Library of Congress", "Library of Congress"), + ("Marine Mammal Commission", "Marine Mammal Commission"), + ( + "Medicaid and CHIP Payment and Access Commission", + "Medicaid and CHIP Payment and Access Commission", + ), + ("Medical Payment Advisory Commission", "Medical Payment Advisory Commission"), + ("Medicare Payment Advisory Commission", "Medicare Payment Advisory Commission"), + ("Merit Systems Protection Board", "Merit Systems Protection Board"), + ("Millennium Challenge Corporation", "Millennium Challenge Corporation"), + ( + "Morris K. Udall and Stewart L. Udall Foundation", + "Morris K. Udall and Stewart L. Udall Foundation", + ), + ("National Aeronautics and Space Administration", "National Aeronautics and Space Administration"), + ("National Archives and Records Administration", "National Archives and Records Administration"), + ("National Capital Planning Commission", "National Capital Planning Commission"), + ("National Council on Disability", "National Council on Disability"), + ("National Credit Union Administration", "National Credit Union Administration"), + ("National Endowment for the Arts", "National Endowment for the Arts"), + ("National Endowment for the Humanities", "National Endowment for the Humanities"), + ( + "National Foundation on the Arts and the Humanities", + "National Foundation on the Arts and the Humanities", + ), + ("National Gallery of Art", "National Gallery of Art"), + ("National Indian Gaming Commission", "National Indian Gaming Commission"), + ("National Labor Relations Board", "National Labor Relations Board"), + ("National Mediation Board", "National Mediation Board"), + ("National Science Foundation", "National Science Foundation"), + ( + "National Security Commission on Artificial Intelligence", + "National Security Commission on Artificial Intelligence", + ), + ("National Transportation Safety Board", "National Transportation Safety Board"), + ( + "Networking Information Technology Research and Development", + "Networking Information Technology Research and Development", + ), + ("Non-Federal Agency", "Non-Federal Agency"), + ("Northern Border Regional Commission", "Northern Border Regional Commission"), + ("Nuclear Regulatory Commission", "Nuclear Regulatory Commission"), + ("Nuclear Safety Oversight Committee", "Nuclear Safety Oversight Committee"), + ("Nuclear Waste Technical Review Board", "Nuclear Waste Technical Review Board"), + ( + "Occupational Safety & Health Review Commission", + "Occupational Safety & Health Review Commission", + ), + ( + "Occupational Safety and Health Review Commission", + "Occupational Safety and Health Review Commission", + ), + ("Office of Compliance", "Office of Compliance"), + ("Office of Congressional Workplace Rights", "Office of Congressional Workplace Rights"), + ("Office of Government Ethics", "Office of Government Ethics"), + ("Office of Navajo and Hopi Indian Relocation", "Office of Navajo and Hopi Indian Relocation"), + ("Office of Personnel Management", "Office of Personnel Management"), + ("Open World Leadership Center", "Open World Leadership Center"), + ("Overseas Private Investment Corporation", "Overseas Private Investment Corporation"), + ("Peace Corps", "Peace Corps"), + ("Pension Benefit Guaranty Corporation", "Pension Benefit Guaranty Corporation"), + ("Postal Regulatory Commission", "Postal Regulatory Commission"), + ("Presidio Trust", "Presidio Trust"), + ("Privacy and Civil Liberties Oversight Board", "Privacy and Civil Liberties Oversight Board"), + ("Public Buildings Reform Board", "Public Buildings Reform Board"), + ( + "Public Defender Service for the District of Columbia", + "Public Defender Service for the District of Columbia", + ), + ("Railroad Retirement Board", "Railroad Retirement Board"), + ("Securities and Exchange Commission", "Securities and Exchange Commission"), + ("Selective Service System", "Selective Service System"), + ("Small Business Administration", "Small Business Administration"), + ("Smithsonian Institution", "Smithsonian Institution"), + ("Social Security Administration", "Social Security Administration"), + ("Social Security Advisory Board", "Social Security Advisory Board"), + ("Southeast Crescent Regional Commission", "Southeast Crescent Regional Commission"), + ("Southwest Border Regional Commission", "Southwest Border Regional Commission"), + ("State Justice Institute", "State Justice Institute"), + ("State, Local, and Tribal Government", "State, Local, and Tribal Government"), + ("Stennis Center for Public Service", "Stennis Center for Public Service"), + ("Surface Transportation Board", "Surface Transportation Board"), + ("Tennessee Valley Authority", "Tennessee Valley Authority"), + ("The Executive Office of the President", "The Executive Office of the President"), + ("The Intelligence Community", "The Intelligence Community"), + ("The Legislative Branch", "The Legislative Branch"), + ("The Supreme Court", "The Supreme Court"), + ( + "The United States World War One Centennial Commission", + "The United States World War One Centennial Commission", + ), + ("U.S. Access Board", "U.S. Access Board"), + ("U.S. Agency for Global Media", "U.S. Agency for Global Media"), + ("U.S. Agency for International Development", "U.S. Agency for International Development"), + ("U.S. Capitol Police", "U.S. Capitol Police"), + ("U.S. Chemical Safety Board", "U.S. Chemical Safety Board"), + ( + "U.S. China Economic and Security Review Commission", + "U.S. China Economic and Security Review Commission", + ), + ( + "U.S. Commission for the Preservation of Americas Heritage Abroad", + "U.S. Commission for the Preservation of Americas Heritage Abroad", + ), + ("U.S. Commission of Fine Arts", "U.S. Commission of Fine Arts"), + ("U.S. Commission on Civil Rights", "U.S. Commission on Civil Rights"), + ( + "U.S. Commission on International Religious Freedom", + "U.S. Commission on International Religious Freedom", + ), + ("U.S. Courts", "U.S. Courts"), + ("U.S. Department of Agriculture", "U.S. Department of Agriculture"), + ("U.S. Interagency Council on Homelessness", "U.S. Interagency Council on Homelessness"), + ("U.S. International Trade Commission", "U.S. International Trade Commission"), + ("U.S. Nuclear Waste Technical Review Board", "U.S. Nuclear Waste Technical Review Board"), + ("U.S. Office of Special Counsel", "U.S. Office of Special Counsel"), + ("U.S. Peace Corps", "U.S. Peace Corps"), + ("U.S. Postal Service", "U.S. Postal Service"), + ("U.S. Semiquincentennial Commission", "U.S. Semiquincentennial Commission"), + ("U.S. Trade and Development Agency", "U.S. Trade and Development Agency"), + ( + "U.S.-China Economic and Security Review Commission", + "U.S.-China Economic and Security Review Commission", + ), + ("Udall Foundation", "Udall Foundation"), + ("United States AbilityOne", "United States AbilityOne"), + ("United States Access Board", "United States Access Board"), + ("United States African Development Foundation", "United States African Development Foundation"), + ("United States Agency for Global Media", "United States Agency for Global Media"), + ("United States Arctic Research Commission", "United States Arctic Research Commission"), + ("United States Global Change Research Program", "United States Global Change Research Program"), + ("United States Holocaust Memorial Museum", "United States Holocaust Memorial Museum"), + ("United States Institute of Peace", "United States Institute of Peace"), + ( + "United States Interagency Council on Homelessness", + "United States Interagency Council on Homelessness", + ), + ( + "United States International Development Finance Corporation", + "United States International Development Finance Corporation", + ), + ("United States International Trade Commission", "United States International Trade Commission"), + ("United States Postal Service", "United States Postal Service"), + ("United States Senate", "United States Senate"), + ("United States Trade and Development Agency", "United States Trade and Development Agency"), + ( + "Utah Reclamation Mitigation and Conservation Commission", + "Utah Reclamation Mitigation and Conservation Commission", + ), + ("Vietnam Education Foundation", "Vietnam Education Foundation"), + ("Western Hemisphere Drug Policy Commission", "Western Hemisphere Drug Policy Commission"), + ( + "Woodrow Wilson International Center for Scholars", + "Woodrow Wilson International Center for Scholars", + ), + ("World War I Centennial Commission", "World War I Centennial Commission"), + ], + null=True, + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="federal_type", + field=models.CharField( + blank=True, + choices=[("executive", "Executive"), ("judicial", "Judicial"), ("legislative", "Legislative")], + max_length=50, + null=True, + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="federally_recognized_tribe", + field=models.BooleanField(null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="no_other_contacts_rationale", + field=models.TextField( + blank=True, help_text="Required if creator does not list other employees", null=True + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="notes", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="organization_name", + field=models.CharField(blank=True, db_index=True, null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="state_recognized_tribe", + field=models.BooleanField(null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="state_territory", + field=models.CharField( + blank=True, + choices=[ + ("AL", "Alabama (AL)"), + ("AK", "Alaska (AK)"), + ("AS", "American Samoa (AS)"), + ("AZ", "Arizona (AZ)"), + ("AR", "Arkansas (AR)"), + ("CA", "California (CA)"), + ("CO", "Colorado (CO)"), + ("CT", "Connecticut (CT)"), + ("DE", "Delaware (DE)"), + ("DC", "District of Columbia (DC)"), + ("FL", "Florida (FL)"), + ("GA", "Georgia (GA)"), + ("GU", "Guam (GU)"), + ("HI", "Hawaii (HI)"), + ("ID", "Idaho (ID)"), + ("IL", "Illinois (IL)"), + ("IN", "Indiana (IN)"), + ("IA", "Iowa (IA)"), + ("KS", "Kansas (KS)"), + ("KY", "Kentucky (KY)"), + ("LA", "Louisiana (LA)"), + ("ME", "Maine (ME)"), + ("MD", "Maryland (MD)"), + ("MA", "Massachusetts (MA)"), + ("MI", "Michigan (MI)"), + ("MN", "Minnesota (MN)"), + ("MS", "Mississippi (MS)"), + ("MO", "Missouri (MO)"), + ("MT", "Montana (MT)"), + ("NE", "Nebraska (NE)"), + ("NV", "Nevada (NV)"), + ("NH", "New Hampshire (NH)"), + ("NJ", "New Jersey (NJ)"), + ("NM", "New Mexico (NM)"), + ("NY", "New York (NY)"), + ("NC", "North Carolina (NC)"), + ("ND", "North Dakota (ND)"), + ("MP", "Northern Mariana Islands (MP)"), + ("OH", "Ohio (OH)"), + ("OK", "Oklahoma (OK)"), + ("OR", "Oregon (OR)"), + ("PA", "Pennsylvania (PA)"), + ("PR", "Puerto Rico (PR)"), + ("RI", "Rhode Island (RI)"), + ("SC", "South Carolina (SC)"), + ("SD", "South Dakota (SD)"), + ("TN", "Tennessee (TN)"), + ("TX", "Texas (TX)"), + ("UM", "United States Minor Outlying Islands (UM)"), + ("UT", "Utah (UT)"), + ("VT", "Vermont (VT)"), + ("VI", "Virgin Islands (VI)"), + ("VA", "Virginia (VA)"), + ("WA", "Washington (WA)"), + ("WV", "West Virginia (WV)"), + ("WI", "Wisconsin (WI)"), + ("WY", "Wyoming (WY)"), + ("AA", "Armed Forces Americas (AA)"), + ("AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)"), + ("AP", "Armed Forces Pacific (AP)"), + ], + max_length=2, + null=True, + verbose_name="State, territory, or military post", + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="submitter", + field=models.ForeignKey( + blank=True, + help_text='Person listed under "your contact information" in the request form', + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="submitted_domain_requests_information", + to="registrar.contact", + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="tribe_name", + field=models.CharField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="urbanization", + field=models.CharField( + blank=True, + help_text="Required for Puerto Rico only", + null=True, + verbose_name="Urbanization (required for Puerto Rico only)", + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="zipcode", + field=models.CharField(blank=True, db_index=True, max_length=10, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="about_your_organization", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="address_line1", + field=models.CharField(blank=True, null=True, verbose_name="Address line 1"), + ), + migrations.AlterField( + model_name="domainrequest", + name="address_line2", + field=models.CharField(blank=True, null=True, verbose_name="Address line 2"), + ), + migrations.AlterField( + model_name="domainrequest", + name="alternative_domains", + field=models.ManyToManyField( + blank=True, + help_text="Other domain names the creator provided for consideration", + related_name="alternatives+", + to="registrar.website", + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="anything_else", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="approved_domain", + field=models.OneToOneField( + blank=True, + help_text="Domain associated with this request; will be blank until request is approved", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="domain_request", + to="registrar.domain", + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="city", + field=models.CharField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="creator", + field=models.ForeignKey( + help_text="Person who submitted the domain request; will not receive email updates", + on_delete=django.db.models.deletion.PROTECT, + related_name="domain_requests_created", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="federal_agency", + field=models.CharField( + blank=True, + choices=[ + ( + "Administrative Conference of the United States", + "Administrative Conference of the United States", + ), + ("Advisory Council on Historic Preservation", "Advisory Council on Historic Preservation"), + ("American Battle Monuments Commission", "American Battle Monuments Commission"), + ("AMTRAK", "AMTRAK"), + ("Appalachian Regional Commission", "Appalachian Regional Commission"), + ( + "Appraisal Subcommittee of the Federal Financial Institutions Examination Council", + "Appraisal Subcommittee of the Federal Financial Institutions Examination Council", + ), + ("Appraisal Subcommittee", "Appraisal Subcommittee"), + ("Architect of the Capitol", "Architect of the Capitol"), + ("Armed Forces Retirement Home", "Armed Forces Retirement Home"), + ( + "Barry Goldwater Scholarship and Excellence in Education Foundation", + "Barry Goldwater Scholarship and Excellence in Education Foundation", + ), + ( + "Barry Goldwater Scholarship and Excellence in Education Program", + "Barry Goldwater Scholarship and Excellence in Education Program", + ), + ("Central Intelligence Agency", "Central Intelligence Agency"), + ("Chemical Safety Board", "Chemical Safety Board"), + ("Christopher Columbus Fellowship Foundation", "Christopher Columbus Fellowship Foundation"), + ("Civil Rights Cold Case Records Review Board", "Civil Rights Cold Case Records Review Board"), + ( + "Commission for the Preservation of America's Heritage Abroad", + "Commission for the Preservation of America's Heritage Abroad", + ), + ("Commission of Fine Arts", "Commission of Fine Arts"), + ( + "Committee for Purchase From People Who Are Blind or Severely Disabled", + "Committee for Purchase From People Who Are Blind or Severely Disabled", + ), + ("Commodity Futures Trading Commission", "Commodity Futures Trading Commission"), + ("Congressional Budget Office", "Congressional Budget Office"), + ("Consumer Financial Protection Bureau", "Consumer Financial Protection Bureau"), + ("Consumer Product Safety Commission", "Consumer Product Safety Commission"), + ("Corporation for National & Community Service", "Corporation for National & Community Service"), + ( + "Corporation for National and Community Service", + "Corporation for National and Community Service", + ), + ( + "Council of Inspectors General on Integrity and Efficiency", + "Council of Inspectors General on Integrity and Efficiency", + ), + ("Court Services and Offender Supervision", "Court Services and Offender Supervision"), + ("Cyberspace Solarium Commission", "Cyberspace Solarium Commission"), + ( + "DC Court Services and Offender Supervision Agency", + "DC Court Services and Offender Supervision Agency", + ), + ("DC Pre-trial Services", "DC Pre-trial Services"), + ("Defense Nuclear Facilities Safety Board", "Defense Nuclear Facilities Safety Board"), + ("Delta Regional Authority", "Delta Regional Authority"), + ("Denali Commission", "Denali Commission"), + ("Department of Agriculture", "Department of Agriculture"), + ("Department of Commerce", "Department of Commerce"), + ("Department of Defense", "Department of Defense"), + ("Department of Education", "Department of Education"), + ("Department of Energy", "Department of Energy"), + ("Department of Health and Human Services", "Department of Health and Human Services"), + ("Department of Homeland Security", "Department of Homeland Security"), + ("Department of Housing and Urban Development", "Department of Housing and Urban Development"), + ("Department of Justice", "Department of Justice"), + ("Department of Labor", "Department of Labor"), + ("Department of State", "Department of State"), + ("Department of the Interior", "Department of the Interior"), + ("Department of the Treasury", "Department of the Treasury"), + ("Department of Transportation", "Department of Transportation"), + ("Department of Veterans Affairs", "Department of Veterans Affairs"), + ("Director of National Intelligence", "Director of National Intelligence"), + ("Dwight D. Eisenhower Memorial Commission", "Dwight D. Eisenhower Memorial Commission"), + ("Election Assistance Commission", "Election Assistance Commission"), + ("Environmental Protection Agency", "Environmental Protection Agency"), + ("Equal Employment Opportunity Commission", "Equal Employment Opportunity Commission"), + ("Executive Office of the President", "Executive Office of the President"), + ("Export-Import Bank of the United States", "Export-Import Bank of the United States"), + ("Export/Import Bank of the U.S.", "Export/Import Bank of the U.S."), + ("Farm Credit Administration", "Farm Credit Administration"), + ("Farm Credit System Insurance Corporation", "Farm Credit System Insurance Corporation"), + ("Federal Communications Commission", "Federal Communications Commission"), + ("Federal Deposit Insurance Corporation", "Federal Deposit Insurance Corporation"), + ("Federal Election Commission", "Federal Election Commission"), + ("Federal Energy Regulatory Commission", "Federal Energy Regulatory Commission"), + ( + "Federal Financial Institutions Examination Council", + "Federal Financial Institutions Examination Council", + ), + ("Federal Housing Finance Agency", "Federal Housing Finance Agency"), + ("Federal Judiciary", "Federal Judiciary"), + ("Federal Labor Relations Authority", "Federal Labor Relations Authority"), + ("Federal Maritime Commission", "Federal Maritime Commission"), + ("Federal Mediation and Conciliation Service", "Federal Mediation and Conciliation Service"), + ( + "Federal Mine Safety and Health Review Commission", + "Federal Mine Safety and Health Review Commission", + ), + ( + "Federal Permitting Improvement Steering Council", + "Federal Permitting Improvement Steering Council", + ), + ("Federal Reserve Board of Governors", "Federal Reserve Board of Governors"), + ("Federal Reserve System", "Federal Reserve System"), + ("Federal Trade Commission", "Federal Trade Commission"), + ("General Services Administration", "General Services Administration"), + ("gov Administration", "gov Administration"), + ("Government Accountability Office", "Government Accountability Office"), + ("Government Publishing Office", "Government Publishing Office"), + ("Gulf Coast Ecosystem Restoration Council", "Gulf Coast Ecosystem Restoration Council"), + ("Harry S Truman Scholarship Foundation", "Harry S Truman Scholarship Foundation"), + ("Harry S. Truman Scholarship Foundation", "Harry S. Truman Scholarship Foundation"), + ("Institute of Museum and Library Services", "Institute of Museum and Library Services"), + ("Institute of Peace", "Institute of Peace"), + ("Inter-American Foundation", "Inter-American Foundation"), + ( + "International Boundary and Water Commission: United States and Mexico", + "International Boundary and Water Commission: United States and Mexico", + ), + ( + "International Boundary Commission: United States and Canada", + "International Boundary Commission: United States and Canada", + ), + ( + "International Joint Commission: United States and Canada", + "International Joint Commission: United States and Canada", + ), + ("James Madison Memorial Fellowship Foundation", "James Madison Memorial Fellowship Foundation"), + ("Japan-United States Friendship Commission", "Japan-United States Friendship Commission"), + ("Japan-US Friendship Commission", "Japan-US Friendship Commission"), + ("John F. Kennedy Center for Performing Arts", "John F. Kennedy Center for Performing Arts"), + ( + "John F. Kennedy Center for the Performing Arts", + "John F. Kennedy Center for the Performing Arts", + ), + ("Legal Services Corporation", "Legal Services Corporation"), + ("Legislative Branch", "Legislative Branch"), + ("Library of Congress", "Library of Congress"), + ("Marine Mammal Commission", "Marine Mammal Commission"), + ( + "Medicaid and CHIP Payment and Access Commission", + "Medicaid and CHIP Payment and Access Commission", + ), + ("Medical Payment Advisory Commission", "Medical Payment Advisory Commission"), + ("Medicare Payment Advisory Commission", "Medicare Payment Advisory Commission"), + ("Merit Systems Protection Board", "Merit Systems Protection Board"), + ("Millennium Challenge Corporation", "Millennium Challenge Corporation"), + ( + "Morris K. Udall and Stewart L. Udall Foundation", + "Morris K. Udall and Stewart L. Udall Foundation", + ), + ("National Aeronautics and Space Administration", "National Aeronautics and Space Administration"), + ("National Archives and Records Administration", "National Archives and Records Administration"), + ("National Capital Planning Commission", "National Capital Planning Commission"), + ("National Council on Disability", "National Council on Disability"), + ("National Credit Union Administration", "National Credit Union Administration"), + ("National Endowment for the Arts", "National Endowment for the Arts"), + ("National Endowment for the Humanities", "National Endowment for the Humanities"), + ( + "National Foundation on the Arts and the Humanities", + "National Foundation on the Arts and the Humanities", + ), + ("National Gallery of Art", "National Gallery of Art"), + ("National Indian Gaming Commission", "National Indian Gaming Commission"), + ("National Labor Relations Board", "National Labor Relations Board"), + ("National Mediation Board", "National Mediation Board"), + ("National Science Foundation", "National Science Foundation"), + ( + "National Security Commission on Artificial Intelligence", + "National Security Commission on Artificial Intelligence", + ), + ("National Transportation Safety Board", "National Transportation Safety Board"), + ( + "Networking Information Technology Research and Development", + "Networking Information Technology Research and Development", + ), + ("Non-Federal Agency", "Non-Federal Agency"), + ("Northern Border Regional Commission", "Northern Border Regional Commission"), + ("Nuclear Regulatory Commission", "Nuclear Regulatory Commission"), + ("Nuclear Safety Oversight Committee", "Nuclear Safety Oversight Committee"), + ("Nuclear Waste Technical Review Board", "Nuclear Waste Technical Review Board"), + ( + "Occupational Safety & Health Review Commission", + "Occupational Safety & Health Review Commission", + ), + ( + "Occupational Safety and Health Review Commission", + "Occupational Safety and Health Review Commission", + ), + ("Office of Compliance", "Office of Compliance"), + ("Office of Congressional Workplace Rights", "Office of Congressional Workplace Rights"), + ("Office of Government Ethics", "Office of Government Ethics"), + ("Office of Navajo and Hopi Indian Relocation", "Office of Navajo and Hopi Indian Relocation"), + ("Office of Personnel Management", "Office of Personnel Management"), + ("Open World Leadership Center", "Open World Leadership Center"), + ("Overseas Private Investment Corporation", "Overseas Private Investment Corporation"), + ("Peace Corps", "Peace Corps"), + ("Pension Benefit Guaranty Corporation", "Pension Benefit Guaranty Corporation"), + ("Postal Regulatory Commission", "Postal Regulatory Commission"), + ("Presidio Trust", "Presidio Trust"), + ("Privacy and Civil Liberties Oversight Board", "Privacy and Civil Liberties Oversight Board"), + ("Public Buildings Reform Board", "Public Buildings Reform Board"), + ( + "Public Defender Service for the District of Columbia", + "Public Defender Service for the District of Columbia", + ), + ("Railroad Retirement Board", "Railroad Retirement Board"), + ("Securities and Exchange Commission", "Securities and Exchange Commission"), + ("Selective Service System", "Selective Service System"), + ("Small Business Administration", "Small Business Administration"), + ("Smithsonian Institution", "Smithsonian Institution"), + ("Social Security Administration", "Social Security Administration"), + ("Social Security Advisory Board", "Social Security Advisory Board"), + ("Southeast Crescent Regional Commission", "Southeast Crescent Regional Commission"), + ("Southwest Border Regional Commission", "Southwest Border Regional Commission"), + ("State Justice Institute", "State Justice Institute"), + ("State, Local, and Tribal Government", "State, Local, and Tribal Government"), + ("Stennis Center for Public Service", "Stennis Center for Public Service"), + ("Surface Transportation Board", "Surface Transportation Board"), + ("Tennessee Valley Authority", "Tennessee Valley Authority"), + ("The Executive Office of the President", "The Executive Office of the President"), + ("The Intelligence Community", "The Intelligence Community"), + ("The Legislative Branch", "The Legislative Branch"), + ("The Supreme Court", "The Supreme Court"), + ( + "The United States World War One Centennial Commission", + "The United States World War One Centennial Commission", + ), + ("U.S. Access Board", "U.S. Access Board"), + ("U.S. Agency for Global Media", "U.S. Agency for Global Media"), + ("U.S. Agency for International Development", "U.S. Agency for International Development"), + ("U.S. Capitol Police", "U.S. Capitol Police"), + ("U.S. Chemical Safety Board", "U.S. Chemical Safety Board"), + ( + "U.S. China Economic and Security Review Commission", + "U.S. China Economic and Security Review Commission", + ), + ( + "U.S. Commission for the Preservation of Americas Heritage Abroad", + "U.S. Commission for the Preservation of Americas Heritage Abroad", + ), + ("U.S. Commission of Fine Arts", "U.S. Commission of Fine Arts"), + ("U.S. Commission on Civil Rights", "U.S. Commission on Civil Rights"), + ( + "U.S. Commission on International Religious Freedom", + "U.S. Commission on International Religious Freedom", + ), + ("U.S. Courts", "U.S. Courts"), + ("U.S. Department of Agriculture", "U.S. Department of Agriculture"), + ("U.S. Interagency Council on Homelessness", "U.S. Interagency Council on Homelessness"), + ("U.S. International Trade Commission", "U.S. International Trade Commission"), + ("U.S. Nuclear Waste Technical Review Board", "U.S. Nuclear Waste Technical Review Board"), + ("U.S. Office of Special Counsel", "U.S. Office of Special Counsel"), + ("U.S. Peace Corps", "U.S. Peace Corps"), + ("U.S. Postal Service", "U.S. Postal Service"), + ("U.S. Semiquincentennial Commission", "U.S. Semiquincentennial Commission"), + ("U.S. Trade and Development Agency", "U.S. Trade and Development Agency"), + ( + "U.S.-China Economic and Security Review Commission", + "U.S.-China Economic and Security Review Commission", + ), + ("Udall Foundation", "Udall Foundation"), + ("United States AbilityOne", "United States AbilityOne"), + ("United States Access Board", "United States Access Board"), + ("United States African Development Foundation", "United States African Development Foundation"), + ("United States Agency for Global Media", "United States Agency for Global Media"), + ("United States Arctic Research Commission", "United States Arctic Research Commission"), + ("United States Global Change Research Program", "United States Global Change Research Program"), + ("United States Holocaust Memorial Museum", "United States Holocaust Memorial Museum"), + ("United States Institute of Peace", "United States Institute of Peace"), + ( + "United States Interagency Council on Homelessness", + "United States Interagency Council on Homelessness", + ), + ( + "United States International Development Finance Corporation", + "United States International Development Finance Corporation", + ), + ("United States International Trade Commission", "United States International Trade Commission"), + ("United States Postal Service", "United States Postal Service"), + ("United States Senate", "United States Senate"), + ("United States Trade and Development Agency", "United States Trade and Development Agency"), + ( + "Utah Reclamation Mitigation and Conservation Commission", + "Utah Reclamation Mitigation and Conservation Commission", + ), + ("Vietnam Education Foundation", "Vietnam Education Foundation"), + ("Western Hemisphere Drug Policy Commission", "Western Hemisphere Drug Policy Commission"), + ( + "Woodrow Wilson International Center for Scholars", + "Woodrow Wilson International Center for Scholars", + ), + ("World War I Centennial Commission", "World War I Centennial Commission"), + ], + null=True, + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="federal_type", + field=models.CharField( + blank=True, + choices=[("executive", "Executive"), ("judicial", "Judicial"), ("legislative", "Legislative")], + max_length=50, + null=True, + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="federally_recognized_tribe", + field=models.BooleanField(null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="generic_org_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"), + ], + max_length=255, + null=True, + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="is_election_board", + field=models.BooleanField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="no_other_contacts_rationale", + field=models.TextField( + blank=True, help_text="Required if creator does not list other employees", null=True + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="notes", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="organization_name", + field=models.CharField(blank=True, db_index=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="purpose", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="requested_domain", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="domain_request", + to="registrar.draftdomain", + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="state_recognized_tribe", + field=models.BooleanField(null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="state_territory", + field=models.CharField( + blank=True, + choices=[ + ("AL", "Alabama (AL)"), + ("AK", "Alaska (AK)"), + ("AS", "American Samoa (AS)"), + ("AZ", "Arizona (AZ)"), + ("AR", "Arkansas (AR)"), + ("CA", "California (CA)"), + ("CO", "Colorado (CO)"), + ("CT", "Connecticut (CT)"), + ("DE", "Delaware (DE)"), + ("DC", "District of Columbia (DC)"), + ("FL", "Florida (FL)"), + ("GA", "Georgia (GA)"), + ("GU", "Guam (GU)"), + ("HI", "Hawaii (HI)"), + ("ID", "Idaho (ID)"), + ("IL", "Illinois (IL)"), + ("IN", "Indiana (IN)"), + ("IA", "Iowa (IA)"), + ("KS", "Kansas (KS)"), + ("KY", "Kentucky (KY)"), + ("LA", "Louisiana (LA)"), + ("ME", "Maine (ME)"), + ("MD", "Maryland (MD)"), + ("MA", "Massachusetts (MA)"), + ("MI", "Michigan (MI)"), + ("MN", "Minnesota (MN)"), + ("MS", "Mississippi (MS)"), + ("MO", "Missouri (MO)"), + ("MT", "Montana (MT)"), + ("NE", "Nebraska (NE)"), + ("NV", "Nevada (NV)"), + ("NH", "New Hampshire (NH)"), + ("NJ", "New Jersey (NJ)"), + ("NM", "New Mexico (NM)"), + ("NY", "New York (NY)"), + ("NC", "North Carolina (NC)"), + ("ND", "North Dakota (ND)"), + ("MP", "Northern Mariana Islands (MP)"), + ("OH", "Ohio (OH)"), + ("OK", "Oklahoma (OK)"), + ("OR", "Oregon (OR)"), + ("PA", "Pennsylvania (PA)"), + ("PR", "Puerto Rico (PR)"), + ("RI", "Rhode Island (RI)"), + ("SC", "South Carolina (SC)"), + ("SD", "South Dakota (SD)"), + ("TN", "Tennessee (TN)"), + ("TX", "Texas (TX)"), + ("UM", "United States Minor Outlying Islands (UM)"), + ("UT", "Utah (UT)"), + ("VT", "Vermont (VT)"), + ("VI", "Virgin Islands (VI)"), + ("VA", "Virginia (VA)"), + ("WA", "Washington (WA)"), + ("WV", "West Virginia (WV)"), + ("WI", "Wisconsin (WI)"), + ("WY", "Wyoming (WY)"), + ("AA", "Armed Forces Americas (AA)"), + ("AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)"), + ("AP", "Armed Forces Pacific (AP)"), + ], + max_length=2, + null=True, + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="submitter", + field=models.ForeignKey( + blank=True, + help_text='Person listed under "your contact information" in the request form; will receive email updates', + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="submitted_domain_requests", + to="registrar.contact", + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="tribe_name", + field=models.CharField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="urbanization", + field=models.CharField(blank=True, help_text="Required for Puetro Rico only", null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="zipcode", + field=models.CharField(blank=True, db_index=True, max_length=10, null=True), + ), + migrations.AlterField( + model_name="host", + name="domain", + field=models.ForeignKey( + help_text="Domain associated with this host", + on_delete=django.db.models.deletion.PROTECT, + related_name="host", + to="registrar.domain", + ), + ), + migrations.AlterField( + model_name="host", + name="name", + field=models.CharField(default=None, max_length=253), + ), + migrations.AlterField( + model_name="hostip", + name="address", + field=models.CharField( + default=None, max_length=46, validators=[django.core.validators.validate_ipv46_address] + ), + ), + migrations.AlterField( + model_name="hostip", + name="host", + field=models.ForeignKey( + help_text="IP associated with this host", + on_delete=django.db.models.deletion.PROTECT, + related_name="ip", + to="registrar.host", + ), + ), + migrations.AlterField( + model_name="user", + name="status", + field=models.CharField( + blank=True, + choices=[("restricted", "restricted")], + default=None, + help_text='Users in "restricted" status cannot make updates in the registrar or start a new request.', + max_length=10, + null=True, + ), + ), + migrations.AlterField( + model_name="verifiedbystaff", + name="email", + field=models.EmailField(db_index=True, max_length=254), + ), + migrations.AlterField( + model_name="verifiedbystaff", + name="notes", + field=models.TextField(), + ), + migrations.AlterField( + model_name="verifiedbystaff", + name="requestor", + field=models.ForeignKey( + blank=True, + help_text="Person who verified this user", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="verifiedby_user", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="website", + name="website", + field=models.CharField( + help_text="An alternative domain or current website listed on a domain request", max_length=255 + ), + ), + ] diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index e4026be87..d6decc7aa 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -1016,13 +1016,13 @@ class Domain(TimeStampedModel, DomainHelper): deleted = DateField( null=True, editable=False, - help_text="Will appear blank unless the domain is in \"deleted\" state", + help_text='Will appear blank unless the domain is in "deleted" state', ) first_ready = DateField( null=True, editable=False, - help_text="Date when this domain first moved into \"ready\" state; date will never change", + help_text='Date when this domain first moved into "ready" state; date will never change', ) def isActive(self): diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index 103e5068a..77d38ef15 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -175,7 +175,7 @@ class DomainInformation(TimeStampedModel): blank=True, related_name="submitted_domain_requests_information", on_delete=models.PROTECT, - help_text="Person listed under \"your contact information\" in the request form", + help_text='Person listed under "your contact information" in the request form', ) purpose = models.TextField( diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index 1cd66b464..cab3c1650 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -606,7 +606,7 @@ class DomainRequest(TimeStampedModel): blank=True, related_name="submitted_domain_requests", on_delete=models.PROTECT, - help_text="Person listed under \"your contact information\" in the request form; will receive email updates" + help_text='Person listed under "your contact information" in the request form; will receive email updates', ) purpose = models.TextField( diff --git a/src/registrar/models/host.py b/src/registrar/models/host.py index 642027592..52c785747 100644 --- a/src/registrar/models/host.py +++ b/src/registrar/models/host.py @@ -27,7 +27,7 @@ class Host(TimeStampedModel): "registrar.Domain", on_delete=models.PROTECT, related_name="host", # access this Host via the Domain as `domain.host` - help_text="Domain associated with this hosts", + help_text="Domain associated with this host", ) def __str__(self): diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index 2688ef57f..e7fc4c75d 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -33,6 +33,7 @@ class User(AbstractUser): default=None, # Set the default value to None null=True, # Allow the field to be null blank=True, # Allow the field to be blank + help_text='Users in "restricted" status cannot make updates in the registrar or start a new request.', ) domains = models.ManyToManyField( diff --git a/src/registrar/models/verified_by_staff.py b/src/registrar/models/verified_by_staff.py index a6d861504..c09dce822 100644 --- a/src/registrar/models/verified_by_staff.py +++ b/src/registrar/models/verified_by_staff.py @@ -9,7 +9,6 @@ class VerifiedByStaff(TimeStampedModel): email = models.EmailField( null=False, blank=False, - help_text="Email", db_index=True, ) @@ -19,12 +18,12 @@ class VerifiedByStaff(TimeStampedModel): blank=True, on_delete=models.SET_NULL, related_name="verifiedby_user", + help_text="Person who verified this user", ) notes = models.TextField( null=False, blank=False, - help_text="Notes", ) class Meta: diff --git a/src/registrar/models/website.py b/src/registrar/models/website.py index 29739b8ee..a062fe248 100644 --- a/src/registrar/models/website.py +++ b/src/registrar/models/website.py @@ -12,7 +12,7 @@ class Website(TimeStampedModel): website = models.CharField( max_length=255, null=False, - help_text="", + help_text="An alternative domain or current website listed on a domain request", ) def __str__(self) -> str: From f5116315e840218424508e329dd15dcbb2c838c4 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 15 Apr 2024 10:53:57 -0600 Subject: [PATCH 08/85] Override help text for AbstractUser --- src/registrar/admin.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 05bfc06b6..57c65199a 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -48,6 +48,35 @@ class MyUserAdminForm(UserChangeForm): "user_permissions": NoAutocompleteFilteredSelectMultiple("user_permissions", False), } + def __init__(self, *args, **kwargs): + """Custom init to modify the user form""" + super(MyUserAdminForm, self).__init__(*args, **kwargs) + self.override_base_help_texts() + + def override_base_help_texts(self): + """ + Used to override pre-existing help texts in AbstractUser. + This is done to avoid modifying the base AbstractUser class. + """ + is_superuser = self.fields.get("is_superuser") + is_staff = self.fields.get("is_staff") + password = self.fields.get("password") + + if is_superuser is not None: + is_superuser.help_text = ( + "For development purposes only; provides superuser access on the database level." + ) + + if is_staff is not None: + is_staff.help_text = "Designates whether the user can log in to this admin site." + + if password is not None: + link = f"../../{self.instance.pk}/password/" + password.help_text = ( + "Raw passwords are not stored, so they will not display here. " + f'You can change the password using this form.' + ) + class DomainInformationAdminForm(forms.ModelForm): """This form utilizes the custom widget for its class's ManyToMany UIs.""" From a8836d35aaa63e1d54cc8f61931047f0034b9caf Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 15 Apr 2024 11:03:46 -0600 Subject: [PATCH 09/85] Linting --- src/registrar/admin.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 57c65199a..fbfff218a 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -51,9 +51,9 @@ class MyUserAdminForm(UserChangeForm): def __init__(self, *args, **kwargs): """Custom init to modify the user form""" super(MyUserAdminForm, self).__init__(*args, **kwargs) - self.override_base_help_texts() + self._override_base_help_texts() - def override_base_help_texts(self): + def _override_base_help_texts(self): """ Used to override pre-existing help texts in AbstractUser. This is done to avoid modifying the base AbstractUser class. @@ -63,14 +63,13 @@ class MyUserAdminForm(UserChangeForm): password = self.fields.get("password") if is_superuser is not None: - is_superuser.help_text = ( - "For development purposes only; provides superuser access on the database level." - ) + is_superuser.help_text = "For development purposes only; provides superuser access on the database level." if is_staff is not None: is_staff.help_text = "Designates whether the user can log in to this admin site." if password is not None: + # Link is copied from the base implementation of UserChangeForm. link = f"../../{self.instance.pk}/password/" password.help_text = ( "Raw passwords are not stored, so they will not display here. " From 3c1c888356faf9b0eb91880ce65b56374ce30145 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 15 Apr 2024 11:33:21 -0600 Subject: [PATCH 10/85] Fix form field w/ refactor --- src/api/views.py | 1 + src/registrar/forms/domain_request_wizard.py | 12 ++++++++++-- ...information_cisa_representative_email_and_more.py | 6 +++--- src/registrar/models/domain_information.py | 1 + src/registrar/models/domain_request.py | 1 + .../templates/domain_request_additional_details.html | 1 - src/registrar/tests/test_admin.py | 1 + 7 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/api/views.py b/src/api/views.py index 2199e15ac..c7b4b2e70 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -72,6 +72,7 @@ def check_domain_available(domain): given domain doesn't end with .gov, ".gov" is added when looking for a match. If check fails, throws a RegistryError. """ + return True Domain = apps.get_model("registrar.Domain") if domain.endswith(".gov"): diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py index 6ddb68a74..78ddd8531 100644 --- a/src/registrar/forms/domain_request_wizard.py +++ b/src/registrar/forms/domain_request_wizard.py @@ -851,8 +851,16 @@ class NoOtherContactsForm(BaseDeletableRegistrarForm): class CisaRepresentativeForm(BaseDeletableRegistrarForm): cisa_representative_email = forms.EmailField( - required=False, - label="Are you working with a CISA representative?", # TODO-NL: (design check) - is this the right label? + required=True, + max_length=None, + error_messages={"invalid": ("Enter your email address in the required format, like name@example.com.")}, + label="Your representative’s email", + validators=[ + MaxLengthValidator( + 320, + message="Response must be less than 320 characters.", + ) + ], ) diff --git a/src/registrar/migrations/0084_domaininformation_cisa_representative_email_and_more.py b/src/registrar/migrations/0084_domaininformation_cisa_representative_email_and_more.py index 7f0d0b71e..ae645cf31 100644 --- a/src/registrar/migrations/0084_domaininformation_cisa_representative_email_and_more.py +++ b/src/registrar/migrations/0084_domaininformation_cisa_representative_email_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.10 on 2024-04-11 21:40 +# Generated by Django 4.2.10 on 2024-04-15 17:30 from django.db import migrations, models @@ -14,14 +14,14 @@ class Migration(migrations.Migration): model_name="domaininformation", name="cisa_representative_email", field=models.EmailField( - blank=True, db_index=True, max_length=254, null=True, verbose_name="CISA region representative" + blank=True, db_index=True, max_length=320, null=True, verbose_name="CISA region representative" ), ), migrations.AddField( model_name="domainrequest", name="cisa_representative_email", field=models.EmailField( - blank=True, db_index=True, max_length=254, null=True, verbose_name="CISA region representative" + blank=True, db_index=True, max_length=320, null=True, verbose_name="CISA region representative" ), ), migrations.AlterField( diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index dc7cae9fd..07e01bddb 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -221,6 +221,7 @@ class DomainInformation(TimeStampedModel): blank=True, db_index=True, verbose_name="CISA region representative", + max_length=320, ) is_policy_acknowledged = models.BooleanField( diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index 731034606..563a1e9b0 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -652,6 +652,7 @@ class DomainRequest(TimeStampedModel): blank=True, db_index=True, verbose_name="CISA region representative", + max_length=320, ) is_policy_acknowledged = models.BooleanField( diff --git a/src/registrar/templates/domain_request_additional_details.html b/src/registrar/templates/domain_request_additional_details.html index 430d47472..44a725552 100644 --- a/src/registrar/templates/domain_request_additional_details.html +++ b/src/registrar/templates/domain_request_additional_details.html @@ -26,7 +26,6 @@
-

Your representative’s email (*)

{% input_with_errors forms.1.cisa_representative_email %} {# forms.1 is a form for inputting the e-mail of a cisa representative #} diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index bd365718f..1df10d98d 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1678,6 +1678,7 @@ class TestDomainRequestAdmin(MockEppLib): "purpose", "no_other_contacts_rationale", "anything_else", + "cisa_representative_email", "is_policy_acknowledged", "submission_date", "notes", From 7013766d39e634e6cf72d0b958486339da766ed4 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 15 Apr 2024 11:47:42 -0600 Subject: [PATCH 11/85] Fix merge conflict --- ...5_domaininformation_cisa_representative_email_and_more.py} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/registrar/migrations/{0084_domaininformation_cisa_representative_email_and_more.py => 0085_domaininformation_cisa_representative_email_and_more.py} (90%) diff --git a/src/registrar/migrations/0084_domaininformation_cisa_representative_email_and_more.py b/src/registrar/migrations/0085_domaininformation_cisa_representative_email_and_more.py similarity index 90% rename from src/registrar/migrations/0084_domaininformation_cisa_representative_email_and_more.py rename to src/registrar/migrations/0085_domaininformation_cisa_representative_email_and_more.py index ae645cf31..71089c520 100644 --- a/src/registrar/migrations/0084_domaininformation_cisa_representative_email_and_more.py +++ b/src/registrar/migrations/0085_domaininformation_cisa_representative_email_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.10 on 2024-04-15 17:30 +# Generated by Django 4.2.10 on 2024-04-15 17:47 from django.db import migrations, models @@ -6,7 +6,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("registrar", "0083_alter_contact_email_alter_publiccontact_email"), + ("registrar", "0084_create_groups_v11"), ] operations = [ From 09b4e7487bdb2947566e032ed2eab79ff486689e Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 15 Apr 2024 12:27:35 -0600 Subject: [PATCH 12/85] Remove accidental return True --- src/api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/views.py b/src/api/views.py index c7b4b2e70..b36b3ee72 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -72,7 +72,7 @@ def check_domain_available(domain): given domain doesn't end with .gov, ".gov" is added when looking for a match. If check fails, throws a RegistryError. """ - return True + Domain = apps.get_model("registrar.Domain") if domain.endswith(".gov"): From 6e33dcfbe1a75d1a9d65baeb66ae518568c3df83 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 15 Apr 2024 12:28:26 -0600 Subject: [PATCH 13/85] Linting --- src/registrar/admin.py | 12 +++++++++++- src/registrar/models/domain_request.py | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 68bc0a4a4..5f6c71f45 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1172,7 +1172,17 @@ class DomainRequestAdmin(ListHeaderAdmin): }, ), (".gov domain", {"fields": ["requested_domain", "alternative_domains"]}), - ("Contacts", {"fields": ["authorizing_official", "other_contacts", "no_other_contacts_rationale", "cisa_representative_email"]}), + ( + "Contacts", + { + "fields": [ + "authorizing_official", + "other_contacts", + "no_other_contacts_rationale", + "cisa_representative_email", + ] + }, + ), ("Background info", {"fields": ["purpose", "anything_else", "current_websites"]}), ( "Type of organization", diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index 563a1e9b0..3fce90aa4 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -1048,7 +1048,7 @@ class DomainRequest(TimeStampedModel): def has_cisa_representative(self) -> bool: """Does this domain request have cisa representative?""" return self.cisa_representative_email != "" and self.cisa_representative_email is not None - + def has_additional_details(self) -> bool: return self.has_anything_else_text() or self.has_cisa_representative() From 78c0f470f2d781861e723068c300050a9ab5f47a Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 15 Apr 2024 12:37:50 -0600 Subject: [PATCH 14/85] Make anything else required --- src/registrar/forms/domain_request_wizard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py index 78ddd8531..0f3b0c3f3 100644 --- a/src/registrar/forms/domain_request_wizard.py +++ b/src/registrar/forms/domain_request_wizard.py @@ -891,7 +891,7 @@ class CisaRepresentativeYesNoForm(RegistrarForm): class AdditionalDetailsForm(BaseDeletableRegistrarForm): anything_else = forms.CharField( - required=False, + required=True, label="Anything else?", widget=forms.Textarea(), validators=[ From 18f7ac7f90d9cf493fdb357a1e161822e31e3df9 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 15 Apr 2024 13:49:05 -0600 Subject: [PATCH 15/85] Additional bug fixes --- src/registrar/admin.py | 30 +++++++++++------- .../migrations/0086_alter_domain_state.py | 31 +++++++++++++++++++ src/registrar/models/domain.py | 29 +++++++++++++++++ .../django/admin/domain_change_form.html | 5 ++- .../admin/includes/domain_fieldset.html | 12 +++++++ 5 files changed, 94 insertions(+), 13 deletions(-) create mode 100644 src/registrar/migrations/0086_alter_domain_state.py create mode 100644 src/registrar/templates/django/admin/includes/domain_fieldset.html diff --git a/src/registrar/admin.py b/src/registrar/admin.py index fbfff218a..adfbfd008 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1690,21 +1690,27 @@ class DomainAdmin(ListHeaderAdmin): # Pass in what the an extended expiration date would be for the expiration date modal if object_id is not None: domain = Domain.objects.get(pk=object_id) - years_to_extend_by = self._get_calculated_years_for_exp_date(domain) - - try: - curr_exp_date = domain.registry_expiration_date - except KeyError: - # No expiration date was found. Return none. - extra_context["extended_expiration_date"] = None - return super().changeform_view(request, object_id, form_url, extra_context) - new_date = curr_exp_date + relativedelta(years=years_to_extend_by) - extra_context["extended_expiration_date"] = new_date - else: - extra_context["extended_expiration_date"] = None + extra_context = self._set_expiration_date_context(domain, extra_context) + extra_context["state_help_message"] = Domain.State.get_admin_help_text(domain.state) return super().changeform_view(request, object_id, form_url, extra_context) + def _set_expiration_date_context(self, domain, extra_context): + """Given a domain, calculate the an extended expiration date + from the current registry expiration date.""" + years_to_extend_by = self._get_calculated_years_for_exp_date(domain) + + try: + curr_exp_date = domain.registry_expiration_date + except KeyError: + # No expiration date was found. Return none. + extra_context["extended_expiration_date"] = None + else: + new_date = curr_exp_date + relativedelta(years=years_to_extend_by) + extra_context["extended_expiration_date"] = new_date + + return extra_context + def response_change(self, request, obj): # Create dictionary of action functions ACTION_FUNCTIONS = { diff --git a/src/registrar/migrations/0086_alter_domain_state.py b/src/registrar/migrations/0086_alter_domain_state.py new file mode 100644 index 000000000..61455f56d --- /dev/null +++ b/src/registrar/migrations/0086_alter_domain_state.py @@ -0,0 +1,31 @@ +# Generated by Django 4.2.10 on 2024-04-15 19:26 + +from django.db import migrations +import django_fsm + + +class Migration(migrations.Migration): + + dependencies = [ + ("registrar", "0085_alter_domain_deleted_alter_domain_expiration_date_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="domain", + name="state", + field=django_fsm.FSMField( + choices=[ + ("unknown", "Unknown"), + ("dns needed", "Dns needed"), + ("ready", "Ready"), + ("on hold", "On hold"), + ("deleted", "Deleted"), + ], + default="unknown", + help_text=" ", + max_length=21, + protected=True, + ), + ), + ] diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index d6decc7aa..fd2431f6b 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -159,6 +159,32 @@ class Domain(TimeStampedModel, DomainHelper): return help_texts.get(state, "") + @classmethod + def get_admin_help_text(cls, state) -> str: + """Returns a help message for a desired state for /admin. If none is found, an empty string is returned""" + admin_help_texts = { + # For now, unknown has the same message as DNS_NEEDED + cls.UNKNOWN: ( + "The creator of the associated domain request has not logged in to " + "manage the domain since it was approved. " + 'The state will switch to "DNS needed" after they access the domain in the registrar.' + ), + cls.DNS_NEEDED: ( + "Before this domain can be used, name server addresses need to be added within the registrar." + ), + cls.READY: "This domain has name servers and is ready for use.", + cls.ON_HOLD: ( + "While on hold, this domain won't resolve in DNS and " + "any infrastructure (like websites) will be offline.", + ), + cls.DELETED: ( + "This domain was permanently removed from the registry. " + "The domain no longer resolves in DNS and any infrastructure (like websites) is offline.", + ), + } + + return admin_help_texts.get(state, "") + class Cache(property): """ Python descriptor to turn class methods into properties. @@ -1000,6 +1026,9 @@ class Domain(TimeStampedModel, DomainHelper): default=State.UNKNOWN, # cannot change state directly, particularly in Django admin protected=True, + # This must be defined for custom state help messages, + # as otherwise the view will purge the help field as it does not exist. + help_text=" ", ) expiration_date = DateField( diff --git a/src/registrar/templates/django/admin/domain_change_form.html b/src/registrar/templates/django/admin/domain_change_form.html index 44fe6851b..80888423d 100644 --- a/src/registrar/templates/django/admin/domain_change_form.html +++ b/src/registrar/templates/django/admin/domain_change_form.html @@ -33,7 +33,10 @@ {% endif %}
- {{ block.super }} + + {% for fieldset in adminform %} + {% include "django/admin/includes/domain_fieldset.html" with state_help_message=state_help_message %} + {% endfor %} {% endblock %} {% block submit_buttons_bottom %} diff --git a/src/registrar/templates/django/admin/includes/domain_fieldset.html b/src/registrar/templates/django/admin/includes/domain_fieldset.html new file mode 100644 index 000000000..484378c78 --- /dev/null +++ b/src/registrar/templates/django/admin/includes/domain_fieldset.html @@ -0,0 +1,12 @@ +{% extends "admin/fieldset.html" %} +{% load static url_helpers %} + + +{# .gov override #} +{% block help_text %} + {% if field.field.name == "state" %} +
+
{{ state_help_message }}
+
+ {% endif %} +{% endblock help_text %} From 007ff23587f875ad191d9414463197ff51b716bd Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 15 Apr 2024 13:49:51 -0600 Subject: [PATCH 16/85] Consolidate migrations --- ...d_alter_domain_expiration_date_and_more.py | 3 +- .../migrations/0086_alter_domain_state.py | 31 ------------------- 2 files changed, 2 insertions(+), 32 deletions(-) delete mode 100644 src/registrar/migrations/0086_alter_domain_state.py diff --git a/src/registrar/migrations/0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py b/src/registrar/migrations/0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py index 523fc522d..54aa6179d 100644 --- a/src/registrar/migrations/0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py +++ b/src/registrar/migrations/0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.10 on 2024-04-15 15:55 +# Generated by Django 4.2.10 on 2024-04-15 19:49 from django.conf import settings import django.core.validators @@ -53,6 +53,7 @@ class Migration(migrations.Migration): ("deleted", "Deleted"), ], default="unknown", + help_text=" ", max_length=21, protected=True, ), diff --git a/src/registrar/migrations/0086_alter_domain_state.py b/src/registrar/migrations/0086_alter_domain_state.py deleted file mode 100644 index 61455f56d..000000000 --- a/src/registrar/migrations/0086_alter_domain_state.py +++ /dev/null @@ -1,31 +0,0 @@ -# Generated by Django 4.2.10 on 2024-04-15 19:26 - -from django.db import migrations -import django_fsm - - -class Migration(migrations.Migration): - - dependencies = [ - ("registrar", "0085_alter_domain_deleted_alter_domain_expiration_date_and_more"), - ] - - operations = [ - migrations.AlterField( - model_name="domain", - name="state", - field=django_fsm.FSMField( - choices=[ - ("unknown", "Unknown"), - ("dns needed", "Dns needed"), - ("ready", "Ready"), - ("on hold", "On hold"), - ("deleted", "Deleted"), - ], - default="unknown", - help_text=" ", - max_length=21, - protected=True, - ), - ), - ] From f0e9cd1feced28ca71a0141afef3ee38465a923b Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 15 Apr 2024 13:53:59 -0600 Subject: [PATCH 17/85] Linting --- src/registrar/models/domain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index fd2431f6b..bf24bd0bc 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -160,7 +160,7 @@ class Domain(TimeStampedModel, DomainHelper): return help_texts.get(state, "") @classmethod - def get_admin_help_text(cls, state) -> str: + def get_admin_help_text(cls, state): """Returns a help message for a desired state for /admin. If none is found, an empty string is returned""" admin_help_texts = { # For now, unknown has the same message as DNS_NEEDED From 4a97f1df33a25af79e51373a8a2c0f18f5389993 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 15 Apr 2024 14:02:23 -0600 Subject: [PATCH 18/85] Update domain_fieldset.html --- .../templates/django/admin/includes/domain_fieldset.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/registrar/templates/django/admin/includes/domain_fieldset.html b/src/registrar/templates/django/admin/includes/domain_fieldset.html index 484378c78..4147a04e3 100644 --- a/src/registrar/templates/django/admin/includes/domain_fieldset.html +++ b/src/registrar/templates/django/admin/includes/domain_fieldset.html @@ -2,11 +2,12 @@ {% load static url_helpers %} -{# .gov override #} {% block help_text %} {% if field.field.name == "state" %}
+ {# .gov override #}
{{ state_help_message }}
+ {# end of .gov override #}
{% endif %} {% endblock help_text %} From 41c77f709a143b31cdea1cf9f9cca01ad950377f Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 15 Apr 2024 14:52:14 -0600 Subject: [PATCH 19/85] Unit test part 1 --- .../admin/includes/domain_fieldset.html | 14 +- src/registrar/tests/test_admin.py | 192 ++++++++++++++++++ 2 files changed, 199 insertions(+), 7 deletions(-) diff --git a/src/registrar/templates/django/admin/includes/domain_fieldset.html b/src/registrar/templates/django/admin/includes/domain_fieldset.html index 4147a04e3..a2c03189a 100644 --- a/src/registrar/templates/django/admin/includes/domain_fieldset.html +++ b/src/registrar/templates/django/admin/includes/domain_fieldset.html @@ -3,11 +3,11 @@ {% block help_text %} - {% if field.field.name == "state" %} -
- {# .gov override #} -
{{ state_help_message }}
- {# end of .gov override #} -
- {% endif %} +
+ {% if field.field.name == "state" %} +
{{ state_help_message }}
+ {% else %} +
{{ field.field.help_text|safe }}
+ {% endif %} +
{% endblock help_text %} diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index bf54efe60..f4d28b2c2 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -18,6 +18,7 @@ from registrar.admin import ( AuditedAdmin, ContactAdmin, DomainInformationAdmin, + MyHostAdmin, UserDomainRoleAdmin, VerifiedByStaffAdmin, ) @@ -30,6 +31,7 @@ from registrar.models import ( Contact, Website, DraftDomain, + Host, ) from registrar.models.user_domain_role import UserDomainRole from registrar.models.verified_by_staff import VerifiedByStaff @@ -74,6 +76,13 @@ class TestDomainAdmin(MockEppLib, WebTest): self.app.set_user(self.superuser.username) self.client.force_login(self.superuser) + # Add domain data + self.ready_domain, _ = Domain.objects.get_or_create(name="fakeready.gov", state=Domain.State.READY) + self.unknown_domain, _ = Domain.objects.get_or_create(name="fakeunknown.gov", state=Domain.State.UNKNOWN) + self.dns_domain, _ = Domain.objects.get_or_create(name="fakedns.gov", state=Domain.State.DNS_NEEDED) + self.hold_domain, _ = Domain.objects.get_or_create(name="fakehold.gov", state=Domain.State.ON_HOLD) + self.deleted_domain, _ = Domain.objects.get_or_create(name="fakedeleted.gov", state=Domain.State.DELETED) + # Contains some test tools self.test_helper = GenericTestHelper( factory=self.factory, @@ -85,6 +94,79 @@ class TestDomainAdmin(MockEppLib, WebTest): ) super().setUp() + def test_helper_text(self): + """ + Tests for the correct helper text on this page + """ + + # Create a ready domain with a preset expiration date + domain, _ = Domain.objects.get_or_create(name="fake.gov", state=Domain.State.READY) + + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/domain/{}/change/".format(domain.pk), + follow=True, + ) + + # Make sure the page loaded, and that we're on the right page + self.assertEqual(response.status_code, 200) + self.assertContains(response, domain.name) + + # These should exist in the response + expected_values = [ + ("expiration_date", "Date the domain expires in the registry"), + ("first_ready_at", 'Date when this domain first moved into "ready" state; date will never change'), + ("deleted_at", 'Will appear blank unless the domain is in "deleted" state') + ] + self.test_helper.assert_response_contains_distinct_values(response, expected_values) + + def test_helper_text_state(self): + """ + Tests for the correct state helper text on this page + """ + + expected_unknown_domain_message = ( + "The creator of the associated domain request has not logged in to " + "manage the domain since it was approved. " + 'The state will switch to "DNS needed" after they access the domain in the registrar.' + ) + expected_dns_message = ( + "Before this domain can be used, name server addresses need " + "to be added within the registrar." + ) + expected_hold_message = ( + "While on hold, this domain won't resolve in DNS and " + "any infrastructure (like websites) will be offline.", + ) + expected_deleted_message = ( + "This domain was permanently removed from the registry. " + "The domain no longer resolves in DNS and any infrastructure (like websites) is offline.", + ) + expected_messages = [ + (self.ready_domain, "This domain has name servers and is ready for use."), + (self.unknown_domain, expected_unknown_domain_message), + (self.dns_domain, expected_dns_message), + (self.hold_domain, expected_hold_message), + (self.deleted_domain, expected_deleted_message), + ] + + p = "userpass" + self.client.login(username="staffuser", password=p) + for domain, message in expected_messages: + with self.subTest(domain_state=domain.state): + response = self.client.get( + "/admin/registrar/domain/{}/change/".format(domain.pk), + follow=True, + ) + + # Make sure the page loaded, and that we're on the right page + self.assertEqual(response.status_code, 200) + self.assertContains(response, domain.name) + + # Check that the right help text exists + self.assertContains(response, message, count=1) + @patch("registrar.admin.DomainAdmin._get_current_date", return_value=date(2024, 1, 1)) def test_extend_expiration_date_button(self, mock_date_today): """ @@ -706,6 +788,38 @@ class TestDomainRequestAdmin(MockEppLib): ) self.mock_client = MockSESClient() + def test_helper_text(self): + """ + Tests for the correct helper text on this page + """ + + # Create a fake domain request and domain + domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW) + + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/domainrequest/{}/change/".format(domain_request.pk), + follow=True, + ) + + # Make sure the page loaded, and that we're on the right page + self.assertEqual(response.status_code, 200) + self.assertContains(response, domain_request.requested_domain.name) + + # These should exist in the response + expected_values = [ + ("creator", "Person who submitted the domain request; will not receive email updates"), + ("submitter", 'Person listed under "your contact information" in the request form; will receive email updates'), + ("approved_domain", 'Domain associated with this request; will be blank until request is approved'), + ("no_other_contacts_rationale", "Required if creator does not list other employees"), + ("alternative_domains", "Other domain names the creator provided for consideration"), + ("no_other_contacts_rationale", "Required if creator does not list other employees"), + ("Urbanization", "Required for Puerto Rico only") + ] + self.test_helper.assert_response_contains_distinct_values(response, expected_values) + + @less_console_noise_decorator def test_analyst_can_see_and_edit_alternative_domain(self): """Tests if an analyst can still see and edit the alternative domain field""" @@ -2237,6 +2351,53 @@ class DomainInvitationAdminTest(TestCase): self.assertContains(response, retrieved_html, count=1) + +class TestHostAdmin(TestCase): + def setUp(self): + """Setup environment for a mock admin user""" + super().setUp() + self.site = AdminSite() + self.factory = RequestFactory() + self.admin = MyHostAdmin(model=Host, admin_site=self.site) + self.client = Client(HTTP_HOST="localhost:8080") + self.superuser = create_superuser() + self.test_helper = GenericTestHelper( + factory=self.factory, + user=self.superuser, + admin=self.admin, + url="/admin/registrar/Host/", + model=Host, + ) + + def tearDown(self): + super().tearDown() + Host.objects.all().delete() + Domain.objects.all().delete() + + def test_helper_text(self): + """ + Tests for the correct helper text on this page + """ + domain, _ = Domain.objects.get_or_create(name="fake.gov", state=Domain.State.READY) + # Create a fake host + host, _ = Host.objects.get_or_create(name="ns1.test.gov", domain=domain) + + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/host/{}/change/".format(host.pk), + follow=True, + ) + + # Make sure the page loaded + self.assertEqual(response.status_code, 200) + + # These should exist in the response + expected_values = [ + ("domain", "Domain associated with this host"), + ] + self.test_helper.assert_response_contains_distinct_values(response, expected_values) + class TestDomainInformationAdmin(TestCase): def setUp(self): """Setup environment for a mock admin user""" @@ -2289,6 +2450,37 @@ class TestDomainInformationAdmin(TestCase): Contact.objects.all().delete() User.objects.all().delete() + def test_helper_text(self): + """ + Tests for the correct helper text on this page + """ + + # Create a fake domain request and domain + domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW) + domain_request.approve() + domain_info = DomainInformation.objects.filter(domain=domain_request.approved_domain).get() + + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/domaininformation/{}/change/".format(domain_info.pk), + follow=True, + ) + + # Make sure the page loaded, and that we're on the right page + self.assertEqual(response.status_code, 200) + self.assertContains(response, domain_info.domain.name) + + # These should exist in the response + expected_values = [ + ("creator", "Person who submitted the domain request"), + ("submitter", 'Person listed under "your contact information" in the request form'), + ("domain_request", 'Request associated with this domain'), + ("no_other_contacts_rationale", "Required if creator does not list other employees"), + ("urbanization", "Required for Puerto Rico only") + ] + self.test_helper.assert_response_contains_distinct_values(response, expected_values) + @less_console_noise_decorator def test_other_contacts_has_readonly_link(self): """Tests if the readonly other_contacts field has links""" From cb5e9e43ea27c18e1021c312e39fa265e4a69168 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 15 Apr 2024 15:07:24 -0600 Subject: [PATCH 20/85] Unit tests part 2 --- src/registrar/tests/test_admin.py | 89 ++++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 14 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index f4d28b2c2..61c02c1ce 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -117,7 +117,7 @@ class TestDomainAdmin(MockEppLib, WebTest): expected_values = [ ("expiration_date", "Date the domain expires in the registry"), ("first_ready_at", 'Date when this domain first moved into "ready" state; date will never change'), - ("deleted_at", 'Will appear blank unless the domain is in "deleted" state') + ("deleted_at", 'Will appear blank unless the domain is in "deleted" state'), ] self.test_helper.assert_response_contains_distinct_values(response, expected_values) @@ -132,8 +132,7 @@ class TestDomainAdmin(MockEppLib, WebTest): 'The state will switch to "DNS needed" after they access the domain in the registrar.' ) expected_dns_message = ( - "Before this domain can be used, name server addresses need " - "to be added within the registrar." + "Before this domain can be used, name server addresses need " "to be added within the registrar." ) expected_hold_message = ( "While on hold, this domain won't resolve in DNS and " @@ -810,16 +809,18 @@ class TestDomainRequestAdmin(MockEppLib): # These should exist in the response expected_values = [ ("creator", "Person who submitted the domain request; will not receive email updates"), - ("submitter", 'Person listed under "your contact information" in the request form; will receive email updates'), - ("approved_domain", 'Domain associated with this request; will be blank until request is approved'), + ( + "submitter", + 'Person listed under "your contact information" in the request form; will receive email updates', + ), + ("approved_domain", "Domain associated with this request; will be blank until request is approved"), ("no_other_contacts_rationale", "Required if creator does not list other employees"), ("alternative_domains", "Other domain names the creator provided for consideration"), ("no_other_contacts_rationale", "Required if creator does not list other employees"), - ("Urbanization", "Required for Puerto Rico only") + ("Urbanization", "Required for Puerto Rico only"), ] self.test_helper.assert_response_contains_distinct_values(response, expected_values) - @less_console_noise_decorator def test_analyst_can_see_and_edit_alternative_domain(self): """Tests if an analyst can still see and edit the alternative domain field""" @@ -2351,7 +2352,6 @@ class DomainInvitationAdminTest(TestCase): self.assertContains(response, retrieved_html, count=1) - class TestHostAdmin(TestCase): def setUp(self): """Setup environment for a mock admin user""" @@ -2368,7 +2368,7 @@ class TestHostAdmin(TestCase): url="/admin/registrar/Host/", model=Host, ) - + def tearDown(self): super().tearDown() Host.objects.all().delete() @@ -2398,6 +2398,7 @@ class TestHostAdmin(TestCase): ] self.test_helper.assert_response_contains_distinct_values(response, expected_values) + class TestDomainInformationAdmin(TestCase): def setUp(self): """Setup environment for a mock admin user""" @@ -2475,9 +2476,9 @@ class TestDomainInformationAdmin(TestCase): expected_values = [ ("creator", "Person who submitted the domain request"), ("submitter", 'Person listed under "your contact information" in the request form'), - ("domain_request", 'Request associated with this domain'), + ("domain_request", "Request associated with this domain"), ("no_other_contacts_rationale", "Required if creator does not list other employees"), - ("urbanization", "Required for Puerto Rico only") + ("urbanization", "Required for Puerto Rico only"), ] self.test_helper.assert_response_contains_distinct_values(response, expected_values) @@ -2825,7 +2826,7 @@ class UserDomainRoleAdminTest(TestCase): self.assertContains(response, "Joe Jones AntarcticPolarBears@example.com", count=1) -class ListHeaderAdminTest(TestCase): +class TestListHeaderAdmin(TestCase): def setUp(self): self.site = AdminSite() self.factory = RequestFactory() @@ -2898,10 +2899,38 @@ class ListHeaderAdminTest(TestCase): User.objects.all().delete() -class MyUserAdminTest(TestCase): +class TestMyUserAdmin(TestCase): def setUp(self): admin_site = AdminSite() self.admin = MyUserAdmin(model=get_user_model(), admin_site=admin_site) + self.client = Client(HTTP_HOST="localhost:8080") + self.superuser = create_superuser() + self.test_helper = GenericTestHelper(admin=self.admin) + + def test_helper_text(self): + """ + Tests for the correct helper text on this page + """ + user = create_user() + + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/user/{}/change/".format(user.pk), + follow=True, + ) + + # Make sure the page loaded + self.assertEqual(response.status_code, 200) + + # These should exist in the response + expected_values = [ + ("password", "Raw passwords are not stored, so they will not display here."), + ("status", 'Users in "restricted" status cannot make updates in the registrar or start a new request.'), + ("is_staff", "Designates whether the user can log in to this admin site"), + ("is_superuser", "For development purposes only; provides superuser access on the database level"), + ] + self.test_helper.assert_response_contains_distinct_values(response, expected_values) def test_list_display_without_username(self): with less_console_noise(): @@ -3417,10 +3446,42 @@ class ContactAdminTest(TestCase): User.objects.all().delete() -class VerifiedByStaffAdminTestCase(TestCase): +class TestVerifiedByStaffAdmin(TestCase): def setUp(self): + super().setUp() + self.site = AdminSite() self.superuser = create_superuser() + self.admin = VerifiedByStaffAdmin(model=VerifiedByStaff, admin_site=self.site) self.factory = RequestFactory() + self.client = Client(HTTP_HOST="localhost:8080") + self.test_helper = GenericTestHelper(admin=self.admin) + + def tearDown(self): + super().tearDown() + VerifiedByStaff.objects.all().delete() + User.objects.all().delete() + + def test_helper_text(self): + """ + Tests for the correct helper text on this page + """ + vip_instance, _ = VerifiedByStaff.objects.get_or_create(email="test@example.com", notes="Test Notes") + + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/verifiedbystaff/{}/change/".format(vip_instance.pk), + follow=True, + ) + + # Make sure the page loaded + self.assertEqual(response.status_code, 200) + + # These should exist in the response + expected_values = [ + ("requestor", "Person who verified this user"), + ] + self.test_helper.assert_response_contains_distinct_values(response, expected_values) def test_save_model_sets_user_field(self): with less_console_noise(): From 2aff68216d4098042e871c94abfd8bc01086fa85 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 15 Apr 2024 15:23:16 -0600 Subject: [PATCH 21/85] Clean up --- src/registrar/models/domain.py | 4 ++-- src/registrar/models/utility/generic_helper.py | 2 +- src/registrar/tests/test_admin.py | 7 ++++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index bf24bd0bc..89bf3b7d9 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -175,11 +175,11 @@ class Domain(TimeStampedModel, DomainHelper): cls.READY: "This domain has name servers and is ready for use.", cls.ON_HOLD: ( "While on hold, this domain won't resolve in DNS and " - "any infrastructure (like websites) will be offline.", + "any infrastructure (like websites) will be offline." ), cls.DELETED: ( "This domain was permanently removed from the registry. " - "The domain no longer resolves in DNS and any infrastructure (like websites) is offline.", + "The domain no longer resolves in DNS and any infrastructure (like websites) is offline." ), } diff --git a/src/registrar/models/utility/generic_helper.py b/src/registrar/models/utility/generic_helper.py index 32f767ede..a5f899d3c 100644 --- a/src/registrar/models/utility/generic_helper.py +++ b/src/registrar/models/utility/generic_helper.py @@ -154,7 +154,7 @@ class CreateOrUpdateOrganizationTypeHelper: # There is no avenue for this to occur in the UI, # as such - this can only occur if the object is initialized in this way. # Or if there are pre-existing data. - logger.warning( + logger.debug( "create_or_update_organization_type() -> is_election_board " f"cannot exist for {generic_org_type}. Setting to None." ) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 61c02c1ce..df4f87c46 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -132,15 +132,16 @@ class TestDomainAdmin(MockEppLib, WebTest): 'The state will switch to "DNS needed" after they access the domain in the registrar.' ) expected_dns_message = ( - "Before this domain can be used, name server addresses need " "to be added within the registrar." + "Before this domain can be used, name server addresses need " + "to be added within the registrar." ) expected_hold_message = ( "While on hold, this domain won't resolve in DNS and " - "any infrastructure (like websites) will be offline.", + "any infrastructure (like websites) will be offline." ) expected_deleted_message = ( "This domain was permanently removed from the registry. " - "The domain no longer resolves in DNS and any infrastructure (like websites) is offline.", + "The domain no longer resolves in DNS and any infrastructure (like websites) is offline." ) expected_messages = [ (self.ready_domain, "This domain has name servers and is ready for use."), From 498becd6c05b4e61dc3b5957749a5bc0e0c5c136 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 15 Apr 2024 15:31:10 -0600 Subject: [PATCH 22/85] Update test_admin.py --- src/registrar/tests/test_admin.py | 47 +------------------------------ 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index df4f87c46..28b7dfe48 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -94,6 +94,7 @@ class TestDomainAdmin(MockEppLib, WebTest): ) super().setUp() + @less_console_noise_decorator def test_helper_text(self): """ Tests for the correct helper text on this page @@ -121,52 +122,6 @@ class TestDomainAdmin(MockEppLib, WebTest): ] self.test_helper.assert_response_contains_distinct_values(response, expected_values) - def test_helper_text_state(self): - """ - Tests for the correct state helper text on this page - """ - - expected_unknown_domain_message = ( - "The creator of the associated domain request has not logged in to " - "manage the domain since it was approved. " - 'The state will switch to "DNS needed" after they access the domain in the registrar.' - ) - expected_dns_message = ( - "Before this domain can be used, name server addresses need " - "to be added within the registrar." - ) - expected_hold_message = ( - "While on hold, this domain won't resolve in DNS and " - "any infrastructure (like websites) will be offline." - ) - expected_deleted_message = ( - "This domain was permanently removed from the registry. " - "The domain no longer resolves in DNS and any infrastructure (like websites) is offline." - ) - expected_messages = [ - (self.ready_domain, "This domain has name servers and is ready for use."), - (self.unknown_domain, expected_unknown_domain_message), - (self.dns_domain, expected_dns_message), - (self.hold_domain, expected_hold_message), - (self.deleted_domain, expected_deleted_message), - ] - - p = "userpass" - self.client.login(username="staffuser", password=p) - for domain, message in expected_messages: - with self.subTest(domain_state=domain.state): - response = self.client.get( - "/admin/registrar/domain/{}/change/".format(domain.pk), - follow=True, - ) - - # Make sure the page loaded, and that we're on the right page - self.assertEqual(response.status_code, 200) - self.assertContains(response, domain.name) - - # Check that the right help text exists - self.assertContains(response, message, count=1) - @patch("registrar.admin.DomainAdmin._get_current_date", return_value=date(2024, 1, 1)) def test_extend_expiration_date_button(self, mock_date_today): """ From 8a9727e4c0cbadb9cac5e81242c07cbfdbd00442 Mon Sep 17 00:00:00 2001 From: CocoByte Date: Mon, 15 Apr 2024 18:08:06 -0600 Subject: [PATCH 23/85] test fix (also fixed error in summary page) --- .../templates/domain_request_review.html | 2 +- .../includes/summary_additional_details.html | 4 +-- src/registrar/tests/test_views_request.py | 25 ++++++++++++------- src/registrar/views/domain_request.py | 4 +-- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/registrar/templates/domain_request_review.html b/src/registrar/templates/domain_request_review.html index 09a930710..569fd717c 100644 --- a/src/registrar/templates/domain_request_review.html +++ b/src/registrar/templates/domain_request_review.html @@ -156,7 +156,7 @@ {% if step == Step.ADDITIONAL_DETAILS %} - {% include "includes/summary_additional_details.html" with domainRequest=DomainRequest %} + {% include "includes/summary_additional_details.html" with domainRequest=domain_request %} {% endif %} diff --git a/src/registrar/templates/includes/summary_additional_details.html b/src/registrar/templates/includes/summary_additional_details.html index 1009f4ff0..344f747b2 100644 --- a/src/registrar/templates/includes/summary_additional_details.html +++ b/src/registrar/templates/includes/summary_additional_details.html @@ -26,7 +26,7 @@ and condense down into one subsection) -->
{% if domainRequest.has_cisa_representative %} - domainRequest.cisa_representative_email + {{domainRequest.cisa_representative_email}} {% else %} (none) {% endif %} @@ -36,7 +36,7 @@ and condense down into one subsection) -->
{% if domainRequest.has_anything_else_text %} - domainRequest.anything_else + {{domainRequest.anything_else}} {% else %} No {% endif %} diff --git a/src/registrar/tests/test_views_request.py b/src/registrar/tests/test_views_request.py index a4cb210bc..80c7fb40a 100644 --- a/src/registrar/tests/test_views_request.py +++ b/src/registrar/tests/test_views_request.py @@ -356,33 +356,39 @@ class DomainRequestTests(TestWithUser, WebTest): # the post request should return a redirect to the next form in # the domain request page self.assertEqual(other_contacts_result.status_code, 302) - self.assertEqual(other_contacts_result["Location"], "/request/anything_else/") + self.assertEqual(other_contacts_result["Location"], "/request/additional_details/") num_pages_tested += 1 - # ---- ANYTHING ELSE PAGE ---- + # ---- ADDITIONAL DETAILS PAGE ---- # Follow the redirect to the next form page self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - anything_else_page = other_contacts_result.follow() - anything_else_form = anything_else_page.forms[0] + additional_details_page = other_contacts_result.follow() + additional_details_form = additional_details_page.forms[0] - anything_else_form["anything_else-anything_else"] = "Nothing else." + # load inputs with test data + + additional_details_form["additional_details-has_cisa_representative"] = "True" + additional_details_form["additional_details-has_anything_else_text"] = "True" + additional_details_form["additional_details-cisa_representative_email"] = "FakeEmail@gmail.com" + additional_details_form["additional_details-anything_else"] = "Nothing else." # test next button self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - anything_else_result = anything_else_form.submit() + 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_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 - self.assertEqual(anything_else_result.status_code, 302) - self.assertEqual(anything_else_result["Location"], "/request/requirements/") + self.assertEqual(additional_details_result.status_code, 302) + self.assertEqual(additional_details_result["Location"], "/request/requirements/") num_pages_tested += 1 # ---- REQUIREMENTS PAGE ---- # Follow the redirect to the next form page self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - requirements_page = anything_else_result.follow() + requirements_page = additional_details_result.follow() requirements_form = requirements_page.forms[0] requirements_form["requirements-is_policy_acknowledged"] = True @@ -434,6 +440,7 @@ class DomainRequestTests(TestWithUser, WebTest): self.assertContains(review_page, "Another Tester") self.assertContains(review_page, "testy2@town.com") self.assertContains(review_page, "(201) 555-5557") + self.assertContains(review_page, "FakeEmail@gmail.com") self.assertContains(review_page, "Nothing else.") # We can't test the modal itself as it relies on JS for init and triggering, diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py index 07f3f77f1..dc4e3b24c 100644 --- a/src/registrar/views/domain_request.py +++ b/src/registrar/views/domain_request.py @@ -45,7 +45,7 @@ class Step(StrEnum): PURPOSE = "purpose" YOUR_CONTACT = "your_contact" OTHER_CONTACTS = "other_contacts" - ADDITIONAL_DETAILS = "anything_else" + ADDITIONAL_DETAILS = "additional_details" REQUIREMENTS = "requirements" REVIEW = "review" @@ -365,7 +365,7 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView): self.domain_request.other_contacts.exists() or self.domain_request.no_other_contacts_rationale is not None ), - "anything_else": ( + "additional_details": ( (self.domain_request.anything_else is not None and self.domain_request.cisa_representative_email) or self.domain_request.is_policy_acknowledged is not None ), From 26832a611f9a1d3a976f0e3d73ac57411ab4ab2c Mon Sep 17 00:00:00 2001 From: CocoByte Date: Mon, 15 Apr 2024 18:14:31 -0600 Subject: [PATCH 24/85] linted --- src/registrar/tests/test_views_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/tests/test_views_request.py b/src/registrar/tests/test_views_request.py index 80c7fb40a..a84d6c374 100644 --- a/src/registrar/tests/test_views_request.py +++ b/src/registrar/tests/test_views_request.py @@ -366,7 +366,7 @@ class DomainRequestTests(TestWithUser, WebTest): additional_details_form = additional_details_page.forms[0] # load inputs with test data - + additional_details_form["additional_details-has_cisa_representative"] = "True" additional_details_form["additional_details-has_anything_else_text"] = "True" additional_details_form["additional_details-cisa_representative_email"] = "FakeEmail@gmail.com" From 6ee8e05923c360ec634e0412123589928a5fa938 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 16 Apr 2024 08:34:13 -0600 Subject: [PATCH 25/85] Fix javascript --- src/registrar/assets/js/get-gov.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js index a8e6008ef..69f916b7f 100644 --- a/src/registrar/assets/js/get-gov.js +++ b/src/registrar/assets/js/get-gov.js @@ -786,7 +786,7 @@ function hideDeletedForms() { * */ (function anythingElseFormListener() { - HookupYesNoListener("anything_else-has_anything_else_text",'anything-else', null) + HookupYesNoListener("additional_details-has_anything_else_text",'anything-else', null) })(); @@ -795,5 +795,5 @@ function hideDeletedForms() { * */ (function cisaRepresentativesFormListener() { - HookupYesNoListener("anything_else-has_cisa_representative",'cisa-representative', null) + HookupYesNoListener("additional_details-has_cisa_representative",'cisa-representative', null) })(); \ No newline at end of file From 46c0d846b6cecec2c6c23d2d85ed21f77376ff44 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 16 Apr 2024 09:17:39 -0600 Subject: [PATCH 26/85] Fix bug with anything else form --- src/registrar/views/domain_request.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py index dc4e3b24c..6bdec0ab9 100644 --- a/src/registrar/views/domain_request.py +++ b/src/registrar/views/domain_request.py @@ -585,13 +585,8 @@ class OtherContacts(DomainRequestWizard): # DONE-NL: rename this to "Additional Details" (note: this is a find-replace job. VS will not refactor properly) class AdditionalDetails(DomainRequestWizard): - # TODO-NL: Delete this old (original code for anything else) - # template_name = "domain_request_anything_else.html" - # forms = [forms.AdditionalDetailsForm] - template_name = "domain_request_additional_details.html" - # OLD: forms = [forms.OtherContactsYesNoForm, forms.OtherContactsFormSet, forms.NoOtherContactsForm] - # TODO-NL: (refactor) -- move form hookups into respective areas + forms = [ forms.CisaRepresentativeYesNoForm, forms.CisaRepresentativeForm, @@ -599,7 +594,6 @@ class AdditionalDetails(DomainRequestWizard): forms.AdditionalDetailsForm, ] - # TODO-NL: (refactor) -- move validation into respective areas def is_valid(self, forms: list) -> bool: # Validate Cisa Representative @@ -637,7 +631,7 @@ class AdditionalDetails(DomainRequestWizard): # mark the anything_else_form for deletion anything_else_form.mark_form_for_deletion() else: - anything_else_portion_is_valid = cisa_representative_email_form.is_valid() + anything_else_portion_is_valid = anything_else_form.is_valid() else: # if yes no form is invalid, no choice has been made # mark the anything_else_form for deletion From 1fa44a61f131f8a39fa43675207707553d15266e Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 16 Apr 2024 09:54:22 -0600 Subject: [PATCH 27/85] Code cleanup --- src/api/views.py | 1 - src/registrar/forms/domain_request_wizard.py | 53 ++++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/api/views.py b/src/api/views.py index b36b3ee72..2199e15ac 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -72,7 +72,6 @@ def check_domain_available(domain): given domain doesn't end with .gov, ".gov" is added when looking for a match. If check fails, throws a RegistryError. """ - Domain = apps.get_model("registrar.Domain") if domain.endswith(".gov"): diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py index 0f3b0c3f3..3be5c4cc8 100644 --- a/src/registrar/forms/domain_request_wizard.py +++ b/src/registrar/forms/domain_request_wizard.py @@ -864,13 +864,19 @@ class CisaRepresentativeForm(BaseDeletableRegistrarForm): ) -class CisaRepresentativeYesNoForm(RegistrarForm): +class BaseYesNoForm(RegistrarForm): + """Used for forms with a yes/no form with a hidden input on toggle""" + + form_is_checked = None + typed_choice_field_name = None + def __init__(self, *args, **kwargs): """Extend the initialization of the form from RegistrarForm __init__""" super().__init__(*args, **kwargs) + # set the initial value based on attributes of domain request if self.domain_request: - if self.domain_request.has_cisa_representative(): + if self.form_is_checked: initial_value = True else: initial_value = False @@ -878,8 +884,8 @@ class CisaRepresentativeYesNoForm(RegistrarForm): # No pre-selection for new domain requests initial_value = None - self.fields["has_cisa_representative"] = forms.TypedChoiceField( - coerce=lambda x: x.lower() == "true" if x is not None else None, # coerce strings to bool, excepting None + self.fields[self.typed_choice_field_name] = forms.TypedChoiceField( + coerce=lambda x: x.lower() == "true" if x is not None else None, choices=((True, "Yes"), (False, "No")), initial=initial_value, widget=forms.RadioSelect, @@ -889,6 +895,16 @@ class CisaRepresentativeYesNoForm(RegistrarForm): ) +class CisaRepresentativeYesNoForm(BaseYesNoForm): + """Yes/no toggle for the CISA regions question on additional details""" + + # Note that these can be set in __init__ if you need more fine-grained control + form_is_checked = property( + lambda self: self.domain_request.has_cisa_representative() if self.domain_request else False + ) + typed_choice_field_name = "has_cisa_representative" + + class AdditionalDetailsForm(BaseDeletableRegistrarForm): anything_else = forms.CharField( required=True, @@ -903,29 +919,14 @@ class AdditionalDetailsForm(BaseDeletableRegistrarForm): ) -class AdditionalDetailsYesNoForm(RegistrarForm): - def __init__(self, *args, **kwargs): - """Extend the initialization of the form from RegistrarForm __init__""" - super().__init__(*args, **kwargs) - # set the initial value based on attributes of domain request - if self.domain_request: - if self.domain_request.has_anything_else_text(): - initial_value = True - else: - initial_value = False - else: - # No pre-selection for new domain requests - initial_value = None +class AdditionalDetailsYesNoForm(BaseYesNoForm): + """Yes/no toggle for the anything else question on additional details""" - self.fields["has_anything_else_text"] = forms.TypedChoiceField( - coerce=lambda x: x.lower() == "true" if x is not None else None, # coerce strings to bool, excepting None - choices=((True, "Yes"), (False, "No")), - initial=initial_value, - widget=forms.RadioSelect, - error_messages={ - "required": "This question is required.", # TODO-NL: (design check) - is this required? - }, - ) + # Note that these can be set in __init__ if you need more fine-grained control + form_is_checked = property( + lambda self: self.domain_request.has_anything_else_text() if self.domain_request else False + ) + typed_choice_field_name = "has_anything_else_text" class RequirementsForm(RegistrarForm): From 4fd03891e5578e13f4bd60509d795a0ddd3b24a5 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 16 Apr 2024 10:14:14 -0600 Subject: [PATCH 28/85] Fix unit test --- ...d_alter_domain_expiration_date_and_more.py | 1177 ----------------- src/registrar/models/domain_request.py | 2 +- src/registrar/tests/test_admin.py | 49 +- 3 files changed, 46 insertions(+), 1182 deletions(-) delete mode 100644 src/registrar/migrations/0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py diff --git a/src/registrar/migrations/0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py b/src/registrar/migrations/0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py deleted file mode 100644 index 54aa6179d..000000000 --- a/src/registrar/migrations/0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py +++ /dev/null @@ -1,1177 +0,0 @@ -# Generated by Django 4.2.10 on 2024-04-15 19:49 - -from django.conf import settings -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion -import django_fsm -import registrar.models.utility.domain_field - - -class Migration(migrations.Migration): - - dependencies = [ - ("registrar", "0084_create_groups_v11"), - ] - - operations = [ - migrations.AlterField( - model_name="domain", - name="deleted", - field=models.DateField( - editable=False, help_text='Will appear blank unless the domain is in "deleted" state', null=True - ), - ), - migrations.AlterField( - model_name="domain", - name="expiration_date", - field=models.DateField(help_text="Date the domain expires in the registry", null=True), - ), - migrations.AlterField( - model_name="domain", - name="first_ready", - field=models.DateField( - editable=False, - help_text='Date when this domain first moved into "ready" state; date will never change', - null=True, - ), - ), - migrations.AlterField( - model_name="domain", - name="name", - field=registrar.models.utility.domain_field.DomainField(default=None, max_length=253, unique=True), - ), - migrations.AlterField( - model_name="domain", - name="state", - field=django_fsm.FSMField( - choices=[ - ("unknown", "Unknown"), - ("dns needed", "Dns needed"), - ("ready", "Ready"), - ("on hold", "On hold"), - ("deleted", "Deleted"), - ], - default="unknown", - help_text=" ", - max_length=21, - protected=True, - ), - ), - migrations.AlterField( - model_name="domaininformation", - name="about_your_organization", - field=models.TextField(blank=True, null=True), - ), - migrations.AlterField( - model_name="domaininformation", - name="address_line1", - field=models.CharField(blank=True, null=True, verbose_name="Street address"), - ), - migrations.AlterField( - model_name="domaininformation", - name="address_line2", - field=models.CharField(blank=True, null=True, verbose_name="Street address line 2 (optional)"), - ), - migrations.AlterField( - model_name="domaininformation", - name="anything_else", - field=models.TextField(blank=True, null=True), - ), - migrations.AlterField( - model_name="domaininformation", - name="city", - field=models.CharField(blank=True, null=True), - ), - migrations.AlterField( - model_name="domaininformation", - name="creator", - field=models.ForeignKey( - help_text="Person who submitted the domain request", - on_delete=django.db.models.deletion.PROTECT, - related_name="information_created", - to=settings.AUTH_USER_MODEL, - ), - ), - migrations.AlterField( - model_name="domaininformation", - name="domain", - field=models.OneToOneField( - blank=True, - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name="domain_info", - to="registrar.domain", - ), - ), - migrations.AlterField( - model_name="domaininformation", - name="domain_request", - field=models.OneToOneField( - blank=True, - help_text="Request associated with this domain", - null=True, - on_delete=django.db.models.deletion.PROTECT, - related_name="DomainRequest_info", - to="registrar.domainrequest", - ), - ), - migrations.AlterField( - model_name="domaininformation", - name="federal_agency", - field=models.CharField( - blank=True, - choices=[ - ( - "Administrative Conference of the United States", - "Administrative Conference of the United States", - ), - ("Advisory Council on Historic Preservation", "Advisory Council on Historic Preservation"), - ("American Battle Monuments Commission", "American Battle Monuments Commission"), - ("AMTRAK", "AMTRAK"), - ("Appalachian Regional Commission", "Appalachian Regional Commission"), - ( - "Appraisal Subcommittee of the Federal Financial Institutions Examination Council", - "Appraisal Subcommittee of the Federal Financial Institutions Examination Council", - ), - ("Appraisal Subcommittee", "Appraisal Subcommittee"), - ("Architect of the Capitol", "Architect of the Capitol"), - ("Armed Forces Retirement Home", "Armed Forces Retirement Home"), - ( - "Barry Goldwater Scholarship and Excellence in Education Foundation", - "Barry Goldwater Scholarship and Excellence in Education Foundation", - ), - ( - "Barry Goldwater Scholarship and Excellence in Education Program", - "Barry Goldwater Scholarship and Excellence in Education Program", - ), - ("Central Intelligence Agency", "Central Intelligence Agency"), - ("Chemical Safety Board", "Chemical Safety Board"), - ("Christopher Columbus Fellowship Foundation", "Christopher Columbus Fellowship Foundation"), - ("Civil Rights Cold Case Records Review Board", "Civil Rights Cold Case Records Review Board"), - ( - "Commission for the Preservation of America's Heritage Abroad", - "Commission for the Preservation of America's Heritage Abroad", - ), - ("Commission of Fine Arts", "Commission of Fine Arts"), - ( - "Committee for Purchase From People Who Are Blind or Severely Disabled", - "Committee for Purchase From People Who Are Blind or Severely Disabled", - ), - ("Commodity Futures Trading Commission", "Commodity Futures Trading Commission"), - ("Congressional Budget Office", "Congressional Budget Office"), - ("Consumer Financial Protection Bureau", "Consumer Financial Protection Bureau"), - ("Consumer Product Safety Commission", "Consumer Product Safety Commission"), - ("Corporation for National & Community Service", "Corporation for National & Community Service"), - ( - "Corporation for National and Community Service", - "Corporation for National and Community Service", - ), - ( - "Council of Inspectors General on Integrity and Efficiency", - "Council of Inspectors General on Integrity and Efficiency", - ), - ("Court Services and Offender Supervision", "Court Services and Offender Supervision"), - ("Cyberspace Solarium Commission", "Cyberspace Solarium Commission"), - ( - "DC Court Services and Offender Supervision Agency", - "DC Court Services and Offender Supervision Agency", - ), - ("DC Pre-trial Services", "DC Pre-trial Services"), - ("Defense Nuclear Facilities Safety Board", "Defense Nuclear Facilities Safety Board"), - ("Delta Regional Authority", "Delta Regional Authority"), - ("Denali Commission", "Denali Commission"), - ("Department of Agriculture", "Department of Agriculture"), - ("Department of Commerce", "Department of Commerce"), - ("Department of Defense", "Department of Defense"), - ("Department of Education", "Department of Education"), - ("Department of Energy", "Department of Energy"), - ("Department of Health and Human Services", "Department of Health and Human Services"), - ("Department of Homeland Security", "Department of Homeland Security"), - ("Department of Housing and Urban Development", "Department of Housing and Urban Development"), - ("Department of Justice", "Department of Justice"), - ("Department of Labor", "Department of Labor"), - ("Department of State", "Department of State"), - ("Department of the Interior", "Department of the Interior"), - ("Department of the Treasury", "Department of the Treasury"), - ("Department of Transportation", "Department of Transportation"), - ("Department of Veterans Affairs", "Department of Veterans Affairs"), - ("Director of National Intelligence", "Director of National Intelligence"), - ("Dwight D. Eisenhower Memorial Commission", "Dwight D. Eisenhower Memorial Commission"), - ("Election Assistance Commission", "Election Assistance Commission"), - ("Environmental Protection Agency", "Environmental Protection Agency"), - ("Equal Employment Opportunity Commission", "Equal Employment Opportunity Commission"), - ("Executive Office of the President", "Executive Office of the President"), - ("Export-Import Bank of the United States", "Export-Import Bank of the United States"), - ("Export/Import Bank of the U.S.", "Export/Import Bank of the U.S."), - ("Farm Credit Administration", "Farm Credit Administration"), - ("Farm Credit System Insurance Corporation", "Farm Credit System Insurance Corporation"), - ("Federal Communications Commission", "Federal Communications Commission"), - ("Federal Deposit Insurance Corporation", "Federal Deposit Insurance Corporation"), - ("Federal Election Commission", "Federal Election Commission"), - ("Federal Energy Regulatory Commission", "Federal Energy Regulatory Commission"), - ( - "Federal Financial Institutions Examination Council", - "Federal Financial Institutions Examination Council", - ), - ("Federal Housing Finance Agency", "Federal Housing Finance Agency"), - ("Federal Judiciary", "Federal Judiciary"), - ("Federal Labor Relations Authority", "Federal Labor Relations Authority"), - ("Federal Maritime Commission", "Federal Maritime Commission"), - ("Federal Mediation and Conciliation Service", "Federal Mediation and Conciliation Service"), - ( - "Federal Mine Safety and Health Review Commission", - "Federal Mine Safety and Health Review Commission", - ), - ( - "Federal Permitting Improvement Steering Council", - "Federal Permitting Improvement Steering Council", - ), - ("Federal Reserve Board of Governors", "Federal Reserve Board of Governors"), - ("Federal Reserve System", "Federal Reserve System"), - ("Federal Trade Commission", "Federal Trade Commission"), - ("General Services Administration", "General Services Administration"), - ("gov Administration", "gov Administration"), - ("Government Accountability Office", "Government Accountability Office"), - ("Government Publishing Office", "Government Publishing Office"), - ("Gulf Coast Ecosystem Restoration Council", "Gulf Coast Ecosystem Restoration Council"), - ("Harry S Truman Scholarship Foundation", "Harry S Truman Scholarship Foundation"), - ("Harry S. Truman Scholarship Foundation", "Harry S. Truman Scholarship Foundation"), - ("Institute of Museum and Library Services", "Institute of Museum and Library Services"), - ("Institute of Peace", "Institute of Peace"), - ("Inter-American Foundation", "Inter-American Foundation"), - ( - "International Boundary and Water Commission: United States and Mexico", - "International Boundary and Water Commission: United States and Mexico", - ), - ( - "International Boundary Commission: United States and Canada", - "International Boundary Commission: United States and Canada", - ), - ( - "International Joint Commission: United States and Canada", - "International Joint Commission: United States and Canada", - ), - ("James Madison Memorial Fellowship Foundation", "James Madison Memorial Fellowship Foundation"), - ("Japan-United States Friendship Commission", "Japan-United States Friendship Commission"), - ("Japan-US Friendship Commission", "Japan-US Friendship Commission"), - ("John F. Kennedy Center for Performing Arts", "John F. Kennedy Center for Performing Arts"), - ( - "John F. Kennedy Center for the Performing Arts", - "John F. Kennedy Center for the Performing Arts", - ), - ("Legal Services Corporation", "Legal Services Corporation"), - ("Legislative Branch", "Legislative Branch"), - ("Library of Congress", "Library of Congress"), - ("Marine Mammal Commission", "Marine Mammal Commission"), - ( - "Medicaid and CHIP Payment and Access Commission", - "Medicaid and CHIP Payment and Access Commission", - ), - ("Medical Payment Advisory Commission", "Medical Payment Advisory Commission"), - ("Medicare Payment Advisory Commission", "Medicare Payment Advisory Commission"), - ("Merit Systems Protection Board", "Merit Systems Protection Board"), - ("Millennium Challenge Corporation", "Millennium Challenge Corporation"), - ( - "Morris K. Udall and Stewart L. Udall Foundation", - "Morris K. Udall and Stewart L. Udall Foundation", - ), - ("National Aeronautics and Space Administration", "National Aeronautics and Space Administration"), - ("National Archives and Records Administration", "National Archives and Records Administration"), - ("National Capital Planning Commission", "National Capital Planning Commission"), - ("National Council on Disability", "National Council on Disability"), - ("National Credit Union Administration", "National Credit Union Administration"), - ("National Endowment for the Arts", "National Endowment for the Arts"), - ("National Endowment for the Humanities", "National Endowment for the Humanities"), - ( - "National Foundation on the Arts and the Humanities", - "National Foundation on the Arts and the Humanities", - ), - ("National Gallery of Art", "National Gallery of Art"), - ("National Indian Gaming Commission", "National Indian Gaming Commission"), - ("National Labor Relations Board", "National Labor Relations Board"), - ("National Mediation Board", "National Mediation Board"), - ("National Science Foundation", "National Science Foundation"), - ( - "National Security Commission on Artificial Intelligence", - "National Security Commission on Artificial Intelligence", - ), - ("National Transportation Safety Board", "National Transportation Safety Board"), - ( - "Networking Information Technology Research and Development", - "Networking Information Technology Research and Development", - ), - ("Non-Federal Agency", "Non-Federal Agency"), - ("Northern Border Regional Commission", "Northern Border Regional Commission"), - ("Nuclear Regulatory Commission", "Nuclear Regulatory Commission"), - ("Nuclear Safety Oversight Committee", "Nuclear Safety Oversight Committee"), - ("Nuclear Waste Technical Review Board", "Nuclear Waste Technical Review Board"), - ( - "Occupational Safety & Health Review Commission", - "Occupational Safety & Health Review Commission", - ), - ( - "Occupational Safety and Health Review Commission", - "Occupational Safety and Health Review Commission", - ), - ("Office of Compliance", "Office of Compliance"), - ("Office of Congressional Workplace Rights", "Office of Congressional Workplace Rights"), - ("Office of Government Ethics", "Office of Government Ethics"), - ("Office of Navajo and Hopi Indian Relocation", "Office of Navajo and Hopi Indian Relocation"), - ("Office of Personnel Management", "Office of Personnel Management"), - ("Open World Leadership Center", "Open World Leadership Center"), - ("Overseas Private Investment Corporation", "Overseas Private Investment Corporation"), - ("Peace Corps", "Peace Corps"), - ("Pension Benefit Guaranty Corporation", "Pension Benefit Guaranty Corporation"), - ("Postal Regulatory Commission", "Postal Regulatory Commission"), - ("Presidio Trust", "Presidio Trust"), - ("Privacy and Civil Liberties Oversight Board", "Privacy and Civil Liberties Oversight Board"), - ("Public Buildings Reform Board", "Public Buildings Reform Board"), - ( - "Public Defender Service for the District of Columbia", - "Public Defender Service for the District of Columbia", - ), - ("Railroad Retirement Board", "Railroad Retirement Board"), - ("Securities and Exchange Commission", "Securities and Exchange Commission"), - ("Selective Service System", "Selective Service System"), - ("Small Business Administration", "Small Business Administration"), - ("Smithsonian Institution", "Smithsonian Institution"), - ("Social Security Administration", "Social Security Administration"), - ("Social Security Advisory Board", "Social Security Advisory Board"), - ("Southeast Crescent Regional Commission", "Southeast Crescent Regional Commission"), - ("Southwest Border Regional Commission", "Southwest Border Regional Commission"), - ("State Justice Institute", "State Justice Institute"), - ("State, Local, and Tribal Government", "State, Local, and Tribal Government"), - ("Stennis Center for Public Service", "Stennis Center for Public Service"), - ("Surface Transportation Board", "Surface Transportation Board"), - ("Tennessee Valley Authority", "Tennessee Valley Authority"), - ("The Executive Office of the President", "The Executive Office of the President"), - ("The Intelligence Community", "The Intelligence Community"), - ("The Legislative Branch", "The Legislative Branch"), - ("The Supreme Court", "The Supreme Court"), - ( - "The United States World War One Centennial Commission", - "The United States World War One Centennial Commission", - ), - ("U.S. Access Board", "U.S. Access Board"), - ("U.S. Agency for Global Media", "U.S. Agency for Global Media"), - ("U.S. Agency for International Development", "U.S. Agency for International Development"), - ("U.S. Capitol Police", "U.S. Capitol Police"), - ("U.S. Chemical Safety Board", "U.S. Chemical Safety Board"), - ( - "U.S. China Economic and Security Review Commission", - "U.S. China Economic and Security Review Commission", - ), - ( - "U.S. Commission for the Preservation of Americas Heritage Abroad", - "U.S. Commission for the Preservation of Americas Heritage Abroad", - ), - ("U.S. Commission of Fine Arts", "U.S. Commission of Fine Arts"), - ("U.S. Commission on Civil Rights", "U.S. Commission on Civil Rights"), - ( - "U.S. Commission on International Religious Freedom", - "U.S. Commission on International Religious Freedom", - ), - ("U.S. Courts", "U.S. Courts"), - ("U.S. Department of Agriculture", "U.S. Department of Agriculture"), - ("U.S. Interagency Council on Homelessness", "U.S. Interagency Council on Homelessness"), - ("U.S. International Trade Commission", "U.S. International Trade Commission"), - ("U.S. Nuclear Waste Technical Review Board", "U.S. Nuclear Waste Technical Review Board"), - ("U.S. Office of Special Counsel", "U.S. Office of Special Counsel"), - ("U.S. Peace Corps", "U.S. Peace Corps"), - ("U.S. Postal Service", "U.S. Postal Service"), - ("U.S. Semiquincentennial Commission", "U.S. Semiquincentennial Commission"), - ("U.S. Trade and Development Agency", "U.S. Trade and Development Agency"), - ( - "U.S.-China Economic and Security Review Commission", - "U.S.-China Economic and Security Review Commission", - ), - ("Udall Foundation", "Udall Foundation"), - ("United States AbilityOne", "United States AbilityOne"), - ("United States Access Board", "United States Access Board"), - ("United States African Development Foundation", "United States African Development Foundation"), - ("United States Agency for Global Media", "United States Agency for Global Media"), - ("United States Arctic Research Commission", "United States Arctic Research Commission"), - ("United States Global Change Research Program", "United States Global Change Research Program"), - ("United States Holocaust Memorial Museum", "United States Holocaust Memorial Museum"), - ("United States Institute of Peace", "United States Institute of Peace"), - ( - "United States Interagency Council on Homelessness", - "United States Interagency Council on Homelessness", - ), - ( - "United States International Development Finance Corporation", - "United States International Development Finance Corporation", - ), - ("United States International Trade Commission", "United States International Trade Commission"), - ("United States Postal Service", "United States Postal Service"), - ("United States Senate", "United States Senate"), - ("United States Trade and Development Agency", "United States Trade and Development Agency"), - ( - "Utah Reclamation Mitigation and Conservation Commission", - "Utah Reclamation Mitigation and Conservation Commission", - ), - ("Vietnam Education Foundation", "Vietnam Education Foundation"), - ("Western Hemisphere Drug Policy Commission", "Western Hemisphere Drug Policy Commission"), - ( - "Woodrow Wilson International Center for Scholars", - "Woodrow Wilson International Center for Scholars", - ), - ("World War I Centennial Commission", "World War I Centennial Commission"), - ], - null=True, - ), - ), - migrations.AlterField( - model_name="domaininformation", - name="federal_type", - field=models.CharField( - blank=True, - choices=[("executive", "Executive"), ("judicial", "Judicial"), ("legislative", "Legislative")], - max_length=50, - null=True, - ), - ), - migrations.AlterField( - model_name="domaininformation", - name="federally_recognized_tribe", - field=models.BooleanField(null=True), - ), - migrations.AlterField( - model_name="domaininformation", - name="no_other_contacts_rationale", - field=models.TextField( - blank=True, help_text="Required if creator does not list other employees", null=True - ), - ), - migrations.AlterField( - model_name="domaininformation", - name="notes", - field=models.TextField(blank=True, null=True), - ), - migrations.AlterField( - model_name="domaininformation", - name="organization_name", - field=models.CharField(blank=True, db_index=True, null=True), - ), - migrations.AlterField( - model_name="domaininformation", - name="state_recognized_tribe", - field=models.BooleanField(null=True), - ), - migrations.AlterField( - model_name="domaininformation", - name="state_territory", - field=models.CharField( - blank=True, - choices=[ - ("AL", "Alabama (AL)"), - ("AK", "Alaska (AK)"), - ("AS", "American Samoa (AS)"), - ("AZ", "Arizona (AZ)"), - ("AR", "Arkansas (AR)"), - ("CA", "California (CA)"), - ("CO", "Colorado (CO)"), - ("CT", "Connecticut (CT)"), - ("DE", "Delaware (DE)"), - ("DC", "District of Columbia (DC)"), - ("FL", "Florida (FL)"), - ("GA", "Georgia (GA)"), - ("GU", "Guam (GU)"), - ("HI", "Hawaii (HI)"), - ("ID", "Idaho (ID)"), - ("IL", "Illinois (IL)"), - ("IN", "Indiana (IN)"), - ("IA", "Iowa (IA)"), - ("KS", "Kansas (KS)"), - ("KY", "Kentucky (KY)"), - ("LA", "Louisiana (LA)"), - ("ME", "Maine (ME)"), - ("MD", "Maryland (MD)"), - ("MA", "Massachusetts (MA)"), - ("MI", "Michigan (MI)"), - ("MN", "Minnesota (MN)"), - ("MS", "Mississippi (MS)"), - ("MO", "Missouri (MO)"), - ("MT", "Montana (MT)"), - ("NE", "Nebraska (NE)"), - ("NV", "Nevada (NV)"), - ("NH", "New Hampshire (NH)"), - ("NJ", "New Jersey (NJ)"), - ("NM", "New Mexico (NM)"), - ("NY", "New York (NY)"), - ("NC", "North Carolina (NC)"), - ("ND", "North Dakota (ND)"), - ("MP", "Northern Mariana Islands (MP)"), - ("OH", "Ohio (OH)"), - ("OK", "Oklahoma (OK)"), - ("OR", "Oregon (OR)"), - ("PA", "Pennsylvania (PA)"), - ("PR", "Puerto Rico (PR)"), - ("RI", "Rhode Island (RI)"), - ("SC", "South Carolina (SC)"), - ("SD", "South Dakota (SD)"), - ("TN", "Tennessee (TN)"), - ("TX", "Texas (TX)"), - ("UM", "United States Minor Outlying Islands (UM)"), - ("UT", "Utah (UT)"), - ("VT", "Vermont (VT)"), - ("VI", "Virgin Islands (VI)"), - ("VA", "Virginia (VA)"), - ("WA", "Washington (WA)"), - ("WV", "West Virginia (WV)"), - ("WI", "Wisconsin (WI)"), - ("WY", "Wyoming (WY)"), - ("AA", "Armed Forces Americas (AA)"), - ("AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)"), - ("AP", "Armed Forces Pacific (AP)"), - ], - max_length=2, - null=True, - verbose_name="State, territory, or military post", - ), - ), - migrations.AlterField( - model_name="domaininformation", - name="submitter", - field=models.ForeignKey( - blank=True, - help_text='Person listed under "your contact information" in the request form', - null=True, - on_delete=django.db.models.deletion.PROTECT, - related_name="submitted_domain_requests_information", - to="registrar.contact", - ), - ), - migrations.AlterField( - model_name="domaininformation", - name="tribe_name", - field=models.CharField(blank=True, null=True), - ), - migrations.AlterField( - model_name="domaininformation", - name="urbanization", - field=models.CharField( - blank=True, - help_text="Required for Puerto Rico only", - null=True, - verbose_name="Urbanization (required for Puerto Rico only)", - ), - ), - migrations.AlterField( - model_name="domaininformation", - name="zipcode", - field=models.CharField(blank=True, db_index=True, max_length=10, null=True), - ), - migrations.AlterField( - model_name="domainrequest", - name="about_your_organization", - field=models.TextField(blank=True, null=True), - ), - migrations.AlterField( - model_name="domainrequest", - name="address_line1", - field=models.CharField(blank=True, null=True, verbose_name="Address line 1"), - ), - migrations.AlterField( - model_name="domainrequest", - name="address_line2", - field=models.CharField(blank=True, null=True, verbose_name="Address line 2"), - ), - migrations.AlterField( - model_name="domainrequest", - name="alternative_domains", - field=models.ManyToManyField( - blank=True, - help_text="Other domain names the creator provided for consideration", - related_name="alternatives+", - to="registrar.website", - ), - ), - migrations.AlterField( - model_name="domainrequest", - name="anything_else", - field=models.TextField(blank=True, null=True), - ), - migrations.AlterField( - model_name="domainrequest", - name="approved_domain", - field=models.OneToOneField( - blank=True, - help_text="Domain associated with this request; will be blank until request is approved", - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="domain_request", - to="registrar.domain", - ), - ), - migrations.AlterField( - model_name="domainrequest", - name="city", - field=models.CharField(blank=True, null=True), - ), - migrations.AlterField( - model_name="domainrequest", - name="creator", - field=models.ForeignKey( - help_text="Person who submitted the domain request; will not receive email updates", - on_delete=django.db.models.deletion.PROTECT, - related_name="domain_requests_created", - to=settings.AUTH_USER_MODEL, - ), - ), - migrations.AlterField( - model_name="domainrequest", - name="federal_agency", - field=models.CharField( - blank=True, - choices=[ - ( - "Administrative Conference of the United States", - "Administrative Conference of the United States", - ), - ("Advisory Council on Historic Preservation", "Advisory Council on Historic Preservation"), - ("American Battle Monuments Commission", "American Battle Monuments Commission"), - ("AMTRAK", "AMTRAK"), - ("Appalachian Regional Commission", "Appalachian Regional Commission"), - ( - "Appraisal Subcommittee of the Federal Financial Institutions Examination Council", - "Appraisal Subcommittee of the Federal Financial Institutions Examination Council", - ), - ("Appraisal Subcommittee", "Appraisal Subcommittee"), - ("Architect of the Capitol", "Architect of the Capitol"), - ("Armed Forces Retirement Home", "Armed Forces Retirement Home"), - ( - "Barry Goldwater Scholarship and Excellence in Education Foundation", - "Barry Goldwater Scholarship and Excellence in Education Foundation", - ), - ( - "Barry Goldwater Scholarship and Excellence in Education Program", - "Barry Goldwater Scholarship and Excellence in Education Program", - ), - ("Central Intelligence Agency", "Central Intelligence Agency"), - ("Chemical Safety Board", "Chemical Safety Board"), - ("Christopher Columbus Fellowship Foundation", "Christopher Columbus Fellowship Foundation"), - ("Civil Rights Cold Case Records Review Board", "Civil Rights Cold Case Records Review Board"), - ( - "Commission for the Preservation of America's Heritage Abroad", - "Commission for the Preservation of America's Heritage Abroad", - ), - ("Commission of Fine Arts", "Commission of Fine Arts"), - ( - "Committee for Purchase From People Who Are Blind or Severely Disabled", - "Committee for Purchase From People Who Are Blind or Severely Disabled", - ), - ("Commodity Futures Trading Commission", "Commodity Futures Trading Commission"), - ("Congressional Budget Office", "Congressional Budget Office"), - ("Consumer Financial Protection Bureau", "Consumer Financial Protection Bureau"), - ("Consumer Product Safety Commission", "Consumer Product Safety Commission"), - ("Corporation for National & Community Service", "Corporation for National & Community Service"), - ( - "Corporation for National and Community Service", - "Corporation for National and Community Service", - ), - ( - "Council of Inspectors General on Integrity and Efficiency", - "Council of Inspectors General on Integrity and Efficiency", - ), - ("Court Services and Offender Supervision", "Court Services and Offender Supervision"), - ("Cyberspace Solarium Commission", "Cyberspace Solarium Commission"), - ( - "DC Court Services and Offender Supervision Agency", - "DC Court Services and Offender Supervision Agency", - ), - ("DC Pre-trial Services", "DC Pre-trial Services"), - ("Defense Nuclear Facilities Safety Board", "Defense Nuclear Facilities Safety Board"), - ("Delta Regional Authority", "Delta Regional Authority"), - ("Denali Commission", "Denali Commission"), - ("Department of Agriculture", "Department of Agriculture"), - ("Department of Commerce", "Department of Commerce"), - ("Department of Defense", "Department of Defense"), - ("Department of Education", "Department of Education"), - ("Department of Energy", "Department of Energy"), - ("Department of Health and Human Services", "Department of Health and Human Services"), - ("Department of Homeland Security", "Department of Homeland Security"), - ("Department of Housing and Urban Development", "Department of Housing and Urban Development"), - ("Department of Justice", "Department of Justice"), - ("Department of Labor", "Department of Labor"), - ("Department of State", "Department of State"), - ("Department of the Interior", "Department of the Interior"), - ("Department of the Treasury", "Department of the Treasury"), - ("Department of Transportation", "Department of Transportation"), - ("Department of Veterans Affairs", "Department of Veterans Affairs"), - ("Director of National Intelligence", "Director of National Intelligence"), - ("Dwight D. Eisenhower Memorial Commission", "Dwight D. Eisenhower Memorial Commission"), - ("Election Assistance Commission", "Election Assistance Commission"), - ("Environmental Protection Agency", "Environmental Protection Agency"), - ("Equal Employment Opportunity Commission", "Equal Employment Opportunity Commission"), - ("Executive Office of the President", "Executive Office of the President"), - ("Export-Import Bank of the United States", "Export-Import Bank of the United States"), - ("Export/Import Bank of the U.S.", "Export/Import Bank of the U.S."), - ("Farm Credit Administration", "Farm Credit Administration"), - ("Farm Credit System Insurance Corporation", "Farm Credit System Insurance Corporation"), - ("Federal Communications Commission", "Federal Communications Commission"), - ("Federal Deposit Insurance Corporation", "Federal Deposit Insurance Corporation"), - ("Federal Election Commission", "Federal Election Commission"), - ("Federal Energy Regulatory Commission", "Federal Energy Regulatory Commission"), - ( - "Federal Financial Institutions Examination Council", - "Federal Financial Institutions Examination Council", - ), - ("Federal Housing Finance Agency", "Federal Housing Finance Agency"), - ("Federal Judiciary", "Federal Judiciary"), - ("Federal Labor Relations Authority", "Federal Labor Relations Authority"), - ("Federal Maritime Commission", "Federal Maritime Commission"), - ("Federal Mediation and Conciliation Service", "Federal Mediation and Conciliation Service"), - ( - "Federal Mine Safety and Health Review Commission", - "Federal Mine Safety and Health Review Commission", - ), - ( - "Federal Permitting Improvement Steering Council", - "Federal Permitting Improvement Steering Council", - ), - ("Federal Reserve Board of Governors", "Federal Reserve Board of Governors"), - ("Federal Reserve System", "Federal Reserve System"), - ("Federal Trade Commission", "Federal Trade Commission"), - ("General Services Administration", "General Services Administration"), - ("gov Administration", "gov Administration"), - ("Government Accountability Office", "Government Accountability Office"), - ("Government Publishing Office", "Government Publishing Office"), - ("Gulf Coast Ecosystem Restoration Council", "Gulf Coast Ecosystem Restoration Council"), - ("Harry S Truman Scholarship Foundation", "Harry S Truman Scholarship Foundation"), - ("Harry S. Truman Scholarship Foundation", "Harry S. Truman Scholarship Foundation"), - ("Institute of Museum and Library Services", "Institute of Museum and Library Services"), - ("Institute of Peace", "Institute of Peace"), - ("Inter-American Foundation", "Inter-American Foundation"), - ( - "International Boundary and Water Commission: United States and Mexico", - "International Boundary and Water Commission: United States and Mexico", - ), - ( - "International Boundary Commission: United States and Canada", - "International Boundary Commission: United States and Canada", - ), - ( - "International Joint Commission: United States and Canada", - "International Joint Commission: United States and Canada", - ), - ("James Madison Memorial Fellowship Foundation", "James Madison Memorial Fellowship Foundation"), - ("Japan-United States Friendship Commission", "Japan-United States Friendship Commission"), - ("Japan-US Friendship Commission", "Japan-US Friendship Commission"), - ("John F. Kennedy Center for Performing Arts", "John F. Kennedy Center for Performing Arts"), - ( - "John F. Kennedy Center for the Performing Arts", - "John F. Kennedy Center for the Performing Arts", - ), - ("Legal Services Corporation", "Legal Services Corporation"), - ("Legislative Branch", "Legislative Branch"), - ("Library of Congress", "Library of Congress"), - ("Marine Mammal Commission", "Marine Mammal Commission"), - ( - "Medicaid and CHIP Payment and Access Commission", - "Medicaid and CHIP Payment and Access Commission", - ), - ("Medical Payment Advisory Commission", "Medical Payment Advisory Commission"), - ("Medicare Payment Advisory Commission", "Medicare Payment Advisory Commission"), - ("Merit Systems Protection Board", "Merit Systems Protection Board"), - ("Millennium Challenge Corporation", "Millennium Challenge Corporation"), - ( - "Morris K. Udall and Stewart L. Udall Foundation", - "Morris K. Udall and Stewart L. Udall Foundation", - ), - ("National Aeronautics and Space Administration", "National Aeronautics and Space Administration"), - ("National Archives and Records Administration", "National Archives and Records Administration"), - ("National Capital Planning Commission", "National Capital Planning Commission"), - ("National Council on Disability", "National Council on Disability"), - ("National Credit Union Administration", "National Credit Union Administration"), - ("National Endowment for the Arts", "National Endowment for the Arts"), - ("National Endowment for the Humanities", "National Endowment for the Humanities"), - ( - "National Foundation on the Arts and the Humanities", - "National Foundation on the Arts and the Humanities", - ), - ("National Gallery of Art", "National Gallery of Art"), - ("National Indian Gaming Commission", "National Indian Gaming Commission"), - ("National Labor Relations Board", "National Labor Relations Board"), - ("National Mediation Board", "National Mediation Board"), - ("National Science Foundation", "National Science Foundation"), - ( - "National Security Commission on Artificial Intelligence", - "National Security Commission on Artificial Intelligence", - ), - ("National Transportation Safety Board", "National Transportation Safety Board"), - ( - "Networking Information Technology Research and Development", - "Networking Information Technology Research and Development", - ), - ("Non-Federal Agency", "Non-Federal Agency"), - ("Northern Border Regional Commission", "Northern Border Regional Commission"), - ("Nuclear Regulatory Commission", "Nuclear Regulatory Commission"), - ("Nuclear Safety Oversight Committee", "Nuclear Safety Oversight Committee"), - ("Nuclear Waste Technical Review Board", "Nuclear Waste Technical Review Board"), - ( - "Occupational Safety & Health Review Commission", - "Occupational Safety & Health Review Commission", - ), - ( - "Occupational Safety and Health Review Commission", - "Occupational Safety and Health Review Commission", - ), - ("Office of Compliance", "Office of Compliance"), - ("Office of Congressional Workplace Rights", "Office of Congressional Workplace Rights"), - ("Office of Government Ethics", "Office of Government Ethics"), - ("Office of Navajo and Hopi Indian Relocation", "Office of Navajo and Hopi Indian Relocation"), - ("Office of Personnel Management", "Office of Personnel Management"), - ("Open World Leadership Center", "Open World Leadership Center"), - ("Overseas Private Investment Corporation", "Overseas Private Investment Corporation"), - ("Peace Corps", "Peace Corps"), - ("Pension Benefit Guaranty Corporation", "Pension Benefit Guaranty Corporation"), - ("Postal Regulatory Commission", "Postal Regulatory Commission"), - ("Presidio Trust", "Presidio Trust"), - ("Privacy and Civil Liberties Oversight Board", "Privacy and Civil Liberties Oversight Board"), - ("Public Buildings Reform Board", "Public Buildings Reform Board"), - ( - "Public Defender Service for the District of Columbia", - "Public Defender Service for the District of Columbia", - ), - ("Railroad Retirement Board", "Railroad Retirement Board"), - ("Securities and Exchange Commission", "Securities and Exchange Commission"), - ("Selective Service System", "Selective Service System"), - ("Small Business Administration", "Small Business Administration"), - ("Smithsonian Institution", "Smithsonian Institution"), - ("Social Security Administration", "Social Security Administration"), - ("Social Security Advisory Board", "Social Security Advisory Board"), - ("Southeast Crescent Regional Commission", "Southeast Crescent Regional Commission"), - ("Southwest Border Regional Commission", "Southwest Border Regional Commission"), - ("State Justice Institute", "State Justice Institute"), - ("State, Local, and Tribal Government", "State, Local, and Tribal Government"), - ("Stennis Center for Public Service", "Stennis Center for Public Service"), - ("Surface Transportation Board", "Surface Transportation Board"), - ("Tennessee Valley Authority", "Tennessee Valley Authority"), - ("The Executive Office of the President", "The Executive Office of the President"), - ("The Intelligence Community", "The Intelligence Community"), - ("The Legislative Branch", "The Legislative Branch"), - ("The Supreme Court", "The Supreme Court"), - ( - "The United States World War One Centennial Commission", - "The United States World War One Centennial Commission", - ), - ("U.S. Access Board", "U.S. Access Board"), - ("U.S. Agency for Global Media", "U.S. Agency for Global Media"), - ("U.S. Agency for International Development", "U.S. Agency for International Development"), - ("U.S. Capitol Police", "U.S. Capitol Police"), - ("U.S. Chemical Safety Board", "U.S. Chemical Safety Board"), - ( - "U.S. China Economic and Security Review Commission", - "U.S. China Economic and Security Review Commission", - ), - ( - "U.S. Commission for the Preservation of Americas Heritage Abroad", - "U.S. Commission for the Preservation of Americas Heritage Abroad", - ), - ("U.S. Commission of Fine Arts", "U.S. Commission of Fine Arts"), - ("U.S. Commission on Civil Rights", "U.S. Commission on Civil Rights"), - ( - "U.S. Commission on International Religious Freedom", - "U.S. Commission on International Religious Freedom", - ), - ("U.S. Courts", "U.S. Courts"), - ("U.S. Department of Agriculture", "U.S. Department of Agriculture"), - ("U.S. Interagency Council on Homelessness", "U.S. Interagency Council on Homelessness"), - ("U.S. International Trade Commission", "U.S. International Trade Commission"), - ("U.S. Nuclear Waste Technical Review Board", "U.S. Nuclear Waste Technical Review Board"), - ("U.S. Office of Special Counsel", "U.S. Office of Special Counsel"), - ("U.S. Peace Corps", "U.S. Peace Corps"), - ("U.S. Postal Service", "U.S. Postal Service"), - ("U.S. Semiquincentennial Commission", "U.S. Semiquincentennial Commission"), - ("U.S. Trade and Development Agency", "U.S. Trade and Development Agency"), - ( - "U.S.-China Economic and Security Review Commission", - "U.S.-China Economic and Security Review Commission", - ), - ("Udall Foundation", "Udall Foundation"), - ("United States AbilityOne", "United States AbilityOne"), - ("United States Access Board", "United States Access Board"), - ("United States African Development Foundation", "United States African Development Foundation"), - ("United States Agency for Global Media", "United States Agency for Global Media"), - ("United States Arctic Research Commission", "United States Arctic Research Commission"), - ("United States Global Change Research Program", "United States Global Change Research Program"), - ("United States Holocaust Memorial Museum", "United States Holocaust Memorial Museum"), - ("United States Institute of Peace", "United States Institute of Peace"), - ( - "United States Interagency Council on Homelessness", - "United States Interagency Council on Homelessness", - ), - ( - "United States International Development Finance Corporation", - "United States International Development Finance Corporation", - ), - ("United States International Trade Commission", "United States International Trade Commission"), - ("United States Postal Service", "United States Postal Service"), - ("United States Senate", "United States Senate"), - ("United States Trade and Development Agency", "United States Trade and Development Agency"), - ( - "Utah Reclamation Mitigation and Conservation Commission", - "Utah Reclamation Mitigation and Conservation Commission", - ), - ("Vietnam Education Foundation", "Vietnam Education Foundation"), - ("Western Hemisphere Drug Policy Commission", "Western Hemisphere Drug Policy Commission"), - ( - "Woodrow Wilson International Center for Scholars", - "Woodrow Wilson International Center for Scholars", - ), - ("World War I Centennial Commission", "World War I Centennial Commission"), - ], - null=True, - ), - ), - migrations.AlterField( - model_name="domainrequest", - name="federal_type", - field=models.CharField( - blank=True, - choices=[("executive", "Executive"), ("judicial", "Judicial"), ("legislative", "Legislative")], - max_length=50, - null=True, - ), - ), - migrations.AlterField( - model_name="domainrequest", - name="federally_recognized_tribe", - field=models.BooleanField(null=True), - ), - migrations.AlterField( - model_name="domainrequest", - name="generic_org_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"), - ], - max_length=255, - null=True, - ), - ), - migrations.AlterField( - model_name="domainrequest", - name="is_election_board", - field=models.BooleanField(blank=True, null=True), - ), - migrations.AlterField( - model_name="domainrequest", - name="no_other_contacts_rationale", - field=models.TextField( - blank=True, help_text="Required if creator does not list other employees", null=True - ), - ), - migrations.AlterField( - model_name="domainrequest", - name="notes", - field=models.TextField(blank=True, null=True), - ), - migrations.AlterField( - model_name="domainrequest", - name="organization_name", - field=models.CharField(blank=True, db_index=True, null=True), - ), - migrations.AlterField( - model_name="domainrequest", - name="purpose", - field=models.TextField(blank=True, null=True), - ), - migrations.AlterField( - model_name="domainrequest", - name="requested_domain", - field=models.OneToOneField( - blank=True, - null=True, - on_delete=django.db.models.deletion.PROTECT, - related_name="domain_request", - to="registrar.draftdomain", - ), - ), - migrations.AlterField( - model_name="domainrequest", - name="state_recognized_tribe", - field=models.BooleanField(null=True), - ), - migrations.AlterField( - model_name="domainrequest", - name="state_territory", - field=models.CharField( - blank=True, - choices=[ - ("AL", "Alabama (AL)"), - ("AK", "Alaska (AK)"), - ("AS", "American Samoa (AS)"), - ("AZ", "Arizona (AZ)"), - ("AR", "Arkansas (AR)"), - ("CA", "California (CA)"), - ("CO", "Colorado (CO)"), - ("CT", "Connecticut (CT)"), - ("DE", "Delaware (DE)"), - ("DC", "District of Columbia (DC)"), - ("FL", "Florida (FL)"), - ("GA", "Georgia (GA)"), - ("GU", "Guam (GU)"), - ("HI", "Hawaii (HI)"), - ("ID", "Idaho (ID)"), - ("IL", "Illinois (IL)"), - ("IN", "Indiana (IN)"), - ("IA", "Iowa (IA)"), - ("KS", "Kansas (KS)"), - ("KY", "Kentucky (KY)"), - ("LA", "Louisiana (LA)"), - ("ME", "Maine (ME)"), - ("MD", "Maryland (MD)"), - ("MA", "Massachusetts (MA)"), - ("MI", "Michigan (MI)"), - ("MN", "Minnesota (MN)"), - ("MS", "Mississippi (MS)"), - ("MO", "Missouri (MO)"), - ("MT", "Montana (MT)"), - ("NE", "Nebraska (NE)"), - ("NV", "Nevada (NV)"), - ("NH", "New Hampshire (NH)"), - ("NJ", "New Jersey (NJ)"), - ("NM", "New Mexico (NM)"), - ("NY", "New York (NY)"), - ("NC", "North Carolina (NC)"), - ("ND", "North Dakota (ND)"), - ("MP", "Northern Mariana Islands (MP)"), - ("OH", "Ohio (OH)"), - ("OK", "Oklahoma (OK)"), - ("OR", "Oregon (OR)"), - ("PA", "Pennsylvania (PA)"), - ("PR", "Puerto Rico (PR)"), - ("RI", "Rhode Island (RI)"), - ("SC", "South Carolina (SC)"), - ("SD", "South Dakota (SD)"), - ("TN", "Tennessee (TN)"), - ("TX", "Texas (TX)"), - ("UM", "United States Minor Outlying Islands (UM)"), - ("UT", "Utah (UT)"), - ("VT", "Vermont (VT)"), - ("VI", "Virgin Islands (VI)"), - ("VA", "Virginia (VA)"), - ("WA", "Washington (WA)"), - ("WV", "West Virginia (WV)"), - ("WI", "Wisconsin (WI)"), - ("WY", "Wyoming (WY)"), - ("AA", "Armed Forces Americas (AA)"), - ("AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)"), - ("AP", "Armed Forces Pacific (AP)"), - ], - max_length=2, - null=True, - ), - ), - migrations.AlterField( - model_name="domainrequest", - name="submitter", - field=models.ForeignKey( - blank=True, - help_text='Person listed under "your contact information" in the request form; will receive email updates', - null=True, - on_delete=django.db.models.deletion.PROTECT, - related_name="submitted_domain_requests", - to="registrar.contact", - ), - ), - migrations.AlterField( - model_name="domainrequest", - name="tribe_name", - field=models.CharField(blank=True, null=True), - ), - migrations.AlterField( - model_name="domainrequest", - name="urbanization", - field=models.CharField(blank=True, help_text="Required for Puetro Rico only", null=True), - ), - migrations.AlterField( - model_name="domainrequest", - name="zipcode", - field=models.CharField(blank=True, db_index=True, max_length=10, null=True), - ), - migrations.AlterField( - model_name="host", - name="domain", - field=models.ForeignKey( - help_text="Domain associated with this host", - on_delete=django.db.models.deletion.PROTECT, - related_name="host", - to="registrar.domain", - ), - ), - migrations.AlterField( - model_name="host", - name="name", - field=models.CharField(default=None, max_length=253), - ), - migrations.AlterField( - model_name="hostip", - name="address", - field=models.CharField( - default=None, max_length=46, validators=[django.core.validators.validate_ipv46_address] - ), - ), - migrations.AlterField( - model_name="hostip", - name="host", - field=models.ForeignKey( - help_text="IP associated with this host", - on_delete=django.db.models.deletion.PROTECT, - related_name="ip", - to="registrar.host", - ), - ), - migrations.AlterField( - model_name="user", - name="status", - field=models.CharField( - blank=True, - choices=[("restricted", "restricted")], - default=None, - help_text='Users in "restricted" status cannot make updates in the registrar or start a new request.', - max_length=10, - null=True, - ), - ), - migrations.AlterField( - model_name="verifiedbystaff", - name="email", - field=models.EmailField(db_index=True, max_length=254), - ), - migrations.AlterField( - model_name="verifiedbystaff", - name="notes", - field=models.TextField(), - ), - migrations.AlterField( - model_name="verifiedbystaff", - name="requestor", - field=models.ForeignKey( - blank=True, - help_text="Person who verified this user", - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="verifiedby_user", - to=settings.AUTH_USER_MODEL, - ), - ), - migrations.AlterField( - model_name="website", - name="website", - field=models.CharField( - help_text="An alternative domain or current website listed on a domain request", max_length=255 - ), - ), - ] diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index cab3c1650..7bcd36e8a 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -550,7 +550,7 @@ class DomainRequest(TimeStampedModel): urbanization = models.CharField( null=True, blank=True, - help_text="Required for Puetro Rico only", + help_text="Required for Puerto Rico only", ) about_your_organization = models.TextField( diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 28b7dfe48..9791fdd68 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -122,6 +122,40 @@ class TestDomainAdmin(MockEppLib, WebTest): ] self.test_helper.assert_response_contains_distinct_values(response, expected_values) + @less_console_noise_decorator + def test_helper_text_state(self): + """ + Tests for the correct state helper text on this page + """ + + # We don't need to check for all text content, just a portion of it + expected_unknown_domain_message = "The creator of the associated domain request has not logged in to" + expected_dns_message = "Before this domain can be used, name server addresses need" + expected_hold_message = "While on hold, this domain" + expected_deleted_message = "This domain was permanently removed from the registry." + expected_messages = [ + (self.ready_domain, "This domain has name servers and is ready for use."), + (self.unknown_domain, expected_unknown_domain_message), + (self.dns_domain, expected_dns_message), + (self.hold_domain, expected_hold_message), + (self.deleted_domain, expected_deleted_message), + ] + + p = "adminpass" + self.client.login(username="superuser", password=p) + for domain, message in expected_messages: + with self.subTest(domain_state=domain.state): + response = self.client.get( + "/admin/registrar/domain/{}/change/".format(domain.id), + ) + + # Make sure the page loaded, and that we're on the right page + self.assertEqual(response.status_code, 200) + self.assertContains(response, domain.name) + + # Check that the right help text exists + self.assertContains(response, message) + @patch("registrar.admin.DomainAdmin._get_current_date", return_value=date(2024, 1, 1)) def test_extend_expiration_date_button(self, mock_date_today): """ @@ -743,6 +777,7 @@ class TestDomainRequestAdmin(MockEppLib): ) self.mock_client = MockSESClient() + @less_console_noise_decorator def test_helper_text(self): """ Tests for the correct helper text on this page @@ -2330,6 +2365,7 @@ class TestHostAdmin(TestCase): Host.objects.all().delete() Domain.objects.all().delete() + @less_console_noise_decorator def test_helper_text(self): """ Tests for the correct helper text on this page @@ -2407,6 +2443,7 @@ class TestDomainInformationAdmin(TestCase): Contact.objects.all().delete() User.objects.all().delete() + @less_console_noise_decorator def test_helper_text(self): """ Tests for the correct helper text on this page @@ -2863,6 +2900,11 @@ class TestMyUserAdmin(TestCase): self.superuser = create_superuser() self.test_helper = GenericTestHelper(admin=self.admin) + def tearDown(self): + super().tearDown() + User.objects.all().delete() + + @less_console_noise_decorator def test_helper_text(self): """ Tests for the correct helper text on this page @@ -2908,8 +2950,9 @@ class TestMyUserAdmin(TestCase): def test_get_fieldsets_superuser(self): with less_console_noise(): request = self.client.request().wsgi_request - request.user = create_superuser() + request.user = self.superuser fieldsets = self.admin.get_fieldsets(request) + expected_fieldsets = super(MyUserAdmin, self.admin).get_fieldsets(request) self.assertEqual(fieldsets, expected_fieldsets) @@ -2926,9 +2969,6 @@ class TestMyUserAdmin(TestCase): ) self.assertEqual(fieldsets, expected_fieldsets) - def tearDown(self): - User.objects.all().delete() - class AuditedAdminTest(TestCase): def setUp(self): @@ -3417,6 +3457,7 @@ class TestVerifiedByStaffAdmin(TestCase): VerifiedByStaff.objects.all().delete() User.objects.all().delete() + @less_console_noise_decorator def test_helper_text(self): """ Tests for the correct helper text on this page From f8ea204e77c4118643d3aa177d82e8ebfffb9ab0 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 16 Apr 2024 10:17:08 -0600 Subject: [PATCH 29/85] Readd migration --- ...d_alter_domain_expiration_date_and_more.py | 1177 +++++++++++++++++ 1 file changed, 1177 insertions(+) create mode 100644 src/registrar/migrations/0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py diff --git a/src/registrar/migrations/0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py b/src/registrar/migrations/0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py new file mode 100644 index 000000000..3590d3869 --- /dev/null +++ b/src/registrar/migrations/0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py @@ -0,0 +1,1177 @@ +# Generated by Django 4.2.10 on 2024-04-16 16:17 + +from django.conf import settings +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import django_fsm +import registrar.models.utility.domain_field + + +class Migration(migrations.Migration): + + dependencies = [ + ("registrar", "0084_create_groups_v11"), + ] + + operations = [ + migrations.AlterField( + model_name="domain", + name="deleted", + field=models.DateField( + editable=False, help_text='Will appear blank unless the domain is in "deleted" state', null=True + ), + ), + migrations.AlterField( + model_name="domain", + name="expiration_date", + field=models.DateField(help_text="Date the domain expires in the registry", null=True), + ), + migrations.AlterField( + model_name="domain", + name="first_ready", + field=models.DateField( + editable=False, + help_text='Date when this domain first moved into "ready" state; date will never change', + null=True, + ), + ), + migrations.AlterField( + model_name="domain", + name="name", + field=registrar.models.utility.domain_field.DomainField(default=None, max_length=253, unique=True), + ), + migrations.AlterField( + model_name="domain", + name="state", + field=django_fsm.FSMField( + choices=[ + ("unknown", "Unknown"), + ("dns needed", "Dns needed"), + ("ready", "Ready"), + ("on hold", "On hold"), + ("deleted", "Deleted"), + ], + default="unknown", + help_text=" ", + max_length=21, + protected=True, + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="about_your_organization", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="address_line1", + field=models.CharField(blank=True, null=True, verbose_name="Street address"), + ), + migrations.AlterField( + model_name="domaininformation", + name="address_line2", + field=models.CharField(blank=True, null=True, verbose_name="Street address line 2 (optional)"), + ), + migrations.AlterField( + model_name="domaininformation", + name="anything_else", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="city", + field=models.CharField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="creator", + field=models.ForeignKey( + help_text="Person who submitted the domain request", + on_delete=django.db.models.deletion.PROTECT, + related_name="information_created", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="domain", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="domain_info", + to="registrar.domain", + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="domain_request", + field=models.OneToOneField( + blank=True, + help_text="Request associated with this domain", + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="DomainRequest_info", + to="registrar.domainrequest", + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="federal_agency", + field=models.CharField( + blank=True, + choices=[ + ( + "Administrative Conference of the United States", + "Administrative Conference of the United States", + ), + ("Advisory Council on Historic Preservation", "Advisory Council on Historic Preservation"), + ("American Battle Monuments Commission", "American Battle Monuments Commission"), + ("AMTRAK", "AMTRAK"), + ("Appalachian Regional Commission", "Appalachian Regional Commission"), + ( + "Appraisal Subcommittee of the Federal Financial Institutions Examination Council", + "Appraisal Subcommittee of the Federal Financial Institutions Examination Council", + ), + ("Appraisal Subcommittee", "Appraisal Subcommittee"), + ("Architect of the Capitol", "Architect of the Capitol"), + ("Armed Forces Retirement Home", "Armed Forces Retirement Home"), + ( + "Barry Goldwater Scholarship and Excellence in Education Foundation", + "Barry Goldwater Scholarship and Excellence in Education Foundation", + ), + ( + "Barry Goldwater Scholarship and Excellence in Education Program", + "Barry Goldwater Scholarship and Excellence in Education Program", + ), + ("Central Intelligence Agency", "Central Intelligence Agency"), + ("Chemical Safety Board", "Chemical Safety Board"), + ("Christopher Columbus Fellowship Foundation", "Christopher Columbus Fellowship Foundation"), + ("Civil Rights Cold Case Records Review Board", "Civil Rights Cold Case Records Review Board"), + ( + "Commission for the Preservation of America's Heritage Abroad", + "Commission for the Preservation of America's Heritage Abroad", + ), + ("Commission of Fine Arts", "Commission of Fine Arts"), + ( + "Committee for Purchase From People Who Are Blind or Severely Disabled", + "Committee for Purchase From People Who Are Blind or Severely Disabled", + ), + ("Commodity Futures Trading Commission", "Commodity Futures Trading Commission"), + ("Congressional Budget Office", "Congressional Budget Office"), + ("Consumer Financial Protection Bureau", "Consumer Financial Protection Bureau"), + ("Consumer Product Safety Commission", "Consumer Product Safety Commission"), + ("Corporation for National & Community Service", "Corporation for National & Community Service"), + ( + "Corporation for National and Community Service", + "Corporation for National and Community Service", + ), + ( + "Council of Inspectors General on Integrity and Efficiency", + "Council of Inspectors General on Integrity and Efficiency", + ), + ("Court Services and Offender Supervision", "Court Services and Offender Supervision"), + ("Cyberspace Solarium Commission", "Cyberspace Solarium Commission"), + ( + "DC Court Services and Offender Supervision Agency", + "DC Court Services and Offender Supervision Agency", + ), + ("DC Pre-trial Services", "DC Pre-trial Services"), + ("Defense Nuclear Facilities Safety Board", "Defense Nuclear Facilities Safety Board"), + ("Delta Regional Authority", "Delta Regional Authority"), + ("Denali Commission", "Denali Commission"), + ("Department of Agriculture", "Department of Agriculture"), + ("Department of Commerce", "Department of Commerce"), + ("Department of Defense", "Department of Defense"), + ("Department of Education", "Department of Education"), + ("Department of Energy", "Department of Energy"), + ("Department of Health and Human Services", "Department of Health and Human Services"), + ("Department of Homeland Security", "Department of Homeland Security"), + ("Department of Housing and Urban Development", "Department of Housing and Urban Development"), + ("Department of Justice", "Department of Justice"), + ("Department of Labor", "Department of Labor"), + ("Department of State", "Department of State"), + ("Department of the Interior", "Department of the Interior"), + ("Department of the Treasury", "Department of the Treasury"), + ("Department of Transportation", "Department of Transportation"), + ("Department of Veterans Affairs", "Department of Veterans Affairs"), + ("Director of National Intelligence", "Director of National Intelligence"), + ("Dwight D. Eisenhower Memorial Commission", "Dwight D. Eisenhower Memorial Commission"), + ("Election Assistance Commission", "Election Assistance Commission"), + ("Environmental Protection Agency", "Environmental Protection Agency"), + ("Equal Employment Opportunity Commission", "Equal Employment Opportunity Commission"), + ("Executive Office of the President", "Executive Office of the President"), + ("Export-Import Bank of the United States", "Export-Import Bank of the United States"), + ("Export/Import Bank of the U.S.", "Export/Import Bank of the U.S."), + ("Farm Credit Administration", "Farm Credit Administration"), + ("Farm Credit System Insurance Corporation", "Farm Credit System Insurance Corporation"), + ("Federal Communications Commission", "Federal Communications Commission"), + ("Federal Deposit Insurance Corporation", "Federal Deposit Insurance Corporation"), + ("Federal Election Commission", "Federal Election Commission"), + ("Federal Energy Regulatory Commission", "Federal Energy Regulatory Commission"), + ( + "Federal Financial Institutions Examination Council", + "Federal Financial Institutions Examination Council", + ), + ("Federal Housing Finance Agency", "Federal Housing Finance Agency"), + ("Federal Judiciary", "Federal Judiciary"), + ("Federal Labor Relations Authority", "Federal Labor Relations Authority"), + ("Federal Maritime Commission", "Federal Maritime Commission"), + ("Federal Mediation and Conciliation Service", "Federal Mediation and Conciliation Service"), + ( + "Federal Mine Safety and Health Review Commission", + "Federal Mine Safety and Health Review Commission", + ), + ( + "Federal Permitting Improvement Steering Council", + "Federal Permitting Improvement Steering Council", + ), + ("Federal Reserve Board of Governors", "Federal Reserve Board of Governors"), + ("Federal Reserve System", "Federal Reserve System"), + ("Federal Trade Commission", "Federal Trade Commission"), + ("General Services Administration", "General Services Administration"), + ("gov Administration", "gov Administration"), + ("Government Accountability Office", "Government Accountability Office"), + ("Government Publishing Office", "Government Publishing Office"), + ("Gulf Coast Ecosystem Restoration Council", "Gulf Coast Ecosystem Restoration Council"), + ("Harry S Truman Scholarship Foundation", "Harry S Truman Scholarship Foundation"), + ("Harry S. Truman Scholarship Foundation", "Harry S. Truman Scholarship Foundation"), + ("Institute of Museum and Library Services", "Institute of Museum and Library Services"), + ("Institute of Peace", "Institute of Peace"), + ("Inter-American Foundation", "Inter-American Foundation"), + ( + "International Boundary and Water Commission: United States and Mexico", + "International Boundary and Water Commission: United States and Mexico", + ), + ( + "International Boundary Commission: United States and Canada", + "International Boundary Commission: United States and Canada", + ), + ( + "International Joint Commission: United States and Canada", + "International Joint Commission: United States and Canada", + ), + ("James Madison Memorial Fellowship Foundation", "James Madison Memorial Fellowship Foundation"), + ("Japan-United States Friendship Commission", "Japan-United States Friendship Commission"), + ("Japan-US Friendship Commission", "Japan-US Friendship Commission"), + ("John F. Kennedy Center for Performing Arts", "John F. Kennedy Center for Performing Arts"), + ( + "John F. Kennedy Center for the Performing Arts", + "John F. Kennedy Center for the Performing Arts", + ), + ("Legal Services Corporation", "Legal Services Corporation"), + ("Legislative Branch", "Legislative Branch"), + ("Library of Congress", "Library of Congress"), + ("Marine Mammal Commission", "Marine Mammal Commission"), + ( + "Medicaid and CHIP Payment and Access Commission", + "Medicaid and CHIP Payment and Access Commission", + ), + ("Medical Payment Advisory Commission", "Medical Payment Advisory Commission"), + ("Medicare Payment Advisory Commission", "Medicare Payment Advisory Commission"), + ("Merit Systems Protection Board", "Merit Systems Protection Board"), + ("Millennium Challenge Corporation", "Millennium Challenge Corporation"), + ( + "Morris K. Udall and Stewart L. Udall Foundation", + "Morris K. Udall and Stewart L. Udall Foundation", + ), + ("National Aeronautics and Space Administration", "National Aeronautics and Space Administration"), + ("National Archives and Records Administration", "National Archives and Records Administration"), + ("National Capital Planning Commission", "National Capital Planning Commission"), + ("National Council on Disability", "National Council on Disability"), + ("National Credit Union Administration", "National Credit Union Administration"), + ("National Endowment for the Arts", "National Endowment for the Arts"), + ("National Endowment for the Humanities", "National Endowment for the Humanities"), + ( + "National Foundation on the Arts and the Humanities", + "National Foundation on the Arts and the Humanities", + ), + ("National Gallery of Art", "National Gallery of Art"), + ("National Indian Gaming Commission", "National Indian Gaming Commission"), + ("National Labor Relations Board", "National Labor Relations Board"), + ("National Mediation Board", "National Mediation Board"), + ("National Science Foundation", "National Science Foundation"), + ( + "National Security Commission on Artificial Intelligence", + "National Security Commission on Artificial Intelligence", + ), + ("National Transportation Safety Board", "National Transportation Safety Board"), + ( + "Networking Information Technology Research and Development", + "Networking Information Technology Research and Development", + ), + ("Non-Federal Agency", "Non-Federal Agency"), + ("Northern Border Regional Commission", "Northern Border Regional Commission"), + ("Nuclear Regulatory Commission", "Nuclear Regulatory Commission"), + ("Nuclear Safety Oversight Committee", "Nuclear Safety Oversight Committee"), + ("Nuclear Waste Technical Review Board", "Nuclear Waste Technical Review Board"), + ( + "Occupational Safety & Health Review Commission", + "Occupational Safety & Health Review Commission", + ), + ( + "Occupational Safety and Health Review Commission", + "Occupational Safety and Health Review Commission", + ), + ("Office of Compliance", "Office of Compliance"), + ("Office of Congressional Workplace Rights", "Office of Congressional Workplace Rights"), + ("Office of Government Ethics", "Office of Government Ethics"), + ("Office of Navajo and Hopi Indian Relocation", "Office of Navajo and Hopi Indian Relocation"), + ("Office of Personnel Management", "Office of Personnel Management"), + ("Open World Leadership Center", "Open World Leadership Center"), + ("Overseas Private Investment Corporation", "Overseas Private Investment Corporation"), + ("Peace Corps", "Peace Corps"), + ("Pension Benefit Guaranty Corporation", "Pension Benefit Guaranty Corporation"), + ("Postal Regulatory Commission", "Postal Regulatory Commission"), + ("Presidio Trust", "Presidio Trust"), + ("Privacy and Civil Liberties Oversight Board", "Privacy and Civil Liberties Oversight Board"), + ("Public Buildings Reform Board", "Public Buildings Reform Board"), + ( + "Public Defender Service for the District of Columbia", + "Public Defender Service for the District of Columbia", + ), + ("Railroad Retirement Board", "Railroad Retirement Board"), + ("Securities and Exchange Commission", "Securities and Exchange Commission"), + ("Selective Service System", "Selective Service System"), + ("Small Business Administration", "Small Business Administration"), + ("Smithsonian Institution", "Smithsonian Institution"), + ("Social Security Administration", "Social Security Administration"), + ("Social Security Advisory Board", "Social Security Advisory Board"), + ("Southeast Crescent Regional Commission", "Southeast Crescent Regional Commission"), + ("Southwest Border Regional Commission", "Southwest Border Regional Commission"), + ("State Justice Institute", "State Justice Institute"), + ("State, Local, and Tribal Government", "State, Local, and Tribal Government"), + ("Stennis Center for Public Service", "Stennis Center for Public Service"), + ("Surface Transportation Board", "Surface Transportation Board"), + ("Tennessee Valley Authority", "Tennessee Valley Authority"), + ("The Executive Office of the President", "The Executive Office of the President"), + ("The Intelligence Community", "The Intelligence Community"), + ("The Legislative Branch", "The Legislative Branch"), + ("The Supreme Court", "The Supreme Court"), + ( + "The United States World War One Centennial Commission", + "The United States World War One Centennial Commission", + ), + ("U.S. Access Board", "U.S. Access Board"), + ("U.S. Agency for Global Media", "U.S. Agency for Global Media"), + ("U.S. Agency for International Development", "U.S. Agency for International Development"), + ("U.S. Capitol Police", "U.S. Capitol Police"), + ("U.S. Chemical Safety Board", "U.S. Chemical Safety Board"), + ( + "U.S. China Economic and Security Review Commission", + "U.S. China Economic and Security Review Commission", + ), + ( + "U.S. Commission for the Preservation of Americas Heritage Abroad", + "U.S. Commission for the Preservation of Americas Heritage Abroad", + ), + ("U.S. Commission of Fine Arts", "U.S. Commission of Fine Arts"), + ("U.S. Commission on Civil Rights", "U.S. Commission on Civil Rights"), + ( + "U.S. Commission on International Religious Freedom", + "U.S. Commission on International Religious Freedom", + ), + ("U.S. Courts", "U.S. Courts"), + ("U.S. Department of Agriculture", "U.S. Department of Agriculture"), + ("U.S. Interagency Council on Homelessness", "U.S. Interagency Council on Homelessness"), + ("U.S. International Trade Commission", "U.S. International Trade Commission"), + ("U.S. Nuclear Waste Technical Review Board", "U.S. Nuclear Waste Technical Review Board"), + ("U.S. Office of Special Counsel", "U.S. Office of Special Counsel"), + ("U.S. Peace Corps", "U.S. Peace Corps"), + ("U.S. Postal Service", "U.S. Postal Service"), + ("U.S. Semiquincentennial Commission", "U.S. Semiquincentennial Commission"), + ("U.S. Trade and Development Agency", "U.S. Trade and Development Agency"), + ( + "U.S.-China Economic and Security Review Commission", + "U.S.-China Economic and Security Review Commission", + ), + ("Udall Foundation", "Udall Foundation"), + ("United States AbilityOne", "United States AbilityOne"), + ("United States Access Board", "United States Access Board"), + ("United States African Development Foundation", "United States African Development Foundation"), + ("United States Agency for Global Media", "United States Agency for Global Media"), + ("United States Arctic Research Commission", "United States Arctic Research Commission"), + ("United States Global Change Research Program", "United States Global Change Research Program"), + ("United States Holocaust Memorial Museum", "United States Holocaust Memorial Museum"), + ("United States Institute of Peace", "United States Institute of Peace"), + ( + "United States Interagency Council on Homelessness", + "United States Interagency Council on Homelessness", + ), + ( + "United States International Development Finance Corporation", + "United States International Development Finance Corporation", + ), + ("United States International Trade Commission", "United States International Trade Commission"), + ("United States Postal Service", "United States Postal Service"), + ("United States Senate", "United States Senate"), + ("United States Trade and Development Agency", "United States Trade and Development Agency"), + ( + "Utah Reclamation Mitigation and Conservation Commission", + "Utah Reclamation Mitigation and Conservation Commission", + ), + ("Vietnam Education Foundation", "Vietnam Education Foundation"), + ("Western Hemisphere Drug Policy Commission", "Western Hemisphere Drug Policy Commission"), + ( + "Woodrow Wilson International Center for Scholars", + "Woodrow Wilson International Center for Scholars", + ), + ("World War I Centennial Commission", "World War I Centennial Commission"), + ], + null=True, + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="federal_type", + field=models.CharField( + blank=True, + choices=[("executive", "Executive"), ("judicial", "Judicial"), ("legislative", "Legislative")], + max_length=50, + null=True, + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="federally_recognized_tribe", + field=models.BooleanField(null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="no_other_contacts_rationale", + field=models.TextField( + blank=True, help_text="Required if creator does not list other employees", null=True + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="notes", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="organization_name", + field=models.CharField(blank=True, db_index=True, null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="state_recognized_tribe", + field=models.BooleanField(null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="state_territory", + field=models.CharField( + blank=True, + choices=[ + ("AL", "Alabama (AL)"), + ("AK", "Alaska (AK)"), + ("AS", "American Samoa (AS)"), + ("AZ", "Arizona (AZ)"), + ("AR", "Arkansas (AR)"), + ("CA", "California (CA)"), + ("CO", "Colorado (CO)"), + ("CT", "Connecticut (CT)"), + ("DE", "Delaware (DE)"), + ("DC", "District of Columbia (DC)"), + ("FL", "Florida (FL)"), + ("GA", "Georgia (GA)"), + ("GU", "Guam (GU)"), + ("HI", "Hawaii (HI)"), + ("ID", "Idaho (ID)"), + ("IL", "Illinois (IL)"), + ("IN", "Indiana (IN)"), + ("IA", "Iowa (IA)"), + ("KS", "Kansas (KS)"), + ("KY", "Kentucky (KY)"), + ("LA", "Louisiana (LA)"), + ("ME", "Maine (ME)"), + ("MD", "Maryland (MD)"), + ("MA", "Massachusetts (MA)"), + ("MI", "Michigan (MI)"), + ("MN", "Minnesota (MN)"), + ("MS", "Mississippi (MS)"), + ("MO", "Missouri (MO)"), + ("MT", "Montana (MT)"), + ("NE", "Nebraska (NE)"), + ("NV", "Nevada (NV)"), + ("NH", "New Hampshire (NH)"), + ("NJ", "New Jersey (NJ)"), + ("NM", "New Mexico (NM)"), + ("NY", "New York (NY)"), + ("NC", "North Carolina (NC)"), + ("ND", "North Dakota (ND)"), + ("MP", "Northern Mariana Islands (MP)"), + ("OH", "Ohio (OH)"), + ("OK", "Oklahoma (OK)"), + ("OR", "Oregon (OR)"), + ("PA", "Pennsylvania (PA)"), + ("PR", "Puerto Rico (PR)"), + ("RI", "Rhode Island (RI)"), + ("SC", "South Carolina (SC)"), + ("SD", "South Dakota (SD)"), + ("TN", "Tennessee (TN)"), + ("TX", "Texas (TX)"), + ("UM", "United States Minor Outlying Islands (UM)"), + ("UT", "Utah (UT)"), + ("VT", "Vermont (VT)"), + ("VI", "Virgin Islands (VI)"), + ("VA", "Virginia (VA)"), + ("WA", "Washington (WA)"), + ("WV", "West Virginia (WV)"), + ("WI", "Wisconsin (WI)"), + ("WY", "Wyoming (WY)"), + ("AA", "Armed Forces Americas (AA)"), + ("AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)"), + ("AP", "Armed Forces Pacific (AP)"), + ], + max_length=2, + null=True, + verbose_name="State, territory, or military post", + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="submitter", + field=models.ForeignKey( + blank=True, + help_text='Person listed under "your contact information" in the request form', + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="submitted_domain_requests_information", + to="registrar.contact", + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="tribe_name", + field=models.CharField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="urbanization", + field=models.CharField( + blank=True, + help_text="Required for Puerto Rico only", + null=True, + verbose_name="Urbanization (required for Puerto Rico only)", + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="zipcode", + field=models.CharField(blank=True, db_index=True, max_length=10, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="about_your_organization", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="address_line1", + field=models.CharField(blank=True, null=True, verbose_name="Address line 1"), + ), + migrations.AlterField( + model_name="domainrequest", + name="address_line2", + field=models.CharField(blank=True, null=True, verbose_name="Address line 2"), + ), + migrations.AlterField( + model_name="domainrequest", + name="alternative_domains", + field=models.ManyToManyField( + blank=True, + help_text="Other domain names the creator provided for consideration", + related_name="alternatives+", + to="registrar.website", + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="anything_else", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="approved_domain", + field=models.OneToOneField( + blank=True, + help_text="Domain associated with this request; will be blank until request is approved", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="domain_request", + to="registrar.domain", + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="city", + field=models.CharField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="creator", + field=models.ForeignKey( + help_text="Person who submitted the domain request; will not receive email updates", + on_delete=django.db.models.deletion.PROTECT, + related_name="domain_requests_created", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="federal_agency", + field=models.CharField( + blank=True, + choices=[ + ( + "Administrative Conference of the United States", + "Administrative Conference of the United States", + ), + ("Advisory Council on Historic Preservation", "Advisory Council on Historic Preservation"), + ("American Battle Monuments Commission", "American Battle Monuments Commission"), + ("AMTRAK", "AMTRAK"), + ("Appalachian Regional Commission", "Appalachian Regional Commission"), + ( + "Appraisal Subcommittee of the Federal Financial Institutions Examination Council", + "Appraisal Subcommittee of the Federal Financial Institutions Examination Council", + ), + ("Appraisal Subcommittee", "Appraisal Subcommittee"), + ("Architect of the Capitol", "Architect of the Capitol"), + ("Armed Forces Retirement Home", "Armed Forces Retirement Home"), + ( + "Barry Goldwater Scholarship and Excellence in Education Foundation", + "Barry Goldwater Scholarship and Excellence in Education Foundation", + ), + ( + "Barry Goldwater Scholarship and Excellence in Education Program", + "Barry Goldwater Scholarship and Excellence in Education Program", + ), + ("Central Intelligence Agency", "Central Intelligence Agency"), + ("Chemical Safety Board", "Chemical Safety Board"), + ("Christopher Columbus Fellowship Foundation", "Christopher Columbus Fellowship Foundation"), + ("Civil Rights Cold Case Records Review Board", "Civil Rights Cold Case Records Review Board"), + ( + "Commission for the Preservation of America's Heritage Abroad", + "Commission for the Preservation of America's Heritage Abroad", + ), + ("Commission of Fine Arts", "Commission of Fine Arts"), + ( + "Committee for Purchase From People Who Are Blind or Severely Disabled", + "Committee for Purchase From People Who Are Blind or Severely Disabled", + ), + ("Commodity Futures Trading Commission", "Commodity Futures Trading Commission"), + ("Congressional Budget Office", "Congressional Budget Office"), + ("Consumer Financial Protection Bureau", "Consumer Financial Protection Bureau"), + ("Consumer Product Safety Commission", "Consumer Product Safety Commission"), + ("Corporation for National & Community Service", "Corporation for National & Community Service"), + ( + "Corporation for National and Community Service", + "Corporation for National and Community Service", + ), + ( + "Council of Inspectors General on Integrity and Efficiency", + "Council of Inspectors General on Integrity and Efficiency", + ), + ("Court Services and Offender Supervision", "Court Services and Offender Supervision"), + ("Cyberspace Solarium Commission", "Cyberspace Solarium Commission"), + ( + "DC Court Services and Offender Supervision Agency", + "DC Court Services and Offender Supervision Agency", + ), + ("DC Pre-trial Services", "DC Pre-trial Services"), + ("Defense Nuclear Facilities Safety Board", "Defense Nuclear Facilities Safety Board"), + ("Delta Regional Authority", "Delta Regional Authority"), + ("Denali Commission", "Denali Commission"), + ("Department of Agriculture", "Department of Agriculture"), + ("Department of Commerce", "Department of Commerce"), + ("Department of Defense", "Department of Defense"), + ("Department of Education", "Department of Education"), + ("Department of Energy", "Department of Energy"), + ("Department of Health and Human Services", "Department of Health and Human Services"), + ("Department of Homeland Security", "Department of Homeland Security"), + ("Department of Housing and Urban Development", "Department of Housing and Urban Development"), + ("Department of Justice", "Department of Justice"), + ("Department of Labor", "Department of Labor"), + ("Department of State", "Department of State"), + ("Department of the Interior", "Department of the Interior"), + ("Department of the Treasury", "Department of the Treasury"), + ("Department of Transportation", "Department of Transportation"), + ("Department of Veterans Affairs", "Department of Veterans Affairs"), + ("Director of National Intelligence", "Director of National Intelligence"), + ("Dwight D. Eisenhower Memorial Commission", "Dwight D. Eisenhower Memorial Commission"), + ("Election Assistance Commission", "Election Assistance Commission"), + ("Environmental Protection Agency", "Environmental Protection Agency"), + ("Equal Employment Opportunity Commission", "Equal Employment Opportunity Commission"), + ("Executive Office of the President", "Executive Office of the President"), + ("Export-Import Bank of the United States", "Export-Import Bank of the United States"), + ("Export/Import Bank of the U.S.", "Export/Import Bank of the U.S."), + ("Farm Credit Administration", "Farm Credit Administration"), + ("Farm Credit System Insurance Corporation", "Farm Credit System Insurance Corporation"), + ("Federal Communications Commission", "Federal Communications Commission"), + ("Federal Deposit Insurance Corporation", "Federal Deposit Insurance Corporation"), + ("Federal Election Commission", "Federal Election Commission"), + ("Federal Energy Regulatory Commission", "Federal Energy Regulatory Commission"), + ( + "Federal Financial Institutions Examination Council", + "Federal Financial Institutions Examination Council", + ), + ("Federal Housing Finance Agency", "Federal Housing Finance Agency"), + ("Federal Judiciary", "Federal Judiciary"), + ("Federal Labor Relations Authority", "Federal Labor Relations Authority"), + ("Federal Maritime Commission", "Federal Maritime Commission"), + ("Federal Mediation and Conciliation Service", "Federal Mediation and Conciliation Service"), + ( + "Federal Mine Safety and Health Review Commission", + "Federal Mine Safety and Health Review Commission", + ), + ( + "Federal Permitting Improvement Steering Council", + "Federal Permitting Improvement Steering Council", + ), + ("Federal Reserve Board of Governors", "Federal Reserve Board of Governors"), + ("Federal Reserve System", "Federal Reserve System"), + ("Federal Trade Commission", "Federal Trade Commission"), + ("General Services Administration", "General Services Administration"), + ("gov Administration", "gov Administration"), + ("Government Accountability Office", "Government Accountability Office"), + ("Government Publishing Office", "Government Publishing Office"), + ("Gulf Coast Ecosystem Restoration Council", "Gulf Coast Ecosystem Restoration Council"), + ("Harry S Truman Scholarship Foundation", "Harry S Truman Scholarship Foundation"), + ("Harry S. Truman Scholarship Foundation", "Harry S. Truman Scholarship Foundation"), + ("Institute of Museum and Library Services", "Institute of Museum and Library Services"), + ("Institute of Peace", "Institute of Peace"), + ("Inter-American Foundation", "Inter-American Foundation"), + ( + "International Boundary and Water Commission: United States and Mexico", + "International Boundary and Water Commission: United States and Mexico", + ), + ( + "International Boundary Commission: United States and Canada", + "International Boundary Commission: United States and Canada", + ), + ( + "International Joint Commission: United States and Canada", + "International Joint Commission: United States and Canada", + ), + ("James Madison Memorial Fellowship Foundation", "James Madison Memorial Fellowship Foundation"), + ("Japan-United States Friendship Commission", "Japan-United States Friendship Commission"), + ("Japan-US Friendship Commission", "Japan-US Friendship Commission"), + ("John F. Kennedy Center for Performing Arts", "John F. Kennedy Center for Performing Arts"), + ( + "John F. Kennedy Center for the Performing Arts", + "John F. Kennedy Center for the Performing Arts", + ), + ("Legal Services Corporation", "Legal Services Corporation"), + ("Legislative Branch", "Legislative Branch"), + ("Library of Congress", "Library of Congress"), + ("Marine Mammal Commission", "Marine Mammal Commission"), + ( + "Medicaid and CHIP Payment and Access Commission", + "Medicaid and CHIP Payment and Access Commission", + ), + ("Medical Payment Advisory Commission", "Medical Payment Advisory Commission"), + ("Medicare Payment Advisory Commission", "Medicare Payment Advisory Commission"), + ("Merit Systems Protection Board", "Merit Systems Protection Board"), + ("Millennium Challenge Corporation", "Millennium Challenge Corporation"), + ( + "Morris K. Udall and Stewart L. Udall Foundation", + "Morris K. Udall and Stewart L. Udall Foundation", + ), + ("National Aeronautics and Space Administration", "National Aeronautics and Space Administration"), + ("National Archives and Records Administration", "National Archives and Records Administration"), + ("National Capital Planning Commission", "National Capital Planning Commission"), + ("National Council on Disability", "National Council on Disability"), + ("National Credit Union Administration", "National Credit Union Administration"), + ("National Endowment for the Arts", "National Endowment for the Arts"), + ("National Endowment for the Humanities", "National Endowment for the Humanities"), + ( + "National Foundation on the Arts and the Humanities", + "National Foundation on the Arts and the Humanities", + ), + ("National Gallery of Art", "National Gallery of Art"), + ("National Indian Gaming Commission", "National Indian Gaming Commission"), + ("National Labor Relations Board", "National Labor Relations Board"), + ("National Mediation Board", "National Mediation Board"), + ("National Science Foundation", "National Science Foundation"), + ( + "National Security Commission on Artificial Intelligence", + "National Security Commission on Artificial Intelligence", + ), + ("National Transportation Safety Board", "National Transportation Safety Board"), + ( + "Networking Information Technology Research and Development", + "Networking Information Technology Research and Development", + ), + ("Non-Federal Agency", "Non-Federal Agency"), + ("Northern Border Regional Commission", "Northern Border Regional Commission"), + ("Nuclear Regulatory Commission", "Nuclear Regulatory Commission"), + ("Nuclear Safety Oversight Committee", "Nuclear Safety Oversight Committee"), + ("Nuclear Waste Technical Review Board", "Nuclear Waste Technical Review Board"), + ( + "Occupational Safety & Health Review Commission", + "Occupational Safety & Health Review Commission", + ), + ( + "Occupational Safety and Health Review Commission", + "Occupational Safety and Health Review Commission", + ), + ("Office of Compliance", "Office of Compliance"), + ("Office of Congressional Workplace Rights", "Office of Congressional Workplace Rights"), + ("Office of Government Ethics", "Office of Government Ethics"), + ("Office of Navajo and Hopi Indian Relocation", "Office of Navajo and Hopi Indian Relocation"), + ("Office of Personnel Management", "Office of Personnel Management"), + ("Open World Leadership Center", "Open World Leadership Center"), + ("Overseas Private Investment Corporation", "Overseas Private Investment Corporation"), + ("Peace Corps", "Peace Corps"), + ("Pension Benefit Guaranty Corporation", "Pension Benefit Guaranty Corporation"), + ("Postal Regulatory Commission", "Postal Regulatory Commission"), + ("Presidio Trust", "Presidio Trust"), + ("Privacy and Civil Liberties Oversight Board", "Privacy and Civil Liberties Oversight Board"), + ("Public Buildings Reform Board", "Public Buildings Reform Board"), + ( + "Public Defender Service for the District of Columbia", + "Public Defender Service for the District of Columbia", + ), + ("Railroad Retirement Board", "Railroad Retirement Board"), + ("Securities and Exchange Commission", "Securities and Exchange Commission"), + ("Selective Service System", "Selective Service System"), + ("Small Business Administration", "Small Business Administration"), + ("Smithsonian Institution", "Smithsonian Institution"), + ("Social Security Administration", "Social Security Administration"), + ("Social Security Advisory Board", "Social Security Advisory Board"), + ("Southeast Crescent Regional Commission", "Southeast Crescent Regional Commission"), + ("Southwest Border Regional Commission", "Southwest Border Regional Commission"), + ("State Justice Institute", "State Justice Institute"), + ("State, Local, and Tribal Government", "State, Local, and Tribal Government"), + ("Stennis Center for Public Service", "Stennis Center for Public Service"), + ("Surface Transportation Board", "Surface Transportation Board"), + ("Tennessee Valley Authority", "Tennessee Valley Authority"), + ("The Executive Office of the President", "The Executive Office of the President"), + ("The Intelligence Community", "The Intelligence Community"), + ("The Legislative Branch", "The Legislative Branch"), + ("The Supreme Court", "The Supreme Court"), + ( + "The United States World War One Centennial Commission", + "The United States World War One Centennial Commission", + ), + ("U.S. Access Board", "U.S. Access Board"), + ("U.S. Agency for Global Media", "U.S. Agency for Global Media"), + ("U.S. Agency for International Development", "U.S. Agency for International Development"), + ("U.S. Capitol Police", "U.S. Capitol Police"), + ("U.S. Chemical Safety Board", "U.S. Chemical Safety Board"), + ( + "U.S. China Economic and Security Review Commission", + "U.S. China Economic and Security Review Commission", + ), + ( + "U.S. Commission for the Preservation of Americas Heritage Abroad", + "U.S. Commission for the Preservation of Americas Heritage Abroad", + ), + ("U.S. Commission of Fine Arts", "U.S. Commission of Fine Arts"), + ("U.S. Commission on Civil Rights", "U.S. Commission on Civil Rights"), + ( + "U.S. Commission on International Religious Freedom", + "U.S. Commission on International Religious Freedom", + ), + ("U.S. Courts", "U.S. Courts"), + ("U.S. Department of Agriculture", "U.S. Department of Agriculture"), + ("U.S. Interagency Council on Homelessness", "U.S. Interagency Council on Homelessness"), + ("U.S. International Trade Commission", "U.S. International Trade Commission"), + ("U.S. Nuclear Waste Technical Review Board", "U.S. Nuclear Waste Technical Review Board"), + ("U.S. Office of Special Counsel", "U.S. Office of Special Counsel"), + ("U.S. Peace Corps", "U.S. Peace Corps"), + ("U.S. Postal Service", "U.S. Postal Service"), + ("U.S. Semiquincentennial Commission", "U.S. Semiquincentennial Commission"), + ("U.S. Trade and Development Agency", "U.S. Trade and Development Agency"), + ( + "U.S.-China Economic and Security Review Commission", + "U.S.-China Economic and Security Review Commission", + ), + ("Udall Foundation", "Udall Foundation"), + ("United States AbilityOne", "United States AbilityOne"), + ("United States Access Board", "United States Access Board"), + ("United States African Development Foundation", "United States African Development Foundation"), + ("United States Agency for Global Media", "United States Agency for Global Media"), + ("United States Arctic Research Commission", "United States Arctic Research Commission"), + ("United States Global Change Research Program", "United States Global Change Research Program"), + ("United States Holocaust Memorial Museum", "United States Holocaust Memorial Museum"), + ("United States Institute of Peace", "United States Institute of Peace"), + ( + "United States Interagency Council on Homelessness", + "United States Interagency Council on Homelessness", + ), + ( + "United States International Development Finance Corporation", + "United States International Development Finance Corporation", + ), + ("United States International Trade Commission", "United States International Trade Commission"), + ("United States Postal Service", "United States Postal Service"), + ("United States Senate", "United States Senate"), + ("United States Trade and Development Agency", "United States Trade and Development Agency"), + ( + "Utah Reclamation Mitigation and Conservation Commission", + "Utah Reclamation Mitigation and Conservation Commission", + ), + ("Vietnam Education Foundation", "Vietnam Education Foundation"), + ("Western Hemisphere Drug Policy Commission", "Western Hemisphere Drug Policy Commission"), + ( + "Woodrow Wilson International Center for Scholars", + "Woodrow Wilson International Center for Scholars", + ), + ("World War I Centennial Commission", "World War I Centennial Commission"), + ], + null=True, + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="federal_type", + field=models.CharField( + blank=True, + choices=[("executive", "Executive"), ("judicial", "Judicial"), ("legislative", "Legislative")], + max_length=50, + null=True, + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="federally_recognized_tribe", + field=models.BooleanField(null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="generic_org_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"), + ], + max_length=255, + null=True, + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="is_election_board", + field=models.BooleanField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="no_other_contacts_rationale", + field=models.TextField( + blank=True, help_text="Required if creator does not list other employees", null=True + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="notes", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="organization_name", + field=models.CharField(blank=True, db_index=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="purpose", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="requested_domain", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="domain_request", + to="registrar.draftdomain", + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="state_recognized_tribe", + field=models.BooleanField(null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="state_territory", + field=models.CharField( + blank=True, + choices=[ + ("AL", "Alabama (AL)"), + ("AK", "Alaska (AK)"), + ("AS", "American Samoa (AS)"), + ("AZ", "Arizona (AZ)"), + ("AR", "Arkansas (AR)"), + ("CA", "California (CA)"), + ("CO", "Colorado (CO)"), + ("CT", "Connecticut (CT)"), + ("DE", "Delaware (DE)"), + ("DC", "District of Columbia (DC)"), + ("FL", "Florida (FL)"), + ("GA", "Georgia (GA)"), + ("GU", "Guam (GU)"), + ("HI", "Hawaii (HI)"), + ("ID", "Idaho (ID)"), + ("IL", "Illinois (IL)"), + ("IN", "Indiana (IN)"), + ("IA", "Iowa (IA)"), + ("KS", "Kansas (KS)"), + ("KY", "Kentucky (KY)"), + ("LA", "Louisiana (LA)"), + ("ME", "Maine (ME)"), + ("MD", "Maryland (MD)"), + ("MA", "Massachusetts (MA)"), + ("MI", "Michigan (MI)"), + ("MN", "Minnesota (MN)"), + ("MS", "Mississippi (MS)"), + ("MO", "Missouri (MO)"), + ("MT", "Montana (MT)"), + ("NE", "Nebraska (NE)"), + ("NV", "Nevada (NV)"), + ("NH", "New Hampshire (NH)"), + ("NJ", "New Jersey (NJ)"), + ("NM", "New Mexico (NM)"), + ("NY", "New York (NY)"), + ("NC", "North Carolina (NC)"), + ("ND", "North Dakota (ND)"), + ("MP", "Northern Mariana Islands (MP)"), + ("OH", "Ohio (OH)"), + ("OK", "Oklahoma (OK)"), + ("OR", "Oregon (OR)"), + ("PA", "Pennsylvania (PA)"), + ("PR", "Puerto Rico (PR)"), + ("RI", "Rhode Island (RI)"), + ("SC", "South Carolina (SC)"), + ("SD", "South Dakota (SD)"), + ("TN", "Tennessee (TN)"), + ("TX", "Texas (TX)"), + ("UM", "United States Minor Outlying Islands (UM)"), + ("UT", "Utah (UT)"), + ("VT", "Vermont (VT)"), + ("VI", "Virgin Islands (VI)"), + ("VA", "Virginia (VA)"), + ("WA", "Washington (WA)"), + ("WV", "West Virginia (WV)"), + ("WI", "Wisconsin (WI)"), + ("WY", "Wyoming (WY)"), + ("AA", "Armed Forces Americas (AA)"), + ("AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)"), + ("AP", "Armed Forces Pacific (AP)"), + ], + max_length=2, + null=True, + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="submitter", + field=models.ForeignKey( + blank=True, + help_text='Person listed under "your contact information" in the request form; will receive email updates', + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="submitted_domain_requests", + to="registrar.contact", + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="tribe_name", + field=models.CharField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="urbanization", + field=models.CharField(blank=True, help_text="Required for Puerto Rico only", null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="zipcode", + field=models.CharField(blank=True, db_index=True, max_length=10, null=True), + ), + migrations.AlterField( + model_name="host", + name="domain", + field=models.ForeignKey( + help_text="Domain associated with this host", + on_delete=django.db.models.deletion.PROTECT, + related_name="host", + to="registrar.domain", + ), + ), + migrations.AlterField( + model_name="host", + name="name", + field=models.CharField(default=None, max_length=253), + ), + migrations.AlterField( + model_name="hostip", + name="address", + field=models.CharField( + default=None, max_length=46, validators=[django.core.validators.validate_ipv46_address] + ), + ), + migrations.AlterField( + model_name="hostip", + name="host", + field=models.ForeignKey( + help_text="IP associated with this host", + on_delete=django.db.models.deletion.PROTECT, + related_name="ip", + to="registrar.host", + ), + ), + migrations.AlterField( + model_name="user", + name="status", + field=models.CharField( + blank=True, + choices=[("restricted", "restricted")], + default=None, + help_text='Users in "restricted" status cannot make updates in the registrar or start a new request.', + max_length=10, + null=True, + ), + ), + migrations.AlterField( + model_name="verifiedbystaff", + name="email", + field=models.EmailField(db_index=True, max_length=254), + ), + migrations.AlterField( + model_name="verifiedbystaff", + name="notes", + field=models.TextField(), + ), + migrations.AlterField( + model_name="verifiedbystaff", + name="requestor", + field=models.ForeignKey( + blank=True, + help_text="Person who verified this user", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="verifiedby_user", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="website", + name="website", + field=models.CharField( + help_text="An alternative domain or current website listed on a domain request", max_length=255 + ), + ), + ] From d35fabcbce24dc3046c1fa83e9a3c084512ab1c7 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 16 Apr 2024 11:37:05 -0600 Subject: [PATCH 30/85] Generalize BaseYesNoForm + centralize logic into helper This commit is important for two reasons: 1. It keeps things consistent in our code base 2. It moves the ever growing list of wizard base classes into a consistent location --- src/registrar/forms/domain_request_wizard.py | 238 ++---------------- .../forms/utility/wizard_form_helper.py | 226 +++++++++++++++++ 2 files changed, 249 insertions(+), 215 deletions(-) create mode 100644 src/registrar/forms/utility/wizard_form_helper.py diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py index 3be5c4cc8..dc2fc3a84 100644 --- a/src/registrar/forms/domain_request_wizard.py +++ b/src/registrar/forms/domain_request_wizard.py @@ -1,15 +1,13 @@ from __future__ import annotations # allows forward references in annotations -from itertools import zip_longest import logging -from typing import Callable from api.views import DOMAIN_API_MESSAGES from phonenumber_field.formfields import PhoneNumberField # type: ignore from django import forms from django.core.validators import RegexValidator, MaxLengthValidator from django.utils.safestring import mark_safe -from django.db.models.fields.related import ForeignObjectRel +from registrar.forms.utility.wizard_form_helper import RegistrarForm, RegistrarFormSet, BaseYesNoForm from registrar.models import Contact, DomainRequest, DraftDomain, Domain from registrar.templatetags.url_helpers import public_site_url from registrar.utility.enums import ValidationReturnType @@ -17,157 +15,6 @@ from registrar.utility.enums import ValidationReturnType logger = logging.getLogger(__name__) -class RegistrarForm(forms.Form): - """ - A common set of methods and configuration. - - The registrar's domain request is several pages of "steps". - Each step is an HTML form containing one or more Django "forms". - - Subclass this class to create new forms. - """ - - def __init__(self, *args, **kwargs): - kwargs.setdefault("label_suffix", "") - # save a reference to a domain request object - self.domain_request = kwargs.pop("domain_request", None) - super(RegistrarForm, self).__init__(*args, **kwargs) - - def to_database(self, obj: DomainRequest | Contact): - """ - Adds this form's cleaned data to `obj` and saves `obj`. - - Does nothing if form is not valid. - """ - if not self.is_valid(): - return - for name, value in self.cleaned_data.items(): - setattr(obj, name, value) - obj.save() - - @classmethod - def from_database(cls, obj: DomainRequest | Contact | None): - """Returns a dict of form field values gotten from `obj`.""" - if obj is None: - return {} - return {name: getattr(obj, name) for name in cls.declared_fields.keys()} # type: ignore - - -class RegistrarFormSet(forms.BaseFormSet): - """ - As with RegistrarForm, a common set of methods and configuration. - - Subclass this class to create new formsets. - """ - - def __init__(self, *args, **kwargs): - # save a reference to an domain_request object - self.domain_request = kwargs.pop("domain_request", None) - super(RegistrarFormSet, self).__init__(*args, **kwargs) - # quick workaround to ensure that the HTML `required` - # attribute shows up on required fields for any forms - # in the formset which have data already (stated another - # way: you can leave a form in the formset blank, but - # if you opt to fill it out, you must fill it out _right_) - for index in range(self.initial_form_count()): - self.forms[index].use_required_attribute = True - - def should_delete(self, cleaned): - """Should this entry be deleted from the database?""" - raise NotImplementedError - - def pre_update(self, db_obj, cleaned): - """Code to run before an item in the formset is saved.""" - for key, value in cleaned.items(): - setattr(db_obj, key, value) - - def pre_create(self, db_obj, cleaned): - """Code to run before an item in the formset is created in the database.""" - return cleaned - - def to_database(self, obj: DomainRequest): - """ - Adds this form's cleaned data to `obj` and saves `obj`. - - Does nothing if form is not valid. - - Hint: Subclass should call `self._to_database(...)`. - """ - raise NotImplementedError - - def _to_database( - self, - obj: DomainRequest, - join: str, - should_delete: Callable, - pre_update: Callable, - pre_create: Callable, - ): - """ - Performs the actual work of saving. - - Has hooks such as `should_delete` and `pre_update` by which the - subclass can control behavior. Add more hooks whenever needed. - """ - if not self.is_valid(): - return - obj.save() - - query = getattr(obj, join).order_by("created_at").all() # order matters - - # get the related name for the join defined for the db_obj for this form. - # the related name will be the reference on a related object back to db_obj - related_name = "" - field = obj._meta.get_field(join) - if isinstance(field, ForeignObjectRel) and callable(field.related_query_name): - related_name = field.related_query_name() - elif hasattr(field, "related_query_name") and callable(field.related_query_name): - related_name = field.related_query_name() - - # the use of `zip` pairs the forms in the formset with the - # related objects gotten from the database -- there should always be - # at least as many forms as database entries: extra forms means new - # entries, but fewer forms is _not_ the correct way to delete items - # (likely a client-side error or an attempt at data tampering) - for db_obj, post_data in zip_longest(query, self.forms, fillvalue=None): - cleaned = post_data.cleaned_data if post_data is not None else {} - - # matching database object exists, update it - if db_obj is not None and cleaned: - if should_delete(cleaned): - if hasattr(db_obj, "has_more_than_one_join") and db_obj.has_more_than_one_join(related_name): - # Remove the specific relationship without deleting the object - getattr(db_obj, related_name).remove(self.domain_request) - else: - # If there are no other relationships, delete the object - db_obj.delete() - else: - if hasattr(db_obj, "has_more_than_one_join") and db_obj.has_more_than_one_join(related_name): - # create a new db_obj and disconnect existing one - getattr(db_obj, related_name).remove(self.domain_request) - kwargs = pre_create(db_obj, cleaned) - getattr(obj, join).create(**kwargs) - else: - pre_update(db_obj, cleaned) - db_obj.save() - - # no matching database object, create it - # make sure not to create a database object if cleaned has 'delete' attribute - elif db_obj is None and cleaned and not cleaned.get("DELETE", False): - kwargs = pre_create(db_obj, cleaned) - getattr(obj, join).create(**kwargs) - - @classmethod - def on_fetch(cls, query): - """Code to run when fetching formset's objects from the database.""" - return query.values() - - @classmethod - def from_database(cls, obj: DomainRequest, join: str, on_fetch: Callable): - """Returns a dict of form field values gotten from `obj`.""" - return on_fetch(getattr(obj, join).order_by("created_at")) # order matters - - class OrganizationTypeForm(RegistrarForm): generic_org_type = forms.ChoiceField( # use the long names in the domain request form @@ -588,28 +435,24 @@ class YourContactForm(RegistrarForm): ) -class OtherContactsYesNoForm(RegistrarForm): - def __init__(self, *args, **kwargs): - """Extend the initialization of the form from RegistrarForm __init__""" - super().__init__(*args, **kwargs) - # set the initial value based on attributes of domain request - if self.domain_request and self.domain_request.has_other_contacts(): - initial_value = True - elif self.domain_request and self.domain_request.has_rationale(): - initial_value = False +class OtherContactsYesNoForm(BaseYesNoForm): + """The yes/no field for the OtherContacts form.""" + + form_choices = ((True, "Yes, I can name other employees."), (False, "No. (We’ll ask you to explain why.)")) + field_name = "has_other_contacts" + + @property + def form_is_checked(self): + """ + Determines the initial checked state of the form based on the domain_request's attributes. + """ + if self.domain_request.has_other_contacts(): + return True + elif self.domain_request.has_rationale(): + return False else: # No pre-selection for new domain requests - initial_value = None - - self.fields["has_other_contacts"] = forms.TypedChoiceField( - coerce=lambda x: x.lower() == "true" if x is not None else None, # coerce strings to bool, excepting None - choices=((True, "Yes, I can name other employees."), (False, "No. (We’ll ask you to explain why.)")), - initial=initial_value, - widget=forms.RadioSelect, - error_messages={ - "required": "This question is required.", - }, - ) + return None class OtherContactsForm(RegistrarForm): @@ -864,45 +707,12 @@ class CisaRepresentativeForm(BaseDeletableRegistrarForm): ) -class BaseYesNoForm(RegistrarForm): - """Used for forms with a yes/no form with a hidden input on toggle""" - - form_is_checked = None - typed_choice_field_name = None - - def __init__(self, *args, **kwargs): - """Extend the initialization of the form from RegistrarForm __init__""" - super().__init__(*args, **kwargs) - - # set the initial value based on attributes of domain request - if self.domain_request: - if self.form_is_checked: - initial_value = True - else: - initial_value = False - else: - # No pre-selection for new domain requests - initial_value = None - - self.fields[self.typed_choice_field_name] = forms.TypedChoiceField( - coerce=lambda x: x.lower() == "true" if x is not None else None, - choices=((True, "Yes"), (False, "No")), - initial=initial_value, - widget=forms.RadioSelect, - error_messages={ - "required": "This question is required.", - }, - ) - - class CisaRepresentativeYesNoForm(BaseYesNoForm): """Yes/no toggle for the CISA regions question on additional details""" - # Note that these can be set in __init__ if you need more fine-grained control - form_is_checked = property( - lambda self: self.domain_request.has_cisa_representative() if self.domain_request else False - ) - typed_choice_field_name = "has_cisa_representative" + # Note that these can be set as functions/init if you need more fine-grained control + form_is_checked = property(lambda self: self.domain_request.has_cisa_representative()) + field_name = "has_cisa_representative" class AdditionalDetailsForm(BaseDeletableRegistrarForm): @@ -922,11 +732,9 @@ class AdditionalDetailsForm(BaseDeletableRegistrarForm): class AdditionalDetailsYesNoForm(BaseYesNoForm): """Yes/no toggle for the anything else question on additional details""" - # Note that these can be set in __init__ if you need more fine-grained control - form_is_checked = property( - lambda self: self.domain_request.has_anything_else_text() if self.domain_request else False - ) - typed_choice_field_name = "has_anything_else_text" + # Note that these can be set as functions/init if you need more fine-grained control + form_is_checked = property(lambda self: self.domain_request.has_anything_else_text()) + field_name = "has_anything_else_text" class RequirementsForm(RegistrarForm): diff --git a/src/registrar/forms/utility/wizard_form_helper.py b/src/registrar/forms/utility/wizard_form_helper.py new file mode 100644 index 000000000..2916e0393 --- /dev/null +++ b/src/registrar/forms/utility/wizard_form_helper.py @@ -0,0 +1,226 @@ +"""Containers helpers and base classes for the domain_request_wizard.py file""" + +from itertools import zip_longest +from typing import Callable +from django.db.models.fields.related import ForeignObjectRel +from django import forms + +from registrar.models import DomainRequest, Contact + + +class RegistrarForm(forms.Form): + """ + A common set of methods and configuration. + + The registrar's domain request is several pages of "steps". + Each step is an HTML form containing one or more Django "forms". + + Subclass this class to create new forms. + """ + + def __init__(self, *args, **kwargs): + kwargs.setdefault("label_suffix", "") + # save a reference to a domain request object + self.domain_request = kwargs.pop("domain_request", None) + super(RegistrarForm, self).__init__(*args, **kwargs) + + def to_database(self, obj: DomainRequest | Contact): + """ + Adds this form's cleaned data to `obj` and saves `obj`. + + Does nothing if form is not valid. + """ + if not self.is_valid(): + return + for name, value in self.cleaned_data.items(): + setattr(obj, name, value) + obj.save() + + @classmethod + def from_database(cls, obj: DomainRequest | Contact | None): + """Returns a dict of form field values gotten from `obj`.""" + if obj is None: + return {} + return {name: getattr(obj, name) for name in cls.declared_fields.keys()} # type: ignore + + +class RegistrarFormSet(forms.BaseFormSet): + """ + As with RegistrarForm, a common set of methods and configuration. + + Subclass this class to create new formsets. + """ + + def __init__(self, *args, **kwargs): + # save a reference to an domain_request object + self.domain_request = kwargs.pop("domain_request", None) + super(RegistrarFormSet, self).__init__(*args, **kwargs) + # quick workaround to ensure that the HTML `required` + # attribute shows up on required fields for any forms + # in the formset which have data already (stated another + # way: you can leave a form in the formset blank, but + # if you opt to fill it out, you must fill it out _right_) + for index in range(self.initial_form_count()): + self.forms[index].use_required_attribute = True + + def should_delete(self, cleaned): + """Should this entry be deleted from the database?""" + raise NotImplementedError + + def pre_update(self, db_obj, cleaned): + """Code to run before an item in the formset is saved.""" + for key, value in cleaned.items(): + setattr(db_obj, key, value) + + def pre_create(self, db_obj, cleaned): + """Code to run before an item in the formset is created in the database.""" + return cleaned + + def to_database(self, obj: DomainRequest): + """ + Adds this form's cleaned data to `obj` and saves `obj`. + + Does nothing if form is not valid. + + Hint: Subclass should call `self._to_database(...)`. + """ + raise NotImplementedError + + def _to_database( + self, + obj: DomainRequest, + join: str, + should_delete: Callable, + pre_update: Callable, + pre_create: Callable, + ): + """ + Performs the actual work of saving. + + Has hooks such as `should_delete` and `pre_update` by which the + subclass can control behavior. Add more hooks whenever needed. + """ + if not self.is_valid(): + return + obj.save() + + query = getattr(obj, join).order_by("created_at").all() # order matters + + # get the related name for the join defined for the db_obj for this form. + # the related name will be the reference on a related object back to db_obj + related_name = "" + field = obj._meta.get_field(join) + if isinstance(field, ForeignObjectRel) and callable(field.related_query_name): + related_name = field.related_query_name() + elif hasattr(field, "related_query_name") and callable(field.related_query_name): + related_name = field.related_query_name() + + # the use of `zip` pairs the forms in the formset with the + # related objects gotten from the database -- there should always be + # at least as many forms as database entries: extra forms means new + # entries, but fewer forms is _not_ the correct way to delete items + # (likely a client-side error or an attempt at data tampering) + for db_obj, post_data in zip_longest(query, self.forms, fillvalue=None): + cleaned = post_data.cleaned_data if post_data is not None else {} + + # matching database object exists, update it + if db_obj is not None and cleaned: + if should_delete(cleaned): + if hasattr(db_obj, "has_more_than_one_join") and db_obj.has_more_than_one_join(related_name): + # Remove the specific relationship without deleting the object + getattr(db_obj, related_name).remove(self.domain_request) + else: + # If there are no other relationships, delete the object + db_obj.delete() + else: + if hasattr(db_obj, "has_more_than_one_join") and db_obj.has_more_than_one_join(related_name): + # create a new db_obj and disconnect existing one + getattr(db_obj, related_name).remove(self.domain_request) + kwargs = pre_create(db_obj, cleaned) + getattr(obj, join).create(**kwargs) + else: + pre_update(db_obj, cleaned) + db_obj.save() + + # no matching database object, create it + # make sure not to create a database object if cleaned has 'delete' attribute + elif db_obj is None and cleaned and not cleaned.get("DELETE", False): + kwargs = pre_create(db_obj, cleaned) + getattr(obj, join).create(**kwargs) + + @classmethod + def on_fetch(cls, query): + """Code to run when fetching formset's objects from the database.""" + return query.values() + + @classmethod + def from_database(cls, obj: DomainRequest, join: str, on_fetch: Callable): + """Returns a dict of form field values gotten from `obj`.""" + return on_fetch(getattr(obj, join).order_by("created_at")) # order matters + + +class BaseYesNoForm(RegistrarForm): + """ + Base class used for forms with a yes/no form with a hidden input on toggle. + Use this class when you need something similar to the AdditionalDetailsYesNoForm. + + Attributes: + form_is_checked (bool): Determines the default state (checked or not) of the Yes/No toggle. + field_name (str): Specifies the form field name that the Yes/No toggle controls. + required_error_message (str): Custom error message displayed when the field is required but not provided. + form_choices (tuple): Defines the choice options for the form field, defaulting to Yes/No choices. + + Usage: + Subclass this form to implement specific Yes/No fields in various parts of the application, customizing + `form_is_checked` and `field_name` as necessary for the context. + """ + + form_is_checked: bool + + # What field does the yes/no button hook to? + # For instance, this could be "has_other_contacts" + field_name: str + + required_error_message = "This question is required." + + # Default form choice mapping. Default is suitable for most cases. + form_choices = ((True, "Yes"), (False, "No")) + + def __init__(self, *args, **kwargs): + """Extend the initialization of the form from RegistrarForm __init__""" + super().__init__(*args, **kwargs) + + self.fields[self.field_name] = self.get_typed_choice_field() + + def get_typed_choice_field(self): + """ + Creates a TypedChoiceField for the form with specified initial value and choices. + Returns: + TypedChoiceField: A Django form field specifically configured for selecting between + predefined choices with type coercion and custom error messages. + """ + choice_field = forms.TypedChoiceField( + coerce=lambda x: x.lower() == "true" if x is not None else None, + choices=self.form_choices, + initial=self.get_initial_value(), + widget=forms.RadioSelect, + error_messages={ + "required": self.required_error_message, + }, + ) + + return choice_field + + def get_initial_value(self): + """ + Determines the initial value for TypedChoiceField. + More directly, this controls the "initial" field on forms.TypedChoiceField. + + Returns: + bool | None: The initial value for the form field. If the domain request is set, + this will always return the value of self.form_is_checked. + Otherwise, None will be returned as a new domain request can't start out checked. + """ + # No pre-selection for new domain requests + initial_value = self.form_is_checked if self.domain_request else None + return initial_value From 43160df81b6e6b259f76dd1d19658f1292c69bcf Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 16 Apr 2024 11:43:02 -0600 Subject: [PATCH 31/85] Move BaseDeletableRegistrarForm --- src/registrar/forms/domain_request_wizard.py | 61 ++----------------- .../forms/utility/wizard_form_helper.py | 54 ++++++++++++++++ 2 files changed, 60 insertions(+), 55 deletions(-) diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py index dc2fc3a84..4e2c8425d 100644 --- a/src/registrar/forms/domain_request_wizard.py +++ b/src/registrar/forms/domain_request_wizard.py @@ -7,7 +7,12 @@ from django import forms from django.core.validators import RegexValidator, MaxLengthValidator from django.utils.safestring import mark_safe -from registrar.forms.utility.wizard_form_helper import RegistrarForm, RegistrarFormSet, BaseYesNoForm +from registrar.forms.utility.wizard_form_helper import ( + RegistrarForm, + RegistrarFormSet, + BaseYesNoForm, + BaseDeletableRegistrarForm, +) from registrar.models import Contact, DomainRequest, DraftDomain, Domain from registrar.templatetags.url_helpers import public_site_url from registrar.utility.enums import ValidationReturnType @@ -622,60 +627,6 @@ OtherContactsFormSet = forms.formset_factory( ) -class BaseDeletableRegistrarForm(RegistrarForm): - """Adds special validation and delete functionality. - Used by forms that are tied to a Yes/No form.""" - - def __init__(self, *args, **kwargs): - self.form_data_marked_for_deletion = False - super().__init__(*args, **kwargs) - - def mark_form_for_deletion(self): - """Marks this form for deletion. - This changes behavior of validity checks and to_database - methods.""" - self.form_data_marked_for_deletion = True - - def clean(self): - """ - This method overrides the default behavior for forms. - This cleans the form after field validation has already taken place. - In this override, remove errors associated with the form if form data - is marked for deletion. - """ - - if self.form_data_marked_for_deletion: - # clear any errors raised by the form fields - # (before this clean() method is run, each field - # performs its own clean, which could result in - # errors that we wish to ignore at this point) - # - # NOTE: we cannot just clear() the errors list. - # That causes problems. - for field in self.fields: - if field in self.errors: - del self.errors[field] - - return self.cleaned_data - - def to_database(self, obj): - """ - This method overrides the behavior of RegistrarForm. - If form data is marked for deletion, set relevant fields - to None before saving. - Do nothing if form is not valid. - """ - if not self.is_valid(): - return - if self.form_data_marked_for_deletion: - for field_name, _ in self.fields.items(): - setattr(obj, field_name, None) - else: - for name, value in self.cleaned_data.items(): - setattr(obj, name, value) - obj.save() - - class NoOtherContactsForm(BaseDeletableRegistrarForm): no_other_contacts_rationale = forms.CharField( required=True, diff --git a/src/registrar/forms/utility/wizard_form_helper.py b/src/registrar/forms/utility/wizard_form_helper.py index 2916e0393..2ae50f908 100644 --- a/src/registrar/forms/utility/wizard_form_helper.py +++ b/src/registrar/forms/utility/wizard_form_helper.py @@ -159,6 +159,60 @@ class RegistrarFormSet(forms.BaseFormSet): return on_fetch(getattr(obj, join).order_by("created_at")) # order matters +class BaseDeletableRegistrarForm(RegistrarForm): + """Adds special validation and delete functionality. + Used by forms that are tied to a Yes/No form.""" + + def __init__(self, *args, **kwargs): + self.form_data_marked_for_deletion = False + super().__init__(*args, **kwargs) + + def mark_form_for_deletion(self): + """Marks this form for deletion. + This changes behavior of validity checks and to_database + methods.""" + self.form_data_marked_for_deletion = True + + def clean(self): + """ + This method overrides the default behavior for forms. + This cleans the form after field validation has already taken place. + In this override, remove errors associated with the form if form data + is marked for deletion. + """ + + if self.form_data_marked_for_deletion: + # clear any errors raised by the form fields + # (before this clean() method is run, each field + # performs its own clean, which could result in + # errors that we wish to ignore at this point) + # + # NOTE: we cannot just clear() the errors list. + # That causes problems. + for field in self.fields: + if field in self.errors: + del self.errors[field] + + return self.cleaned_data + + def to_database(self, obj): + """ + This method overrides the behavior of RegistrarForm. + If form data is marked for deletion, set relevant fields + to None before saving. + Do nothing if form is not valid. + """ + if not self.is_valid(): + return + if self.form_data_marked_for_deletion: + for field_name, _ in self.fields.items(): + setattr(obj, field_name, None) + else: + for name, value in self.cleaned_data.items(): + setattr(obj, name, value) + obj.save() + + class BaseYesNoForm(RegistrarForm): """ Base class used for forms with a yes/no form with a hidden input on toggle. From 644ca4638ad75e7f6ef7e38f694d2a6ffda60c3b Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 16 Apr 2024 11:56:58 -0600 Subject: [PATCH 32/85] Overzealous linter --- src/registrar/forms/domain_request_wizard.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py index 4e2c8425d..789d4498a 100644 --- a/src/registrar/forms/domain_request_wizard.py +++ b/src/registrar/forms/domain_request_wizard.py @@ -662,7 +662,7 @@ class CisaRepresentativeYesNoForm(BaseYesNoForm): """Yes/no toggle for the CISA regions question on additional details""" # Note that these can be set as functions/init if you need more fine-grained control - form_is_checked = property(lambda self: self.domain_request.has_cisa_representative()) + form_is_checked = property(lambda self: self.domain_request.has_cisa_representative()) # type: ignore field_name = "has_cisa_representative" @@ -683,8 +683,8 @@ class AdditionalDetailsForm(BaseDeletableRegistrarForm): class AdditionalDetailsYesNoForm(BaseYesNoForm): """Yes/no toggle for the anything else question on additional details""" - # Note that these can be set as functions/init if you need more fine-grained control - form_is_checked = property(lambda self: self.domain_request.has_anything_else_text()) + # Note that these can be set as functions/init if you need more fine-grained control. + form_is_checked = property(lambda self: self.domain_request.has_anything_else_text()) # type: ignore field_name = "has_anything_else_text" From 2122ff8239b19260a3229b15542df440b5edebe0 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 18 Apr 2024 11:00:56 -0600 Subject: [PATCH 33/85] Rename additional details --- src/registrar/models/domain_information.py | 2 +- src/registrar/models/domain_request.py | 2 +- .../templates/includes/summary_additional_details.html | 2 +- src/registrar/views/domain_request.py | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index 07e01bddb..ec2a4ca22 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -213,7 +213,7 @@ class DomainInformation(TimeStampedModel): null=True, blank=True, help_text="Anything else?", - verbose_name="Additional Details", + verbose_name="Additional details", ) cisa_representative_email = models.EmailField( diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index 3fce90aa4..02324ce61 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -644,7 +644,7 @@ class DomainRequest(TimeStampedModel): null=True, blank=True, help_text="Anything else?", - verbose_name="Additional Details", + verbose_name="Additional details", ) cisa_representative_email = models.EmailField( diff --git a/src/registrar/templates/includes/summary_additional_details.html b/src/registrar/templates/includes/summary_additional_details.html index 344f747b2..233734e7d 100644 --- a/src/registrar/templates/includes/summary_additional_details.html +++ b/src/registrar/templates/includes/summary_additional_details.html @@ -16,7 +16,7 @@ and condense down into one subsection) --> margin-top-0 margin-bottom-05 padding-right-1" > - Additional Details + Additional details {% if domainRequest is not none %} diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py index 6bdec0ab9..f93976138 100644 --- a/src/registrar/views/domain_request.py +++ b/src/registrar/views/domain_request.py @@ -91,7 +91,7 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView): Step.PURPOSE: _("Purpose of your domain"), Step.YOUR_CONTACT: _("Your contact information"), Step.OTHER_CONTACTS: _("Other employees from your organization"), - Step.ADDITIONAL_DETAILS: _("Additional Details"), + Step.ADDITIONAL_DETAILS: _("Additional details"), Step.REQUIREMENTS: _("Requirements for operating a .gov domain"), Step.REVIEW: _("Review and submit your domain request"), } @@ -582,7 +582,6 @@ class OtherContacts(DomainRequestWizard): return all_forms_valid -# DONE-NL: rename this to "Additional Details" (note: this is a find-replace job. VS will not refactor properly) class AdditionalDetails(DomainRequestWizard): template_name = "domain_request_additional_details.html" From 29406f05077bb68f27bff792c68c7b579cbc8ceb Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 18 Apr 2024 11:04:01 -0600 Subject: [PATCH 34/85] Delete 0085_domaininformation_cisa_representative_email_and_more.py --- ...tion_cisa_representative_email_and_more.py | 41 ------------------- 1 file changed, 41 deletions(-) delete mode 100644 src/registrar/migrations/0085_domaininformation_cisa_representative_email_and_more.py diff --git a/src/registrar/migrations/0085_domaininformation_cisa_representative_email_and_more.py b/src/registrar/migrations/0085_domaininformation_cisa_representative_email_and_more.py deleted file mode 100644 index 71089c520..000000000 --- a/src/registrar/migrations/0085_domaininformation_cisa_representative_email_and_more.py +++ /dev/null @@ -1,41 +0,0 @@ -# Generated by Django 4.2.10 on 2024-04-15 17:47 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("registrar", "0084_create_groups_v11"), - ] - - operations = [ - migrations.AddField( - model_name="domaininformation", - name="cisa_representative_email", - field=models.EmailField( - blank=True, db_index=True, max_length=320, null=True, verbose_name="CISA region representative" - ), - ), - migrations.AddField( - model_name="domainrequest", - name="cisa_representative_email", - field=models.EmailField( - blank=True, db_index=True, max_length=320, null=True, verbose_name="CISA region representative" - ), - ), - migrations.AlterField( - model_name="domaininformation", - name="anything_else", - field=models.TextField( - blank=True, help_text="Anything else?", null=True, verbose_name="Additional Details" - ), - ), - migrations.AlterField( - model_name="domainrequest", - name="anything_else", - field=models.TextField( - blank=True, help_text="Anything else?", null=True, verbose_name="Additional Details" - ), - ), - ] From 7e7919691e1767c7ad81aeb8541e26cbaf5acd83 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 18 Apr 2024 11:17:54 -0600 Subject: [PATCH 35/85] Add migration --- ...tion_cisa_representative_email_and_more.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/registrar/migrations/0085_domaininformation_cisa_representative_email_and_more.py diff --git a/src/registrar/migrations/0085_domaininformation_cisa_representative_email_and_more.py b/src/registrar/migrations/0085_domaininformation_cisa_representative_email_and_more.py new file mode 100644 index 000000000..be43a1969 --- /dev/null +++ b/src/registrar/migrations/0085_domaininformation_cisa_representative_email_and_more.py @@ -0,0 +1,41 @@ +# Generated by Django 4.2.10 on 2024-04-18 17:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("registrar", "0084_create_groups_v11"), + ] + + operations = [ + migrations.AddField( + model_name="domaininformation", + name="cisa_representative_email", + field=models.EmailField( + blank=True, db_index=True, max_length=320, null=True, verbose_name="CISA region representative" + ), + ), + migrations.AddField( + model_name="domainrequest", + name="cisa_representative_email", + field=models.EmailField( + blank=True, db_index=True, max_length=320, null=True, verbose_name="CISA region representative" + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="anything_else", + field=models.TextField( + blank=True, help_text="Anything else?", null=True, verbose_name="Additional details" + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="anything_else", + field=models.TextField( + blank=True, help_text="Anything else?", null=True, verbose_name="Additional details" + ), + ), + ] From 03be457e372c528d729f58dea19ebfe7b58a8e50 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 18 Apr 2024 12:14:10 -0600 Subject: [PATCH 36/85] Add correct error messages, allow default state to be none --- src/registrar/forms/domain_request_wizard.py | 16 +++++-- .../forms/utility/wizard_form_helper.py | 2 + ...request_has_anything_else_text_and_more.py | 27 +++++++++++ src/registrar/models/domain_request.py | 45 +++++++++++++++---- 4 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 src/registrar/migrations/0086_domainrequest_has_anything_else_text_and_more.py diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py index 789d4498a..95d1f9a4a 100644 --- a/src/registrar/forms/domain_request_wizard.py +++ b/src/registrar/forms/domain_request_wizard.py @@ -647,7 +647,6 @@ class CisaRepresentativeForm(BaseDeletableRegistrarForm): cisa_representative_email = forms.EmailField( required=True, max_length=None, - error_messages={"invalid": ("Enter your email address in the required format, like name@example.com.")}, label="Your representative’s email", validators=[ MaxLengthValidator( @@ -655,14 +654,17 @@ class CisaRepresentativeForm(BaseDeletableRegistrarForm): 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."), + }, ) class CisaRepresentativeYesNoForm(BaseYesNoForm): """Yes/no toggle for the CISA regions question on additional details""" - # Note that these can be set as functions/init if you need more fine-grained control - form_is_checked = property(lambda self: self.domain_request.has_cisa_representative()) # type: ignore + form_is_checked = property(lambda self: self.domain_request.has_cisa_representative) field_name = "has_cisa_representative" @@ -677,6 +679,12 @@ class AdditionalDetailsForm(BaseDeletableRegistrarForm): message="Response must be less than 2000 characters.", ) ], + error_messages={ + "required": ( + "Provide additional details you’d like us to know. " + "If you have nothing to add, select “No.”" + ) + }, ) @@ -684,7 +692,7 @@ class AdditionalDetailsYesNoForm(BaseYesNoForm): """Yes/no toggle for the anything else question on additional details""" # Note that these can be set as functions/init if you need more fine-grained control. - form_is_checked = property(lambda self: self.domain_request.has_anything_else_text()) # type: ignore + form_is_checked = property(lambda self: self.domain_request.has_anything_else_text) # type: ignore field_name = "has_anything_else_text" diff --git a/src/registrar/forms/utility/wizard_form_helper.py b/src/registrar/forms/utility/wizard_form_helper.py index 2ae50f908..f15382b44 100644 --- a/src/registrar/forms/utility/wizard_form_helper.py +++ b/src/registrar/forms/utility/wizard_form_helper.py @@ -263,6 +263,8 @@ class BaseYesNoForm(RegistrarForm): }, ) + print(f"wjat are the error messages? {choice_field.error_messages}") + return choice_field def get_initial_value(self): diff --git a/src/registrar/migrations/0086_domainrequest_has_anything_else_text_and_more.py b/src/registrar/migrations/0086_domainrequest_has_anything_else_text_and_more.py new file mode 100644 index 000000000..5b1d24711 --- /dev/null +++ b/src/registrar/migrations/0086_domainrequest_has_anything_else_text_and_more.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2.10 on 2024-04-18 17:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("registrar", "0085_domaininformation_cisa_representative_email_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="domainrequest", + 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="domainrequest", + name="has_cisa_representative", + field=models.BooleanField( + blank=True, help_text="Determines if the user has a representative email or not", null=True + ), + ), + ] diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index 1e8091c44..0b6e9a2b4 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -647,6 +647,15 @@ class DomainRequest(TimeStampedModel): 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, @@ -655,6 +664,15 @@ class DomainRequest(TimeStampedModel): max_length=320, ) + # 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, @@ -707,8 +725,25 @@ class DomainRequest(TimeStampedModel): def save(self, *args, **kwargs): """Save override for custom properties""" self.sync_organization_type() + self.sync_yes_no_form_fields() + super().save(*args, **kwargs) + 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 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 + ) + + if self.anything_else is not None: + self.has_anything_else_text = ( + self.anything_else != "" and self.anything_else is not None + ) + def __str__(self): try: if self.requested_domain and self.requested_domain.name: @@ -1047,16 +1082,8 @@ class DomainRequest(TimeStampedModel): """Does this domain request have other contacts listed?""" return self.other_contacts.exists() - def has_anything_else_text(self) -> bool: - """Does this domain request have an 'anything else?' entry""" - return self.anything_else != "" and self.anything_else is not None - - def has_cisa_representative(self) -> bool: - """Does this domain request have cisa representative?""" - return self.cisa_representative_email != "" and self.cisa_representative_email is not None - def has_additional_details(self) -> bool: - return self.has_anything_else_text() or self.has_cisa_representative() + return self.has_anything_else_text() or self.has_cisa_representative def is_federal(self) -> Union[bool, None]: """Is this domain request for a federal agency? From 2c0987d0485a891f766cdbdd822c00c795590c8e Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 18 Apr 2024 12:14:34 -0600 Subject: [PATCH 37/85] Update wizard_form_helper.py --- src/registrar/forms/utility/wizard_form_helper.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/registrar/forms/utility/wizard_form_helper.py b/src/registrar/forms/utility/wizard_form_helper.py index f15382b44..2ae50f908 100644 --- a/src/registrar/forms/utility/wizard_form_helper.py +++ b/src/registrar/forms/utility/wizard_form_helper.py @@ -263,8 +263,6 @@ class BaseYesNoForm(RegistrarForm): }, ) - print(f"wjat are the error messages? {choice_field.error_messages}") - return choice_field def get_initial_value(self): From 229e08070350347eb05f14dfcd9c1c374bf885de Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 18 Apr 2024 12:21:47 -0600 Subject: [PATCH 38/85] Consolidate migrations --- ...tion_cisa_representative_email_and_more.py | 16 ++++++++++- ...request_has_anything_else_text_and_more.py | 27 ------------------- 2 files changed, 15 insertions(+), 28 deletions(-) delete mode 100644 src/registrar/migrations/0086_domainrequest_has_anything_else_text_and_more.py diff --git a/src/registrar/migrations/0085_domaininformation_cisa_representative_email_and_more.py b/src/registrar/migrations/0085_domaininformation_cisa_representative_email_and_more.py index be43a1969..bc146aef0 100644 --- a/src/registrar/migrations/0085_domaininformation_cisa_representative_email_and_more.py +++ b/src/registrar/migrations/0085_domaininformation_cisa_representative_email_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.10 on 2024-04-18 17:17 +# Generated by Django 4.2.10 on 2024-04-18 18:21 from django.db import migrations, models @@ -24,6 +24,20 @@ class Migration(migrations.Migration): blank=True, db_index=True, max_length=320, null=True, verbose_name="CISA region representative" ), ), + migrations.AddField( + model_name="domainrequest", + 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="domainrequest", + name="has_cisa_representative", + field=models.BooleanField( + blank=True, help_text="Determines if the user has a representative email or not", null=True + ), + ), migrations.AlterField( model_name="domaininformation", name="anything_else", diff --git a/src/registrar/migrations/0086_domainrequest_has_anything_else_text_and_more.py b/src/registrar/migrations/0086_domainrequest_has_anything_else_text_and_more.py deleted file mode 100644 index 5b1d24711..000000000 --- a/src/registrar/migrations/0086_domainrequest_has_anything_else_text_and_more.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 4.2.10 on 2024-04-18 17:59 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("registrar", "0085_domaininformation_cisa_representative_email_and_more"), - ] - - operations = [ - migrations.AddField( - model_name="domainrequest", - 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="domainrequest", - name="has_cisa_representative", - field=models.BooleanField( - blank=True, help_text="Determines if the user has a representative email or not", null=True - ), - ), - ] From 9931fcc4d3a4c7ce924f03b5f86649926a2ad1a1 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 18 Apr 2024 12:36:11 -0600 Subject: [PATCH 39/85] Fix tests / lint --- src/registrar/forms/domain_request_wizard.py | 5 ++--- src/registrar/models/domain_request.py | 6 ++---- src/registrar/tests/test_admin.py | 2 ++ 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py index 95d1f9a4a..752ad7f35 100644 --- a/src/registrar/forms/domain_request_wizard.py +++ b/src/registrar/forms/domain_request_wizard.py @@ -680,9 +680,8 @@ class AdditionalDetailsForm(BaseDeletableRegistrarForm): ) ], error_messages={ - "required": ( - "Provide additional details you’d like us to know. " - "If you have nothing to add, select “No.”" + "required": ( + "Provide additional details you’d like us to know. " "If you have nothing to add, select “No.”" ) }, ) diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index 0b6e9a2b4..32c0ec0c6 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -738,11 +738,9 @@ class DomainRequest(TimeStampedModel): self.has_cisa_representative = ( self.cisa_representative_email != "" and self.cisa_representative_email is not None ) - + if self.anything_else is not None: - self.has_anything_else_text = ( - self.anything_else != "" and self.anything_else is not None - ) + self.has_anything_else_text = self.anything_else != "" and self.anything_else is not None def __str__(self): try: diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index abc6b76aa..3293fc47c 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1883,7 +1883,9 @@ class TestDomainRequestAdmin(MockEppLib): "purpose", "no_other_contacts_rationale", "anything_else", + "has_anything_else_text", "cisa_representative_email", + "has_cisa_representative", "is_policy_acknowledged", "submission_date", "notes", From 7b19e29cf6800edb5c55187d8243dcb91bbb2c3d Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 19 Apr 2024 12:58:42 -0600 Subject: [PATCH 40/85] Some unit tests --- src/registrar/models/domain_information.py | 1 - src/registrar/models/domain_request.py | 3 +- src/registrar/tests/common.py | 2 + src/registrar/tests/test_views_request.py | 48 +++++++++++++++++++++- 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index c61f1e2a2..7d71915ba 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -219,7 +219,6 @@ class DomainInformation(TimeStampedModel): cisa_representative_email = models.EmailField( null=True, blank=True, - db_index=True, verbose_name="CISA region representative", max_length=320, ) diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index 32c0ec0c6..6da51d485 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -659,7 +659,6 @@ class DomainRequest(TimeStampedModel): cisa_representative_email = models.EmailField( null=True, blank=True, - db_index=True, verbose_name="CISA region representative", max_length=320, ) @@ -739,7 +738,7 @@ class DomainRequest(TimeStampedModel): self.cisa_representative_email != "" and self.cisa_representative_email is not None ) - if self.anything_else is not None: + 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 __str__(self): diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 07dc08f8a..97e620813 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -857,6 +857,8 @@ def completed_domain_request( creator=user, status=status, investigator=investigator, + has_cisa_representative=False, + has_anything_else_text=False, ) if has_about_your_organization: domain_request_kwargs["about_your_organization"] = "e-Government" diff --git a/src/registrar/tests/test_views_request.py b/src/registrar/tests/test_views_request.py index a84d6c374..3d9dec37c 100644 --- a/src/registrar/tests/test_views_request.py +++ b/src/registrar/tests/test_views_request.py @@ -724,14 +724,26 @@ class DomainRequestTests(TestWithUser, WebTest): self.assertContains(contact_page, self.TITLES[Step.ABOUT_YOUR_ORGANIZATION]) - def test_yes_no_form_inits_blank_for_new_domain_request(self): + def test_yes_no_contact_form_inits_blank_for_new_domain_request(self): """On the Other Contacts page, the yes/no form gets initialized with nothing selected for new domain requests""" other_contacts_page = self.app.get(reverse("domain-request:other_contacts")) other_contacts_form = other_contacts_page.forms[0] self.assertEquals(other_contacts_form["other_contacts-has_other_contacts"].value, None) - def test_yes_no_form_inits_yes_for_domain_request_with_other_contacts(self): + def test_yes_no_additional_form_inits_blank_for_new_domain_request(self): + """On the Additional Details page, the yes/no form gets initialized with nothing selected for + new domain requests""" + additional_details_page = self.app.get(reverse("domain-request:additional_details")) + additional_form = additional_details_page.forms[0] + + # Check the cisa representative yes/no field + self.assertEquals(additional_form["additional_details-has_cisa_representative"].value, None) + + # Check the anything else yes/no field + self.assertEquals(additional_form["additional_details-has_anything_else_text"].value, None) + + def test_yes_no_contact_form_inits_yes_for_domain_request_with_other_contacts(self): """On the Other Contacts page, the yes/no form gets initialized with YES selected if the domain request has other contacts""" # Domain Request has other contacts by default @@ -751,6 +763,38 @@ class DomainRequestTests(TestWithUser, WebTest): other_contacts_form = other_contacts_page.forms[0] self.assertEquals(other_contacts_form["other_contacts-has_other_contacts"].value, "True") + def test_yes_no_additional_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 + anything_else""" + + domain_request = completed_domain_request(user=self.user, has_anything_else=True) + domain_request.cisa_representative_email="test@igorville.gov" + domain_request.anything_else="1234" + domain_request.save() + + # prime the form by visiting /edit + self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk})) + # django-webtest does not handle cookie-based sessions well because it keeps + # resetting the session key on each new request, thus destroying the concept + # of a "session". We are going to do it manually, saving the session ID here + # and then setting the cookie on each request. + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + additional_details_page = self.app.get(reverse("domain-request:additional_details")) + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + additional_details_form = additional_details_page.forms[0] + + # Check the cisa representative yes/no field + yes_no_cisa = additional_details_form["additional_details-has_cisa_representative"].value + self.assertEquals(yes_no_cisa, True) + + # Check the anything else yes/no field + yes_no_anything_else = additional_details_form["additional_details-has_anything_else_text"].value + self.assertEquals(yes_no_anything_else, True) + def test_yes_no_form_inits_no_for_domain_request_with_no_other_contacts_rationale(self): """On the Other Contacts page, the yes/no form gets initialized with NO selected if the domain request has no other contacts""" From af99b30c194f61e2da076ac6a9828490ee604425 Mon Sep 17 00:00:00 2001 From: Alysia Broddrick Date: Sat, 20 Apr 2024 19:37:15 -0700 Subject: [PATCH 41/85] updated docs --- src/Pipfile | 4 +- src/Pipfile.lock | 658 ++++++++++++++++++++++++------------------- src/requirements.txt | 30 +- 3 files changed, 385 insertions(+), 307 deletions(-) diff --git a/src/Pipfile b/src/Pipfile index 9366423f1..1565af79b 100644 --- a/src/Pipfile +++ b/src/Pipfile @@ -9,7 +9,7 @@ cfenv = "*" django-cors-headers = "*" pycryptodomex = "*" django-allow-cidr = "*" -django-auditlog = "*" +django-auditlog = "2.3.0" django-csp = "*" environs = {extras=["django"]} Faker = "*" @@ -21,7 +21,7 @@ whitenoise = "*" django-widget-tweaks = "*" cachetools = "*" requests = "*" -django-fsm = "*" +django-fsm = "2.8.1" django-phonenumber-field = {extras = ["phonenumberslite"], version = "*"} boto3 = "*" typing-extensions ='*' diff --git a/src/Pipfile.lock b/src/Pipfile.lock index a1c27e1db..5940f455e 100644 --- a/src/Pipfile.lock +++ b/src/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "52143c73ccc59cd3dd6a1294a9352dbae009ebfc6e3ca5d018b8484275e2b6f8" + "sha256": "ce10883aef7e1ce10421d99b3ac35ebf419857a3fe468f0e2d93785f4323eaa8" }, "pipfile-spec": 6, "requires": {}, @@ -32,20 +32,20 @@ }, "boto3": { "hashes": [ - "sha256:7ce8c9a50af2f8a159a0dd86b40011d8dfdaba35005a118e51cd3ac72dc630f1", - "sha256:d786e7fbe3c4152866199786468a625dc77b9f27294cd7ad4f63cd2e0c927287" + "sha256:168894499578a9d69d6f7deb5811952bf4171c51b95749a9aef32cf67bc71f87", + "sha256:1bd4cef11b7c5f293cede50f3d33ca89fe3413c51f1864f40163c56a732dd6b3" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.34.71" + "version": "==1.34.88" }, "botocore": { "hashes": [ - "sha256:3bc9e23aee73fe6f097823d61f79a8877790436038101a83fa96c7593e8109f8", - "sha256:c58f9ed71af2ea53d24146187130541222d7de8c27eb87d23f15457e7b83d88b" + "sha256:36f2e9e8dfa856e55dbbe703aea601f134db3fddc3615f1020a755b27fd26a5e", + "sha256:e87a660599ed3e14b2a770f4efc3df2f2f6d04f3c7bfd64ddbae186667864a7b" ], "markers": "python_version >= '3.8'", - "version": "==1.34.71" + "version": "==1.34.88" }, "cachetools": { "hashes": [ @@ -392,12 +392,12 @@ }, "faker": { "hashes": [ - "sha256:998c29ee7d64429bd59204abffa9ba11f784fb26c7b9df4def78d1a70feb36a7", - "sha256:a5ddccbe97ab691fad6bd8036c31f5697cfaa550e62e000078d1935fa8a7ec2e" + "sha256:34b947581c2bced340c39b35f89dbfac4f356932cfff8fe893bde854903f0e6e", + "sha256:adb98e771073a06bdc5d2d6710d8af07ac5da64c8dc2ae3b17bb32319e66fd82" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==24.4.0" + "version": "==24.11.0" }, "fred-epplib": { "git": "https://github.com/cisagov/epplib.git", @@ -533,20 +533,20 @@ }, "gunicorn": { "hashes": [ - "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0", - "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033" + "sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9", + "sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63" ], "index": "pypi", - "markers": "python_version >= '3.5'", - "version": "==21.2.0" + "markers": "python_version >= '3.7'", + "version": "==22.0.0" }, "idna": { "hashes": [ - "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca", - "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f" + "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", + "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0" ], "markers": "python_version >= '3.5'", - "version": "==3.6" + "version": "==3.7" }, "jmespath": { "hashes": [ @@ -558,95 +558,172 @@ }, "lxml": { "hashes": [ - "sha256:13521a321a25c641b9ea127ef478b580b5ec82aa2e9fc076c86169d161798b01", - "sha256:14deca1460b4b0f6b01f1ddc9557704e8b365f55c63070463f6c18619ebf964f", - "sha256:16018f7099245157564d7148165132c70adb272fb5a17c048ba70d9cc542a1a1", - "sha256:16dd953fb719f0ffc5bc067428fc9e88f599e15723a85618c45847c96f11f431", - "sha256:19a1bc898ae9f06bccb7c3e1dfd73897ecbbd2c96afe9095a6026016e5ca97b8", - "sha256:1ad17c20e3666c035db502c78b86e58ff6b5991906e55bdbef94977700c72623", - "sha256:22b7ee4c35f374e2c20337a95502057964d7e35b996b1c667b5c65c567d2252a", - "sha256:24ef5a4631c0b6cceaf2dbca21687e29725b7c4e171f33a8f8ce23c12558ded1", - "sha256:25663d6e99659544ee8fe1b89b1a8c0aaa5e34b103fab124b17fa958c4a324a6", - "sha256:262bc5f512a66b527d026518507e78c2f9c2bd9eb5c8aeeb9f0eb43fcb69dc67", - "sha256:280f3edf15c2a967d923bcfb1f8f15337ad36f93525828b40a0f9d6c2ad24890", - "sha256:2ad3a8ce9e8a767131061a22cd28fdffa3cd2dc193f399ff7b81777f3520e372", - "sha256:2befa20a13f1a75c751f47e00929fb3433d67eb9923c2c0b364de449121f447c", - "sha256:2f37c6d7106a9d6f0708d4e164b707037b7380fcd0b04c5bd9cae1fb46a856fb", - "sha256:304128394c9c22b6569eba2a6d98392b56fbdfbad58f83ea702530be80d0f9df", - "sha256:342e95bddec3a698ac24378d61996b3ee5ba9acfeb253986002ac53c9a5f6f84", - "sha256:3aeca824b38ca78d9ee2ab82bd9883083d0492d9d17df065ba3b94e88e4d7ee6", - "sha256:3d184e0d5c918cff04cdde9dbdf9600e960161d773666958c9d7b565ccc60c45", - "sha256:3e3898ae2b58eeafedfe99e542a17859017d72d7f6a63de0f04f99c2cb125936", - "sha256:3eea6ed6e6c918e468e693c41ef07f3c3acc310b70ddd9cc72d9ef84bc9564ca", - "sha256:3f14a4fb1c1c402a22e6a341a24c1341b4a3def81b41cd354386dcb795f83897", - "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a", - "sha256:4946e7f59b7b6a9e27bef34422f645e9a368cb2be11bf1ef3cafc39a1f6ba68d", - "sha256:49a9b4af45e8b925e1cd6f3b15bbba2c81e7dba6dce170c677c9cda547411e14", - "sha256:4f8b0c78e7aac24979ef09b7f50da871c2de2def043d468c4b41f512d831e912", - "sha256:52427a7eadc98f9e62cb1368a5079ae826f94f05755d2d567d93ee1bc3ceb354", - "sha256:5e53d7e6a98b64fe54775d23a7c669763451340c3d44ad5e3a3b48a1efbdc96f", - "sha256:5fcfbebdb0c5d8d18b84118842f31965d59ee3e66996ac842e21f957eb76138c", - "sha256:601f4a75797d7a770daed8b42b97cd1bb1ba18bd51a9382077a6a247a12aa38d", - "sha256:61c5a7edbd7c695e54fca029ceb351fc45cd8860119a0f83e48be44e1c464862", - "sha256:6a2a2c724d97c1eb8cf966b16ca2915566a4904b9aad2ed9a09c748ffe14f969", - "sha256:6d48fc57e7c1e3df57be5ae8614bab6d4e7b60f65c5457915c26892c41afc59e", - "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8", - "sha256:704f5572ff473a5f897745abebc6df40f22d4133c1e0a1f124e4f2bd3330ff7e", - "sha256:725e171e0b99a66ec8605ac77fa12239dbe061482ac854d25720e2294652eeaa", - "sha256:7cfced4a069003d8913408e10ca8ed092c49a7f6cefee9bb74b6b3e860683b45", - "sha256:7ec465e6549ed97e9f1e5ed51c657c9ede767bc1c11552f7f4d022c4df4a977a", - "sha256:82bddf0e72cb2af3cbba7cec1d2fd11fda0de6be8f4492223d4a268713ef2147", - "sha256:82cd34f1081ae4ea2ede3d52f71b7be313756e99b4b5f829f89b12da552d3aa3", - "sha256:843b9c835580d52828d8f69ea4302537337a21e6b4f1ec711a52241ba4a824f3", - "sha256:877efb968c3d7eb2dad540b6cabf2f1d3c0fbf4b2d309a3c141f79c7e0061324", - "sha256:8b9f19df998761babaa7f09e6bc169294eefafd6149aaa272081cbddc7ba4ca3", - "sha256:8cf5877f7ed384dabfdcc37922c3191bf27e55b498fecece9fd5c2c7aaa34c33", - "sha256:8d2900b7f5318bc7ad8631d3d40190b95ef2aa8cc59473b73b294e4a55e9f30f", - "sha256:8d7b4beebb178e9183138f552238f7e6613162a42164233e2bda00cb3afac58f", - "sha256:8f52fe6859b9db71ee609b0c0a70fea5f1e71c3462ecf144ca800d3f434f0764", - "sha256:98f3f020a2b736566c707c8e034945c02aa94e124c24f77ca097c446f81b01f1", - "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114", - "sha256:9b99f564659cfa704a2dd82d0684207b1aadf7d02d33e54845f9fc78e06b7581", - "sha256:9bcf86dfc8ff3e992fed847c077bd875d9e0ba2fa25d859c3a0f0f76f07f0c8d", - "sha256:9bd0ae7cc2b85320abd5e0abad5ccee5564ed5f0cc90245d2f9a8ef330a8deae", - "sha256:9d3c0f8567ffe7502d969c2c1b809892dc793b5d0665f602aad19895f8d508da", - "sha256:9e5ac3437746189a9b4121db2a7b86056ac8786b12e88838696899328fc44bb2", - "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e", - "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda", - "sha256:a96f02ba1bcd330807fc060ed91d1f7a20853da6dd449e5da4b09bfcc08fdcf5", - "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa", - "sha256:ae15347a88cf8af0949a9872b57a320d2605ae069bcdf047677318bc0bba45b1", - "sha256:af8920ce4a55ff41167ddbc20077f5698c2e710ad3353d32a07d3264f3a2021e", - "sha256:afd825e30f8d1f521713a5669b63657bcfe5980a916c95855060048b88e1adb7", - "sha256:b21b4031b53d25b0858d4e124f2f9131ffc1530431c6d1321805c90da78388d1", - "sha256:b4b68c961b5cc402cbd99cca5eb2547e46ce77260eb705f4d117fd9c3f932b95", - "sha256:b66aa6357b265670bb574f050ffceefb98549c721cf28351b748be1ef9577d93", - "sha256:b9e240ae0ba96477682aa87899d94ddec1cc7926f9df29b1dd57b39e797d5ab5", - "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b", - "sha256:bf8443781533b8d37b295016a4b53c1494fa9a03573c09ca5104550c138d5c05", - "sha256:c26aab6ea9c54d3bed716b8851c8bfc40cb249b8e9880e250d1eddde9f709bf5", - "sha256:c3cd1fc1dc7c376c54440aeaaa0dcc803d2126732ff5c6b68ccd619f2e64be4f", - "sha256:c7257171bb8d4432fe9d6fdde4d55fdbe663a63636a17f7f9aaba9bcb3153ad7", - "sha256:d42e3a3fc18acc88b838efded0e6ec3edf3e328a58c68fbd36a7263a874906c8", - "sha256:d74fcaf87132ffc0447b3c685a9f862ffb5b43e70ea6beec2fb8057d5d2a1fea", - "sha256:d8c1d679df4361408b628f42b26a5d62bd3e9ba7f0c0e7969f925021554755aa", - "sha256:e856c1c7255c739434489ec9c8aa9cdf5179785d10ff20add308b5d673bed5cd", - "sha256:eac68f96539b32fce2c9b47eb7c25bb2582bdaf1bbb360d25f564ee9e04c542b", - "sha256:ed7326563024b6e91fef6b6c7a1a2ff0a71b97793ac33dbbcf38f6005e51ff6e", - "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4", - "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204", - "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a" + "sha256:04ab5415bf6c86e0518d57240a96c4d1fcfc3cb370bb2ac2a732b67f579e5a04", + "sha256:057cdc6b86ab732cf361f8b4d8af87cf195a1f6dc5b0ff3de2dced242c2015e0", + "sha256:058a1308914f20784c9f4674036527e7c04f7be6fb60f5d61353545aa7fcb739", + "sha256:08802f0c56ed150cc6885ae0788a321b73505d2263ee56dad84d200cab11c07a", + "sha256:0a15438253b34e6362b2dc41475e7f80de76320f335e70c5528b7148cac253a1", + "sha256:0c3f67e2aeda739d1cc0b1102c9a9129f7dc83901226cc24dd72ba275ced4218", + "sha256:0e7259016bc4345a31af861fdce942b77c99049d6c2107ca07dc2bba2435c1d9", + "sha256:0ed777c1e8c99b63037b91f9d73a6aad20fd035d77ac84afcc205225f8f41188", + "sha256:0f5d65c39f16717a47c36c756af0fb36144069c4718824b7533f803ecdf91138", + "sha256:0f8c09ed18ecb4ebf23e02b8e7a22a05d6411911e6fabef3a36e4f371f4f2585", + "sha256:11a04306fcba10cd9637e669fd73aa274c1c09ca64af79c041aa820ea992b637", + "sha256:1ae67b4e737cddc96c99461d2f75d218bdf7a0c3d3ad5604d1f5e7464a2f9ffe", + "sha256:1c5bb205e9212d0ebddf946bc07e73fa245c864a5f90f341d11ce7b0b854475d", + "sha256:1f7785f4f789fdb522729ae465adcaa099e2a3441519df750ebdccc481d961a1", + "sha256:200e63525948e325d6a13a76ba2911f927ad399ef64f57898cf7c74e69b71095", + "sha256:21c2e6b09565ba5b45ae161b438e033a86ad1736b8c838c766146eff8ceffff9", + "sha256:2213afee476546a7f37c7a9b4ad4d74b1e112a6fafffc9185d6d21f043128c81", + "sha256:27aa20d45c2e0b8cd05da6d4759649170e8dfc4f4e5ef33a34d06f2d79075d57", + "sha256:2a66bf12fbd4666dd023b6f51223aed3d9f3b40fef06ce404cb75bafd3d89536", + "sha256:2c9d147f754b1b0e723e6afb7ba1566ecb162fe4ea657f53d2139bbf894d050a", + "sha256:2ddfe41ddc81f29a4c44c8ce239eda5ade4e7fc305fb7311759dd6229a080052", + "sha256:31e9a882013c2f6bd2f2c974241bf4ba68c85eba943648ce88936d23209a2e01", + "sha256:3249cc2989d9090eeac5467e50e9ec2d40704fea9ab72f36b034ea34ee65ca98", + "sha256:3545039fa4779be2df51d6395e91a810f57122290864918b172d5dc7ca5bb433", + "sha256:394ed3924d7a01b5bd9a0d9d946136e1c2f7b3dc337196d99e61740ed4bc6fe1", + "sha256:3a6b45da02336895da82b9d472cd274b22dc27a5cea1d4b793874eead23dd14f", + "sha256:3a74c4f27167cb95c1d4af1c0b59e88b7f3e0182138db2501c353555f7ec57f4", + "sha256:3d0c3dd24bb4605439bf91068598d00c6370684f8de4a67c2992683f6c309d6b", + "sha256:3dbe858ee582cbb2c6294dc85f55b5f19c918c2597855e950f34b660f1a5ede6", + "sha256:3dc773b2861b37b41a6136e0b72a1a44689a9c4c101e0cddb6b854016acc0aa8", + "sha256:3e183c6e3298a2ed5af9d7a356ea823bccaab4ec2349dc9ed83999fd289d14d5", + "sha256:3f7765e69bbce0906a7c74d5fe46d2c7a7596147318dbc08e4a2431f3060e306", + "sha256:417d14450f06d51f363e41cace6488519038f940676ce9664b34ebf5653433a5", + "sha256:44f6c7caff88d988db017b9b0e4ab04934f11e3e72d478031efc7edcac6c622f", + "sha256:491755202eb21a5e350dae00c6d9a17247769c64dcf62d8c788b5c135e179dc4", + "sha256:4951e4f7a5680a2db62f7f4ab2f84617674d36d2d76a729b9a8be4b59b3659be", + "sha256:52421b41ac99e9d91934e4d0d0fe7da9f02bfa7536bb4431b4c05c906c8c6919", + "sha256:530e7c04f72002d2f334d5257c8a51bf409db0316feee7c87e4385043be136af", + "sha256:533658f8fbf056b70e434dff7e7aa611bcacb33e01f75de7f821810e48d1bb66", + "sha256:5670fb70a828663cc37552a2a85bf2ac38475572b0e9b91283dc09efb52c41d1", + "sha256:56c22432809085b3f3ae04e6e7bdd36883d7258fcd90e53ba7b2e463efc7a6af", + "sha256:58278b29cb89f3e43ff3e0c756abbd1518f3ee6adad9e35b51fb101c1c1daaec", + "sha256:588008b8497667f1ddca7c99f2f85ce8511f8f7871b4a06ceede68ab62dff64b", + "sha256:59565f10607c244bc4c05c0c5fa0c190c990996e0c719d05deec7030c2aa8289", + "sha256:59689a75ba8d7ffca577aefd017d08d659d86ad4585ccc73e43edbfc7476781a", + "sha256:5aea8212fb823e006b995c4dda533edcf98a893d941f173f6c9506126188860d", + "sha256:5c670c0406bdc845b474b680b9a5456c561c65cf366f8db5a60154088c92d102", + "sha256:5ca1e8188b26a819387b29c3895c47a5e618708fe6f787f3b1a471de2c4a94d9", + "sha256:5d077bc40a1fe984e1a9931e801e42959a1e6598edc8a3223b061d30fbd26bbc", + "sha256:5d5792e9b3fb8d16a19f46aa8208987cfeafe082363ee2745ea8b643d9cc5b45", + "sha256:5dd1537e7cc06efd81371f5d1a992bd5ab156b2b4f88834ca852de4a8ea523fa", + "sha256:5ea7b6766ac2dfe4bcac8b8595107665a18ef01f8c8343f00710b85096d1b53a", + "sha256:622020d4521e22fb371e15f580d153134bfb68d6a429d1342a25f051ec72df1c", + "sha256:627402ad8dea044dde2eccde4370560a2b750ef894c9578e1d4f8ffd54000461", + "sha256:644df54d729ef810dcd0f7732e50e5ad1bd0a135278ed8d6bcb06f33b6b6f708", + "sha256:64641a6068a16201366476731301441ce93457eb8452056f570133a6ceb15fca", + "sha256:64c2baa7774bc22dd4474248ba16fe1a7f611c13ac6123408694d4cc93d66dbd", + "sha256:6588c459c5627fefa30139be4d2e28a2c2a1d0d1c265aad2ba1935a7863a4913", + "sha256:66bc5eb8a323ed9894f8fa0ee6cb3e3fb2403d99aee635078fd19a8bc7a5a5da", + "sha256:68a2610dbe138fa8c5826b3f6d98a7cfc29707b850ddcc3e21910a6fe51f6ca0", + "sha256:6935bbf153f9a965f1e07c2649c0849d29832487c52bb4a5c5066031d8b44fd5", + "sha256:6992030d43b916407c9aa52e9673612ff39a575523c5f4cf72cdef75365709a5", + "sha256:6a014510830df1475176466b6087fc0c08b47a36714823e58d8b8d7709132a96", + "sha256:6ab833e4735a7e5533711a6ea2df26459b96f9eec36d23f74cafe03631647c41", + "sha256:6cc6ee342fb7fa2471bd9b6d6fdfc78925a697bf5c2bcd0a302e98b0d35bfad3", + "sha256:6cf58416653c5901e12624e4013708b6e11142956e7f35e7a83f1ab02f3fe456", + "sha256:70a9768e1b9d79edca17890175ba915654ee1725975d69ab64813dd785a2bd5c", + "sha256:70ac664a48aa64e5e635ae5566f5227f2ab7f66a3990d67566d9907edcbbf867", + "sha256:71e97313406ccf55d32cc98a533ee05c61e15d11b99215b237346171c179c0b0", + "sha256:7221d49259aa1e5a8f00d3d28b1e0b76031655ca74bb287123ef56c3db92f213", + "sha256:74b28c6334cca4dd704e8004cba1955af0b778cf449142e581e404bd211fb619", + "sha256:764b521b75701f60683500d8621841bec41a65eb739b8466000c6fdbc256c240", + "sha256:78bfa756eab503673991bdcf464917ef7845a964903d3302c5f68417ecdc948c", + "sha256:794f04eec78f1d0e35d9e0c36cbbb22e42d370dda1609fb03bcd7aeb458c6377", + "sha256:79bd05260359170f78b181b59ce871673ed01ba048deef4bf49a36ab3e72e80b", + "sha256:7a7efd5b6d3e30d81ec68ab8a88252d7c7c6f13aaa875009fe3097eb4e30b84c", + "sha256:7c17b64b0a6ef4e5affae6a3724010a7a66bda48a62cfe0674dabd46642e8b54", + "sha256:804f74efe22b6a227306dd890eecc4f8c59ff25ca35f1f14e7482bbce96ef10b", + "sha256:853e074d4931dbcba7480d4dcab23d5c56bd9607f92825ab80ee2bd916edea53", + "sha256:857500f88b17a6479202ff5fe5f580fc3404922cd02ab3716197adf1ef628029", + "sha256:865bad62df277c04beed9478fe665b9ef63eb28fe026d5dedcb89b537d2e2ea6", + "sha256:88e22fc0a6684337d25c994381ed8a1580a6f5ebebd5ad41f89f663ff4ec2885", + "sha256:8b9c07e7a45bb64e21df4b6aa623cb8ba214dfb47d2027d90eac197329bb5e94", + "sha256:8de8f9d6caa7f25b204fc861718815d41cbcf27ee8f028c89c882a0cf4ae4134", + "sha256:8e77c69d5892cb5ba71703c4057091e31ccf534bd7f129307a4d084d90d014b8", + "sha256:9123716666e25b7b71c4e1789ec829ed18663152008b58544d95b008ed9e21e9", + "sha256:958244ad566c3ffc385f47dddde4145088a0ab893504b54b52c041987a8c1863", + "sha256:96323338e6c14e958d775700ec8a88346014a85e5de73ac7967db0367582049b", + "sha256:9676bfc686fa6a3fa10cd4ae6b76cae8be26eb5ec6811d2a325636c460da1806", + "sha256:9b0ff53900566bc6325ecde9181d89afadc59c5ffa39bddf084aaedfe3b06a11", + "sha256:9b9ec9c9978b708d488bec36b9e4c94d88fd12ccac3e62134a9d17ddba910ea9", + "sha256:9c6ad0fbf105f6bcc9300c00010a2ffa44ea6f555df1a2ad95c88f5656104817", + "sha256:9ca66b8e90daca431b7ca1408cae085d025326570e57749695d6a01454790e95", + "sha256:9e2addd2d1866fe112bc6f80117bcc6bc25191c5ed1bfbcf9f1386a884252ae8", + "sha256:a0af35bd8ebf84888373630f73f24e86bf016642fb8576fba49d3d6b560b7cbc", + "sha256:a2b44bec7adf3e9305ce6cbfa47a4395667e744097faed97abb4728748ba7d47", + "sha256:a2dfe7e2473f9b59496247aad6e23b405ddf2e12ef0765677b0081c02d6c2c0b", + "sha256:a55ee573116ba208932e2d1a037cc4b10d2c1cb264ced2184d00b18ce585b2c0", + "sha256:a7baf9ffc238e4bf401299f50e971a45bfcc10a785522541a6e3179c83eabf0a", + "sha256:a8d5c70e04aac1eda5c829a26d1f75c6e5286c74743133d9f742cda8e53b9c2f", + "sha256:a91481dbcddf1736c98a80b122afa0f7296eeb80b72344d7f45dc9f781551f56", + "sha256:ab31a88a651039a07a3ae327d68ebdd8bc589b16938c09ef3f32a4b809dc96ef", + "sha256:abc25c3cab9ec7fcd299b9bcb3b8d4a1231877e425c650fa1c7576c5107ab851", + "sha256:adfb84ca6b87e06bc6b146dc7da7623395db1e31621c4785ad0658c5028b37d7", + "sha256:afbbdb120d1e78d2ba8064a68058001b871154cc57787031b645c9142b937a62", + "sha256:afd5562927cdef7c4f5550374acbc117fd4ecc05b5007bdfa57cc5355864e0a4", + "sha256:b070bbe8d3f0f6147689bed981d19bbb33070225373338df755a46893528104a", + "sha256:b0b58fbfa1bf7367dde8a557994e3b1637294be6cf2169810375caf8571a085c", + "sha256:b560e3aa4b1d49e0e6c847d72665384db35b2f5d45f8e6a5c0072e0283430533", + "sha256:b6241d4eee5f89453307c2f2bfa03b50362052ca0af1efecf9fef9a41a22bb4f", + "sha256:b6787b643356111dfd4032b5bffe26d2f8331556ecb79e15dacb9275da02866e", + "sha256:bcbf4af004f98793a95355980764b3d80d47117678118a44a80b721c9913436a", + "sha256:beb72935a941965c52990f3a32d7f07ce869fe21c6af8b34bf6a277b33a345d3", + "sha256:bf2e2458345d9bffb0d9ec16557d8858c9c88d2d11fed53998512504cd9df49b", + "sha256:c2d35a1d047efd68027817b32ab1586c1169e60ca02c65d428ae815b593e65d4", + "sha256:c38d7b9a690b090de999835f0443d8aa93ce5f2064035dfc48f27f02b4afc3d0", + "sha256:c6f2c8372b98208ce609c9e1d707f6918cc118fea4e2c754c9f0812c04ca116d", + "sha256:c817d420c60a5183953c783b0547d9eb43b7b344a2c46f69513d5952a78cddf3", + "sha256:c8ba129e6d3b0136a0f50345b2cb3db53f6bda5dd8c7f5d83fbccba97fb5dcb5", + "sha256:c94e75445b00319c1fad60f3c98b09cd63fe1134a8a953dcd48989ef42318534", + "sha256:cc4691d60512798304acb9207987e7b2b7c44627ea88b9d77489bbe3e6cc3bd4", + "sha256:cc518cea79fd1e2f6c90baafa28906d4309d24f3a63e801d855e7424c5b34144", + "sha256:cd53553ddad4a9c2f1f022756ae64abe16da1feb497edf4d9f87f99ec7cf86bd", + "sha256:cf22b41fdae514ee2f1691b6c3cdeae666d8b7fa9434de445f12bbeee0cf48dd", + "sha256:d38c8f50ecf57f0463399569aa388b232cf1a2ffb8f0a9a5412d0db57e054860", + "sha256:d3be9b2076112e51b323bdf6d5a7f8a798de55fb8d95fcb64bd179460cdc0704", + "sha256:d4f2cc7060dc3646632d7f15fe68e2fa98f58e35dd5666cd525f3b35d3fed7f8", + "sha256:d7520db34088c96cc0e0a3ad51a4fd5b401f279ee112aa2b7f8f976d8582606d", + "sha256:d793bebb202a6000390a5390078e945bbb49855c29c7e4d56a85901326c3b5d9", + "sha256:da052e7962ea2d5e5ef5bc0355d55007407087392cf465b7ad84ce5f3e25fe0f", + "sha256:dae0ed02f6b075426accbf6b2863c3d0a7eacc1b41fb40f2251d931e50188dad", + "sha256:ddc678fb4c7e30cf830a2b5a8d869538bc55b28d6c68544d09c7d0d8f17694dc", + "sha256:df2e6f546c4df14bc81f9498bbc007fbb87669f1bb707c6138878c46b06f6510", + "sha256:e02c5175f63effbd7c5e590399c118d5db6183bbfe8e0d118bdb5c2d1b48d937", + "sha256:e196a4ff48310ba62e53a8e0f97ca2bca83cdd2fe2934d8b5cb0df0a841b193a", + "sha256:e233db59c8f76630c512ab4a4daf5a5986da5c3d5b44b8e9fc742f2a24dbd460", + "sha256:e32be23d538753a8adb6c85bd539f5fd3b15cb987404327c569dfc5fd8366e85", + "sha256:e3d30321949861404323c50aebeb1943461a67cd51d4200ab02babc58bd06a86", + "sha256:e89580a581bf478d8dcb97d9cd011d567768e8bc4095f8557b21c4d4c5fea7d0", + "sha256:e998e304036198b4f6914e6a1e2b6f925208a20e2042563d9734881150c6c246", + "sha256:ec42088248c596dbd61d4ae8a5b004f97a4d91a9fd286f632e42e60b706718d7", + "sha256:efa7b51824aa0ee957ccd5a741c73e6851de55f40d807f08069eb4c5a26b2baa", + "sha256:f0a1bc63a465b6d72569a9bba9f2ef0334c4e03958e043da1920299100bc7c08", + "sha256:f18a5a84e16886898e51ab4b1d43acb3083c39b14c8caeb3589aabff0ee0b270", + "sha256:f2a9efc53d5b714b8df2b4b3e992accf8ce5bbdfe544d74d5c6766c9e1146a3a", + "sha256:f3bbbc998d42f8e561f347e798b85513ba4da324c2b3f9b7969e9c45b10f6169", + "sha256:f42038016852ae51b4088b2862126535cc4fc85802bfe30dea3500fdfaf1864e", + "sha256:f443cdef978430887ed55112b491f670bba6462cea7a7742ff8f14b7abb98d75", + "sha256:f51969bac61441fd31f028d7b3b45962f3ecebf691a510495e5d2cd8c8092dbd", + "sha256:f8aca2e3a72f37bfc7b14ba96d4056244001ddcc18382bd0daa087fd2e68a354", + "sha256:f9737bf36262046213a28e789cc82d82c6ef19c85a0cf05e75c670a33342ac2c", + "sha256:fd6037392f2d57793ab98d9e26798f44b8b4da2f2464388588f48ac52c489ea1", + "sha256:feaa45c0eae424d3e90d78823f3828e7dc42a42f21ed420db98da2c4ecf0a2cb", + "sha256:ff097ae562e637409b429a7ac958a20aab237a0378c42dabaa1e3abf2f896e5f", + "sha256:ff46d772d5f6f73564979cd77a4fffe55c916a05f3cb70e7c9c0590059fb29ef" ], "markers": "python_version >= '3.6'", - "version": "==5.1.0" + "version": "==5.2.1" }, "mako": { "hashes": [ - "sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e", - "sha256:32a99d70754dfce237019d17ffe4a282d2d3351b9c476e90d8a60e63f133b80c" + "sha256:5324b88089a8978bf76d1629774fcc2f1c07b82acdf00f4c5dd8ceadfffc4b40", + "sha256:e16c01d9ab9c11f7290eef1cfefc093fb5a45ee4a3da09e2fec2e4d1bae54e73" ], "markers": "python_version >= '3.8'", - "version": "==1.3.2" + "version": "==1.3.3" }, "markupsafe": { "hashes": [ @@ -748,10 +825,10 @@ }, "phonenumberslite": { "hashes": [ - "sha256:4d92f4f9079bb83588dde45fd8a414bc13e4962886aa4d23576984196f4d83c2", - "sha256:7426bc46af3de5a800a4c8f33ab13e33225d2c8ed4fc52aa3c0380dadd8d7381" + "sha256:343b300d9c8ac4dca84e6b922ec51c3d838f2feabf9dd2418da64b639d220879", + "sha256:64b513134b785fbeeaf4cc020e18d384541c4118ed3ece2118437d996f435ca0" ], - "version": "==8.13.33" + "version": "==8.13.35" }, "psycopg2-binary": { "hashes": [ @@ -834,10 +911,11 @@ }, "pycparser": { "hashes": [ - "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", - "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" + "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", + "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc" ], - "version": "==2.21" + "markers": "python_version >= '3.8'", + "version": "==2.22" }, "pycryptodomex": { "hashes": [ @@ -880,96 +958,96 @@ }, "pydantic": { "hashes": [ - "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6", - "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5" + "sha256:9dee74a271705f14f9a1567671d144a851c675b072736f0a7b2608fd9e495352", + "sha256:b5ecdd42262ca2462e2624793551e80911a1e989f462910bb81aef974b4bb383" ], "markers": "python_version >= '3.8'", - "version": "==2.6.4" + "version": "==2.7.0" }, "pydantic-core": { "hashes": [ - "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a", - "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed", - "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979", - "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff", - "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5", - "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45", - "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340", - "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad", - "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23", - "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6", - "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7", - "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241", - "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda", - "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187", - "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba", - "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c", - "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2", - "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c", - "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132", - "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf", - "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972", - "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db", - "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade", - "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4", - "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8", - "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f", - "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9", - "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48", - "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec", - "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d", - "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9", - "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb", - "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4", - "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89", - "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c", - "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9", - "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da", - "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac", - "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b", - "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf", - "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e", - "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137", - "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1", - "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b", - "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8", - "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e", - "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053", - "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01", - "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe", - "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd", - "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805", - "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183", - "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8", - "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99", - "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820", - "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074", - "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256", - "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8", - "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975", - "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad", - "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e", - "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca", - "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df", - "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b", - "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a", - "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a", - "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721", - "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a", - "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f", - "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2", - "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97", - "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6", - "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed", - "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc", - "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1", - "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe", - "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120", - "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f", - "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a" + "sha256:030e4f9516f9947f38179249778709a460a3adb516bf39b5eb9066fcfe43d0e6", + "sha256:09f03dfc0ef8c22622eaa8608caa4a1e189cfb83ce847045eca34f690895eccb", + "sha256:12a05db5013ec0ca4a32cc6433f53faa2a014ec364031408540ba858c2172bb0", + "sha256:14fe73881cf8e4cbdaded8ca0aa671635b597e42447fec7060d0868b52d074e6", + "sha256:1a0c3e718f4e064efde68092d9d974e39572c14e56726ecfaeebbe6544521f47", + "sha256:1be91ad664fc9245404a789d60cba1e91c26b1454ba136d2a1bf0c2ac0c0505a", + "sha256:201713f2f462e5c015b343e86e68bd8a530a4f76609b33d8f0ec65d2b921712a", + "sha256:2027493cc44c23b598cfaf200936110433d9caa84e2c6cf487a83999638a96ac", + "sha256:250ae39445cb5475e483a36b1061af1bc233de3e9ad0f4f76a71b66231b07f88", + "sha256:2533ad2883f001efa72f3d0e733fb846710c3af6dcdd544fe5bf14fa5fe2d7db", + "sha256:25595ac311f20e5324d1941909b0d12933f1fd2171075fcff763e90f43e92a0d", + "sha256:2684a94fdfd1b146ff10689c6e4e815f6a01141781c493b97342cdc5b06f4d5d", + "sha256:27f1009dc292f3b7ca77feb3571c537276b9aad5dd4efb471ac88a8bd09024e9", + "sha256:2adaeea59849ec0939af5c5d476935f2bab4b7f0335b0110f0f069a41024278e", + "sha256:2ae80f72bb7a3e397ab37b53a2b49c62cc5496412e71bc4f1277620a7ce3f52b", + "sha256:2d5728e93d28a3c63ee513d9ffbac9c5989de8c76e049dbcb5bfe4b923a9739d", + "sha256:2e91711e36e229978d92642bfc3546333a9127ecebb3f2761372e096395fc649", + "sha256:2fe0c1ce5b129455e43f941f7a46f61f3d3861e571f2905d55cdbb8b5c6f5e2c", + "sha256:38a5024de321d672a132b1834a66eeb7931959c59964b777e8f32dbe9523f6b1", + "sha256:3e352f0191d99fe617371096845070dee295444979efb8f27ad941227de6ad09", + "sha256:48dd883db92e92519201f2b01cafa881e5f7125666141a49ffba8b9facc072b0", + "sha256:54764c083bbe0264f0f746cefcded6cb08fbbaaf1ad1d78fb8a4c30cff999a90", + "sha256:54c7375c62190a7845091f521add19b0f026bcf6ae674bdb89f296972272e86d", + "sha256:561cf62c8a3498406495cfc49eee086ed2bb186d08bcc65812b75fda42c38294", + "sha256:56823a92075780582d1ffd4489a2e61d56fd3ebb4b40b713d63f96dd92d28144", + "sha256:582cf2cead97c9e382a7f4d3b744cf0ef1a6e815e44d3aa81af3ad98762f5a9b", + "sha256:58aca931bef83217fca7a390e0486ae327c4af9c3e941adb75f8772f8eeb03a1", + "sha256:5f7973c381283783cd1043a8c8f61ea5ce7a3a58b0369f0ee0ee975eaf2f2a1b", + "sha256:6395a4435fa26519fd96fdccb77e9d00ddae9dd6c742309bd0b5610609ad7fb2", + "sha256:63d7523cd95d2fde0d28dc42968ac731b5bb1e516cc56b93a50ab293f4daeaad", + "sha256:641a018af4fe48be57a2b3d7a1f0f5dbca07c1d00951d3d7463f0ac9dac66622", + "sha256:667880321e916a8920ef49f5d50e7983792cf59f3b6079f3c9dac2b88a311d17", + "sha256:684d840d2c9ec5de9cb397fcb3f36d5ebb6fa0d94734f9886032dd796c1ead06", + "sha256:68717c38a68e37af87c4da20e08f3e27d7e4212e99e96c3d875fbf3f4812abfc", + "sha256:6b7bbb97d82659ac8b37450c60ff2e9f97e4eb0f8a8a3645a5568b9334b08b50", + "sha256:72722ce529a76a4637a60be18bd789d8fb871e84472490ed7ddff62d5fed620d", + "sha256:73c1bc8a86a5c9e8721a088df234265317692d0b5cd9e86e975ce3bc3db62a59", + "sha256:76909849d1a6bffa5a07742294f3fa1d357dc917cb1fe7b470afbc3a7579d539", + "sha256:76b86e24039c35280ceee6dce7e62945eb93a5175d43689ba98360ab31eebc4a", + "sha256:7a5d83efc109ceddb99abd2c1316298ced2adb4570410defe766851a804fcd5b", + "sha256:80e0e57cc704a52fb1b48f16d5b2c8818da087dbee6f98d9bf19546930dc64b5", + "sha256:85233abb44bc18d16e72dc05bf13848a36f363f83757541f1a97db2f8d58cfd9", + "sha256:907a4d7720abfcb1c81619863efd47c8a85d26a257a2dbebdb87c3b847df0278", + "sha256:9376d83d686ec62e8b19c0ac3bf8d28d8a5981d0df290196fb6ef24d8a26f0d6", + "sha256:94b9769ba435b598b547c762184bcfc4783d0d4c7771b04a3b45775c3589ca44", + "sha256:9a29726f91c6cb390b3c2338f0df5cd3e216ad7a938762d11c994bb37552edb0", + "sha256:9b6431559676a1079eac0f52d6d0721fb8e3c5ba43c37bc537c8c83724031feb", + "sha256:9ece8a49696669d483d206b4474c367852c44815fca23ac4e48b72b339807f80", + "sha256:a139fe9f298dc097349fb4f28c8b81cc7a202dbfba66af0e14be5cfca4ef7ce5", + "sha256:a32204489259786a923e02990249c65b0f17235073149d0033efcebe80095570", + "sha256:a3982b0a32d0a88b3907e4b0dc36809fda477f0757c59a505d4e9b455f384b8b", + "sha256:aad17e462f42ddbef5984d70c40bfc4146c322a2da79715932cd8976317054de", + "sha256:b560b72ed4816aee52783c66854d96157fd8175631f01ef58e894cc57c84f0f6", + "sha256:b6b0e4912030c6f28bcb72b9ebe4989d6dc2eebcd2a9cdc35fefc38052dd4fe8", + "sha256:baf1c7b78cddb5af00971ad5294a4583188bda1495b13760d9f03c9483bb6203", + "sha256:c0295d52b012cbe0d3059b1dba99159c3be55e632aae1999ab74ae2bd86a33d7", + "sha256:c562b49c96906b4029b5685075fe1ebd3b5cc2601dfa0b9e16c2c09d6cbce048", + "sha256:c69567ddbac186e8c0aadc1f324a60a564cfe25e43ef2ce81bcc4b8c3abffbae", + "sha256:ca71d501629d1fa50ea7fa3b08ba884fe10cefc559f5c6c8dfe9036c16e8ae89", + "sha256:ca976884ce34070799e4dfc6fbd68cb1d181db1eefe4a3a94798ddfb34b8867f", + "sha256:d0491006a6ad20507aec2be72e7831a42efc93193d2402018007ff827dc62926", + "sha256:d074b07a10c391fc5bbdcb37b2f16f20fcd9e51e10d01652ab298c0d07908ee2", + "sha256:d2ce426ee691319d4767748c8e0895cfc56593d725594e415f274059bcf3cb76", + "sha256:d4284c621f06a72ce2cb55f74ea3150113d926a6eb78ab38340c08f770eb9b4d", + "sha256:d5e6b7155b8197b329dc787356cfd2684c9d6a6b1a197f6bbf45f5555a98d411", + "sha256:d816f44a51ba5175394bc6c7879ca0bd2be560b2c9e9f3411ef3a4cbe644c2e9", + "sha256:dd3f79e17b56741b5177bcc36307750d50ea0698df6aa82f69c7db32d968c1c2", + "sha256:dd63cec4e26e790b70544ae5cc48d11b515b09e05fdd5eff12e3195f54b8a586", + "sha256:de9d3e8717560eb05e28739d1b35e4eac2e458553a52a301e51352a7ffc86a35", + "sha256:df4249b579e75094f7e9bb4bd28231acf55e308bf686b952f43100a5a0be394c", + "sha256:e178e5b66a06ec5bf51668ec0d4ac8cfb2bdcb553b2c207d58148340efd00143", + "sha256:e60defc3c15defb70bb38dd605ff7e0fae5f6c9c7cbfe0ad7868582cb7e844a6", + "sha256:ee2794111c188548a4547eccc73a6a8527fe2af6cf25e1a4ebda2fd01cdd2e60", + "sha256:ee7ccc7fb7e921d767f853b47814c3048c7de536663e82fbc37f5eb0d532224b", + "sha256:ee9cf33e7fe14243f5ca6977658eb7d1042caaa66847daacbd2117adb258b226", + "sha256:f0f17814c505f07806e22b28856c59ac80cee7dd0fbb152aed273e116378f519", + "sha256:f3202a429fe825b699c57892d4371c74cc3456d8d71b7f35d6028c96dfecad31", + "sha256:f7054fdc556f5421f01e39cbb767d5ec5c1139ea98c3e5b350e02e62201740c7", + "sha256:fd1a9edb9dd9d79fbeac1ea1f9a8dd527a6113b18d2e9bcc0d541d308dae639b" ], "markers": "python_version >= '3.8'", - "version": "==2.16.3" + "version": "==2.18.1" }, "pydantic-settings": { "hashes": [ @@ -1030,11 +1108,11 @@ }, "setuptools": { "hashes": [ - "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e", - "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c" + "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987", + "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32" ], "markers": "python_version >= '3.8'", - "version": "==69.2.0" + "version": "==69.5.1" }, "six": { "hashes": [ @@ -1046,11 +1124,11 @@ }, "sqlparse": { "hashes": [ - "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3", - "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c" + "sha256:714d0a4932c059d16189f58ef5411ec2287a4360f17cdd0edd2d09d4c5087c93", + "sha256:c204494cd97479d0e39f28c93d46c0b2d5959c7b9ab904762ea6c7af211c8663" ], - "markers": "python_version >= '3.5'", - "version": "==0.4.4" + "markers": "python_version >= '3.8'", + "version": "==0.5.0" }, "tblib": { "hashes": [ @@ -1063,12 +1141,12 @@ }, "typing-extensions": { "hashes": [ - "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475", - "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb" + "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0", + "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.10.0" + "version": "==4.11.0" }, "urllib3": { "hashes": [ @@ -1097,45 +1175,45 @@ }, "zope.interface": { "hashes": [ - "sha256:02adbab560683c4eca3789cc0ac487dcc5f5a81cc48695ec247f00803cafe2fe", - "sha256:14e02a6fc1772b458ebb6be1c276528b362041217b9ca37e52ecea2cbdce9fac", - "sha256:25e0af9663eeac6b61b231b43c52293c2cb7f0c232d914bdcbfd3e3bd5c182ad", - "sha256:2606955a06c6852a6cff4abeca38346ed01e83f11e960caa9a821b3626a4467b", - "sha256:396f5c94654301819a7f3a702c5830f0ea7468d7b154d124ceac823e2419d000", - "sha256:3b240883fb43160574f8f738e6d09ddbdbf8fa3e8cea051603d9edfd947d9328", - "sha256:3b6c62813c63c543a06394a636978b22dffa8c5410affc9331ce6cdb5bfa8565", - "sha256:4ae9793f114cee5c464cc0b821ae4d36e1eba961542c6086f391a61aee167b6f", - "sha256:4bce517b85f5debe07b186fc7102b332676760f2e0c92b7185dd49c138734b70", - "sha256:4d45d2ba8195850e3e829f1f0016066a122bfa362cc9dc212527fc3d51369037", - "sha256:4dd374927c00764fcd6fe1046bea243ebdf403fba97a937493ae4be2c8912c2b", - "sha256:506f5410b36e5ba494136d9fa04c548eaf1a0d9c442b0b0e7a0944db7620e0ab", - "sha256:59f7374769b326a217d0b2366f1c176a45a4ff21e8f7cebb3b4a3537077eff85", - "sha256:5ee9789a20b0081dc469f65ff6c5007e67a940d5541419ca03ef20c6213dd099", - "sha256:6fc711acc4a1c702ca931fdbf7bf7c86f2a27d564c85c4964772dadf0e3c52f5", - "sha256:75d2ec3d9b401df759b87bc9e19d1b24db73083147089b43ae748aefa63067ef", - "sha256:76e0531d86523be7a46e15d379b0e975a9db84316617c0efe4af8338dc45b80c", - "sha256:8af82afc5998e1f307d5e72712526dba07403c73a9e287d906a8aa2b1f2e33dd", - "sha256:8f5d2c39f3283e461de3655e03faf10e4742bb87387113f787a7724f32db1e48", - "sha256:97785604824981ec8c81850dd25c8071d5ce04717a34296eeac771231fbdd5cd", - "sha256:a3046e8ab29b590d723821d0785598e0b2e32b636a0272a38409be43e3ae0550", - "sha256:abb0b3f2cb606981c7432f690db23506b1db5899620ad274e29dbbbdd740e797", - "sha256:ac7c2046d907e3b4e2605a130d162b1b783c170292a11216479bb1deb7cadebe", - "sha256:af27b3fe5b6bf9cd01b8e1c5ddea0a0d0a1b8c37dc1c7452f1e90bf817539c6d", - "sha256:b386b8b9d2b6a5e1e4eadd4e62335571244cb9193b7328c2b6e38b64cfda4f0e", - "sha256:b66335bbdbb4c004c25ae01cc4a54fd199afbc1fd164233813c6d3c2293bb7e1", - "sha256:d54f66c511ea01b9ef1d1a57420a93fbb9d48a08ec239f7d9c581092033156d0", - "sha256:de125151a53ecdb39df3cb3deb9951ed834dd6a110a9e795d985b10bb6db4532", - "sha256:de7916380abaef4bb4891740879b1afcba2045aee51799dfd6d6ca9bdc71f35f", - "sha256:e2fefad268ff5c5b314794e27e359e48aeb9c8bb2cbb5748a071757a56f6bb8f", - "sha256:e7b2bed4eea047a949296e618552d3fed00632dc1b795ee430289bdd0e3717f3", - "sha256:e87698e2fea5ca2f0a99dff0a64ce8110ea857b640de536c76d92aaa2a91ff3a", - "sha256:ede888382882f07b9e4cd942255921ffd9f2901684198b88e247c7eabd27a000", - "sha256:f444de0565db46d26c9fa931ca14f497900a295bd5eba480fc3fad25af8c763e", - "sha256:fa994e8937e8ccc7e87395b7b35092818905cf27c651e3ff3e7f29729f5ce3ce", - "sha256:febceb04ee7dd2aef08c2ff3d6f8a07de3052fc90137c507b0ede3ea80c21440" + "sha256:014bb94fe6bf1786da1aa044eadf65bc6437bcb81c451592987e5be91e70a91e", + "sha256:01a0b3dd012f584afcf03ed814bce0fc40ed10e47396578621509ac031be98bf", + "sha256:10cde8dc6b2fd6a1d0b5ca4be820063e46ddba417ab82bcf55afe2227337b130", + "sha256:187f7900b63845dcdef1be320a523dbbdba94d89cae570edc2781eb55f8c2f86", + "sha256:1b0c4c90e5eefca2c3e045d9f9ed9f1e2cdbe70eb906bff6b247e17119ad89a1", + "sha256:22e8a218e8e2d87d4d9342aa973b7915297a08efbebea5b25900c73e78ed468e", + "sha256:26c9a37fb395a703e39b11b00b9e921c48f82b6e32cc5851ad5d0618cd8876b5", + "sha256:2bb78c12c1ad3a20c0d981a043d133299117b6854f2e14893b156979ed4e1d2c", + "sha256:2c3cfb272bcb83650e6695d49ae0d14dd06dc694789a3d929f23758557a23d92", + "sha256:2f32010ffb87759c6a3ad1c65ed4d2e38e51f6b430a1ca11cee901ec2b42e021", + "sha256:3c8731596198198746f7ce2a4487a0edcbc9ea5e5918f0ab23c4859bce56055c", + "sha256:40aa8c8e964d47d713b226c5baf5f13cdf3a3169c7a2653163b17ff2e2334d10", + "sha256:4137025731e824eee8d263b20682b28a0bdc0508de9c11d6c6be54163e5b7c83", + "sha256:46034be614d1f75f06e7dcfefba21d609b16b38c21fc912b01a99cb29e58febb", + "sha256:483e118b1e075f1819b3c6ace082b9d7d3a6a5eb14b2b375f1b80a0868117920", + "sha256:4d6b229f5e1a6375f206455cc0a63a8e502ed190fe7eb15e94a312dc69d40299", + "sha256:567d54c06306f9c5b6826190628d66753b9f2b0422f4c02d7c6d2b97ebf0a24e", + "sha256:5683aa8f2639016fd2b421df44301f10820e28a9b96382a6e438e5c6427253af", + "sha256:600101f43a7582d5b9504a7c629a1185a849ce65e60fca0f6968dfc4b76b6d39", + "sha256:62e32f02b3f26204d9c02c3539c802afc3eefb19d601a0987836ed126efb1f21", + "sha256:69dedb790530c7ca5345899a1b4cb837cc53ba669051ea51e8c18f82f9389061", + "sha256:72d5efecad16c619a97744a4f0b67ce1bcc88115aa82fcf1dc5be9bb403bcc0b", + "sha256:8d407e0fd8015f6d5dfad481309638e1968d70e6644e0753f229154667dd6cd5", + "sha256:a058e6cf8d68a5a19cb5449f42a404f0d6c2778b897e6ce8fadda9cea308b1b0", + "sha256:a1adc14a2a9d5e95f76df625a9b39f4709267a483962a572e3f3001ef90ea6e6", + "sha256:a56fe1261230093bfeedc1c1a6cd6f3ec568f9b07f031c9a09f46b201f793a85", + "sha256:ad4524289d8dbd6fb5aa17aedb18f5643e7d48358f42c007a5ee51a2afc2a7c5", + "sha256:afa0491a9f154cf8519a02026dc85a416192f4cb1efbbf32db4a173ba28b289a", + "sha256:bf34840e102d1d0b2d39b1465918d90b312b1119552cebb61a242c42079817b9", + "sha256:c40df4aea777be321b7e68facb901bc67317e94b65d9ab20fb96e0eb3c0b60a1", + "sha256:d0e7321557c702bd92dac3c66a2f22b963155fdb4600133b6b29597f62b71b12", + "sha256:d165d7774d558ea971cb867739fb334faf68fc4756a784e689e11efa3becd59e", + "sha256:e78a183a3c2f555c2ad6aaa1ab572d1c435ba42f1dc3a7e8c82982306a19b785", + "sha256:e8fa0fb05083a1a4216b4b881fdefa71c5d9a106e9b094cd4399af6b52873e91", + "sha256:f83d6b4b22262d9a826c3bd4b2fbfafe1d0000f085ef8e44cd1328eea274ae6a", + "sha256:f95bebd0afe86b2adc074df29edb6848fc4d474ff24075e2c263d698774e108d" ], "markers": "python_version >= '3.7'", - "version": "==6.2" + "version": "==6.3" } }, "develop": { @@ -1166,32 +1244,32 @@ }, "black": { "hashes": [ - "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f", - "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93", - "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11", - "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0", - "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9", - "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5", - "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213", - "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d", - "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7", - "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837", - "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f", - "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395", - "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995", - "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f", - "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597", - "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959", - "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5", - "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb", - "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4", - "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7", - "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd", - "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7" + "sha256:1bb9ca06e556a09f7f7177bc7cb604e5ed2d2df1e9119e4f7d2f1f7071c32e5d", + "sha256:21f9407063ec71c5580b8ad975653c66508d6a9f57bd008bb8691d273705adcd", + "sha256:4396ca365a4310beef84d446ca5016f671b10f07abdba3e4e4304218d2c71d33", + "sha256:44d99dfdf37a2a00a6f7a8dcbd19edf361d056ee51093b2445de7ca09adac965", + "sha256:5cd5b4f76056cecce3e69b0d4c228326d2595f506797f40b9233424e2524c070", + "sha256:64578cf99b6b46a6301bc28bdb89f9d6f9b592b1c5837818a177c98525dbe397", + "sha256:64e60a7edd71fd542a10a9643bf369bfd2644de95ec71e86790b063aa02ff745", + "sha256:652e55bb722ca026299eb74e53880ee2315b181dfdd44dca98e43448620ddec1", + "sha256:6644f97a7ef6f401a150cca551a1ff97e03c25d8519ee0bbc9b0058772882665", + "sha256:6ad001a9ddd9b8dfd1b434d566be39b1cd502802c8d38bbb1ba612afda2ef436", + "sha256:71d998b73c957444fb7c52096c3843875f4b6b47a54972598741fe9a7f737fcb", + "sha256:74eb9b5420e26b42c00a3ff470dc0cd144b80a766128b1771d07643165e08d0e", + "sha256:75a2d0b4f5eb81f7eebc31f788f9830a6ce10a68c91fbe0fade34fff7a2836e6", + "sha256:7852b05d02b5b9a8c893ab95863ef8986e4dda29af80bbbda94d7aee1abf8702", + "sha256:7f2966b9b2b3b7104fca9d75b2ee856fe3fdd7ed9e47c753a4bb1a675f2caab8", + "sha256:8e5537f456a22cf5cfcb2707803431d2feeb82ab3748ade280d6ccd0b40ed2e8", + "sha256:d4e71cdebdc8efeb6deaf5f2deb28325f8614d48426bed118ecc2dcaefb9ebf3", + "sha256:dae79397f367ac8d7adb6c779813328f6d690943f64b32983e896bcccd18cbad", + "sha256:e3a3a092b8b756c643fe45f4624dbd5a389f770a4ac294cf4d0fce6af86addaf", + "sha256:eb949f56a63c5e134dfdca12091e98ffb5fd446293ebae123d10fc1abad00b9e", + "sha256:f07b69fda20578367eaebbd670ff8fc653ab181e1ff95d84497f9fa20e7d0641", + "sha256:f95cece33329dc4aa3b0e1a771c41075812e46cf3d6e3f1dfe3d91ff09826ed2" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==24.3.0" + "version": "==24.4.0" }, "blinker": { "hashes": [ @@ -1203,12 +1281,12 @@ }, "boto3": { "hashes": [ - "sha256:7ce8c9a50af2f8a159a0dd86b40011d8dfdaba35005a118e51cd3ac72dc630f1", - "sha256:d786e7fbe3c4152866199786468a625dc77b9f27294cd7ad4f63cd2e0c927287" + "sha256:168894499578a9d69d6f7deb5811952bf4171c51b95749a9aef32cf67bc71f87", + "sha256:1bd4cef11b7c5f293cede50f3d33ca89fe3413c51f1864f40163c56a732dd6b3" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.34.71" + "version": "==1.34.88" }, "boto3-mocking": { "hashes": [ @@ -1221,28 +1299,28 @@ }, "boto3-stubs": { "hashes": [ - "sha256:1be579780a39a75394db0c413594aec380afde86dbc7eb5eb086a97a25d3c995", - "sha256:c9959c48ee1b53e9d19e424898caacf365aaee34cee4c70f40692e2b47bc8099" + "sha256:23ca9e0cd0d3e7702d6631a1e94a4208a26b39fa6b12c734427e68a7fa649477", + "sha256:8f472d1bf09743c3d33304ecc8830d70ebe3ca19ac9604ae8da9af55421b0fce" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.34.71" + "version": "==1.34.88" }, "botocore": { "hashes": [ - "sha256:3bc9e23aee73fe6f097823d61f79a8877790436038101a83fa96c7593e8109f8", - "sha256:c58f9ed71af2ea53d24146187130541222d7de8c27eb87d23f15457e7b83d88b" + "sha256:36f2e9e8dfa856e55dbbe703aea601f134db3fddc3615f1020a755b27fd26a5e", + "sha256:e87a660599ed3e14b2a770f4efc3df2f2f6d04f3c7bfd64ddbae186667864a7b" ], "markers": "python_version >= '3.8'", - "version": "==1.34.71" + "version": "==1.34.88" }, "botocore-stubs": { "hashes": [ - "sha256:0c3835c775db1387246c1ba8063b197604462fba8603d9b36b5dc60297197b2f", - "sha256:463248fd1d6e7b68a0c57bdd758d04c6bd0c5c2c3bfa81afdf9d64f0930b59bc" + "sha256:656e966ea152a4f2828892aa7a9673bc91799998f5a8efd8e8fe390f61c2f4f1", + "sha256:f55b03ae2e1706bd56299fd2975bb048f96aa49012a866e931a040a74f85c3cc" ], "markers": "python_version >= '3.8' and python_version < '4.0'", - "version": "==1.34.69" + "version": "==1.34.88" }, "click": { "hashes": [ @@ -1548,11 +1626,11 @@ }, "sqlparse": { "hashes": [ - "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3", - "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c" + "sha256:714d0a4932c059d16189f58ef5411ec2287a4360f17cdd0edd2d09d4c5087c93", + "sha256:c204494cd97479d0e39f28c93d46c0b2d5959c7b9ab904762ea6c7af211c8663" ], - "markers": "python_version >= '3.5'", - "version": "==0.4.4" + "markers": "python_version >= '3.8'", + "version": "==0.5.0" }, "stevedore": { "hashes": [ @@ -1572,11 +1650,11 @@ }, "types-awscrt": { "hashes": [ - "sha256:61811bbf4de95248939f9276a434be93d2b95f6ccfe8aa94e56999e9778cfcc2", - "sha256:79d5bfb01f64701b6cf442e89a37d9c4dc6dbb79a46f2f611739b2418d30ecfd" + "sha256:3ae374b553e7228ba41a528cf42bd0b2ad7303d806c73eff4aaaac1515e3ea4e", + "sha256:64898a2f4a2468f66233cb8c29c5f66de907cf80ba1ef5bb1359aef2f81bb521" ], "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==0.20.5" + "version": "==0.20.9" }, "types-cachetools": { "hashes": [ @@ -1589,11 +1667,11 @@ }, "types-pytz": { "hashes": [ - "sha256:9679eef0365db3af91ef7722c199dbb75ee5c1b67e3c4dd7bfbeb1b8a71c21a3", - "sha256:c93751ee20dfc6e054a0148f8f5227b9a00b79c90a4d3c9f464711a73179c89e" + "sha256:6810c8a1f68f21fdf0f4f374a432487c77645a0ac0b31de4bf4690cf21ad3981", + "sha256:8335d443310e2db7b74e007414e74c4f53b67452c0cb0d228ca359ccfba59659" ], "markers": "python_version >= '3.8'", - "version": "==2024.1.0.20240203" + "version": "==2024.1.0.20240417" }, "types-pyyaml": { "hashes": [ @@ -1605,29 +1683,29 @@ }, "types-requests": { "hashes": [ - "sha256:47872893d65a38e282ee9f277a4ee50d1b28bd592040df7d1fdaffdf3779937d", - "sha256:b1c1b66abfb7fa79aae09097a811c4aa97130eb8831c60e47aee4ca344731ca5" + "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1", + "sha256:6216cdac377c6b9a040ac1c0404f7284bd13199c0e1bb235f4324627e8898cf5" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==2.31.0.20240311" + "version": "==2.31.0.20240406" }, "types-s3transfer": { "hashes": [ - "sha256:35e4998c25df7f8985ad69dedc8e4860e8af3b43b7615e940d53c00d413bdc69", - "sha256:44fcdf0097b924a9aab1ee4baa1179081a9559ca62a88c807e2b256893ce688f" + "sha256:02154cce46528287ad76ad1a0153840e0492239a0887e8833466eccf84b98da0", + "sha256:49a7c81fa609ac1532f8de3756e64b58afcecad8767933310228002ec7adff74" ], - "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==0.10.0" + "markers": "python_version >= '3.8' and python_version < '4.0'", + "version": "==0.10.1" }, "typing-extensions": { "hashes": [ - "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475", - "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb" + "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0", + "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.10.0" + "version": "==4.11.0" }, "urllib3": { "hashes": [ diff --git a/src/requirements.txt b/src/requirements.txt index 91f2452ca..3c005f162 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,8 +1,8 @@ -i https://pypi.python.org/simple annotated-types==0.6.0; python_version >= '3.8' asgiref==3.8.1; python_version >= '3.8' -boto3==1.34.71; python_version >= '3.8' -botocore==1.34.71; python_version >= '3.8' +boto3==1.34.88; python_version >= '3.8' +botocore==1.34.88; python_version >= '3.8' cachetools==5.3.3; python_version >= '3.7' certifi==2024.2.2; python_version >= '3.6' cfenv==0.5.3 @@ -24,28 +24,28 @@ django-login-required-middleware==0.9.0 django-phonenumber-field[phonenumberslite]==7.3.0; python_version >= '3.8' django-widget-tweaks==1.5.0; python_version >= '3.8' environs[django]==11.0.0; python_version >= '3.8' -faker==24.4.0; python_version >= '3.8' +faker==24.11.0; python_version >= '3.8' fred-epplib@ git+https://github.com/cisagov/epplib.git@d56d183f1664f34c40ca9716a3a9a345f0ef561c furl==2.1.3 future==1.0.0; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3' gevent==24.2.1; python_version >= '3.8' greenlet==3.0.3; python_version >= '3.7' -gunicorn==21.2.0; python_version >= '3.5' -idna==3.6; python_version >= '3.5' +gunicorn==22.0.0; python_version >= '3.7' +idna==3.7; python_version >= '3.5' jmespath==1.0.1; python_version >= '3.7' -lxml==5.1.0; python_version >= '3.6' -mako==1.3.2; python_version >= '3.8' +lxml==5.2.1; python_version >= '3.6' +mako==1.3.3; python_version >= '3.8' markupsafe==2.1.5; python_version >= '3.7' marshmallow==3.21.1; python_version >= '3.8' oic==1.6.1; python_version ~= '3.7' orderedmultidict==1.0.1 packaging==24.0; python_version >= '3.7' -phonenumberslite==8.13.33 +phonenumberslite==8.13.35 psycopg2-binary==2.9.9; python_version >= '3.7' -pycparser==2.21 +pycparser==2.22; python_version >= '3.8' pycryptodomex==3.20.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' -pydantic==2.6.4; python_version >= '3.8' -pydantic-core==2.16.3; python_version >= '3.8' +pydantic==2.7.0; python_version >= '3.8' +pydantic-core==2.18.1; python_version >= '3.8' pydantic-settings==2.2.1; python_version >= '3.8' pyjwkest==1.4.2 python-dateutil==2.9.0.post0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' @@ -53,12 +53,12 @@ python-dotenv==1.0.1; python_version >= '3.8' pyzipper==0.3.6; python_version >= '3.4' requests==2.31.0; python_version >= '3.7' s3transfer==0.10.1; python_version >= '3.8' -setuptools==69.2.0; python_version >= '3.8' +setuptools==69.5.1; python_version >= '3.8' six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' -sqlparse==0.4.4; python_version >= '3.5' +sqlparse==0.5.0; python_version >= '3.8' tblib==3.0.0; python_version >= '3.8' -typing-extensions==4.10.0; python_version >= '3.8' +typing-extensions==4.11.0; python_version >= '3.8' urllib3==2.2.1; python_version >= '3.8' whitenoise==6.6.0; python_version >= '3.8' zope.event==5.0; python_version >= '3.7' -zope.interface==6.2; python_version >= '3.7' +zope.interface==6.3; python_version >= '3.7' From 880379f7caf7dea0deb99e07e028523705a691e9 Mon Sep 17 00:00:00 2001 From: Alysia Broddrick Date: Sat, 20 Apr 2024 19:38:01 -0700 Subject: [PATCH 42/85] updated dependency doc to remove reference to no-longer used library --- docs/operations/runbooks/update_python_dependencies.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/operations/runbooks/update_python_dependencies.md b/docs/operations/runbooks/update_python_dependencies.md index 468270d09..93f607445 100644 --- a/docs/operations/runbooks/update_python_dependencies.md +++ b/docs/operations/runbooks/update_python_dependencies.md @@ -13,9 +13,6 @@ It is necessary to use `bash -c` because `run pipenv requirements` will not recognize that it is running non-interactively and will include garbage formatting characters. The requirements.txt is used by Cloud.gov. It is needed to work around a bug in the CloudFoundry buildpack version of Pipenv that breaks on installing from a git repository. -4. Change geventconnpool back to what it was originally within the Pipfile.lock and requirements.txt. - This is done by either saving what it was originally or opening a PR and using that as a reference to undo changes to any mention of geventconnpool. - Geventconnpool, when set as a requirement without the reference portion, is defaulting to get a commit from 2014 which then breaks the code, as we want the newest version from them. -5. Run `docker-compose build` to build a new image for local development with the updated dependencies. +4. Run `docker-compose build` to build a new image for local development with the updated dependencies. The reason for de-coupling the `build` and `lock` steps is to increase consistency between builds--a run of `build` will always get exactly the dependencies listed in `Pipfile.lock`, nothing more, nothing less. \ No newline at end of file From eee745b0ca4a8b0faf272faecf60397e414df3d1 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 08:08:10 -0600 Subject: [PATCH 43/85] Fix logic - WIP --- ...ion_cisa_representative_email_and_more.py} | 12 +- src/registrar/models/domain_request.py | 10 ++ src/registrar/tests/common.py | 2 - src/registrar/tests/test_views_request.py | 144 +++++++++++++++++- 4 files changed, 154 insertions(+), 14 deletions(-) rename src/registrar/migrations/{0085_domaininformation_cisa_representative_email_and_more.py => 0087_domaininformation_cisa_representative_email_and_more.py} (77%) diff --git a/src/registrar/migrations/0085_domaininformation_cisa_representative_email_and_more.py b/src/registrar/migrations/0087_domaininformation_cisa_representative_email_and_more.py similarity index 77% rename from src/registrar/migrations/0085_domaininformation_cisa_representative_email_and_more.py rename to src/registrar/migrations/0087_domaininformation_cisa_representative_email_and_more.py index bc146aef0..1e53867d8 100644 --- a/src/registrar/migrations/0085_domaininformation_cisa_representative_email_and_more.py +++ b/src/registrar/migrations/0087_domaininformation_cisa_representative_email_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.10 on 2024-04-18 18:21 +# Generated by Django 4.2.10 on 2024-04-22 13:16 from django.db import migrations, models @@ -6,23 +6,19 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("registrar", "0084_create_groups_v11"), + ("registrar", "0086_domaininformation_updated_federal_agency_and_more"), ] operations = [ migrations.AddField( model_name="domaininformation", name="cisa_representative_email", - field=models.EmailField( - blank=True, db_index=True, max_length=320, null=True, verbose_name="CISA region representative" - ), + field=models.EmailField(blank=True, max_length=320, null=True, verbose_name="CISA region representative"), ), migrations.AddField( model_name="domainrequest", name="cisa_representative_email", - field=models.EmailField( - blank=True, db_index=True, max_length=320, null=True, verbose_name="CISA region representative" - ), + field=models.EmailField(blank=True, max_length=320, null=True, verbose_name="CISA region representative"), ), migrations.AddField( model_name="domainrequest", diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index fb7221c80..bfeb33c8e 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -745,12 +745,22 @@ class DomainRequest(TimeStampedModel): """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 != "" + # 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 ) + # 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 diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 82fa4c061..6dd88c1c1 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -856,8 +856,6 @@ def completed_domain_request( creator=user, status=status, investigator=investigator, - has_cisa_representative=False, - has_anything_else_text=False, ) if has_about_your_organization: domain_request_kwargs["about_your_organization"] = "e-Government" diff --git a/src/registrar/tests/test_views_request.py b/src/registrar/tests/test_views_request.py index 3d9dec37c..352b8d0de 100644 --- a/src/registrar/tests/test_views_request.py +++ b/src/registrar/tests/test_views_request.py @@ -743,7 +743,7 @@ class DomainRequestTests(TestWithUser, WebTest): # Check the anything else yes/no field self.assertEquals(additional_form["additional_details-has_anything_else_text"].value, None) - def test_yes_no_contact_form_inits_yes_for_domain_request_with_other_contacts(self): + def test_yes_no_form_inits_yes_for_domain_request_with_other_contacts(self): """On the Other Contacts page, the yes/no form gets initialized with YES selected if the domain request has other contacts""" # Domain Request has other contacts by default @@ -763,7 +763,7 @@ class DomainRequestTests(TestWithUser, WebTest): other_contacts_form = other_contacts_page.forms[0] self.assertEquals(other_contacts_form["other_contacts-has_other_contacts"].value, "True") - def test_yes_no_additional_form_inits_yes_for_cisa_representative_and_anything_else(self): + 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 anything_else""" @@ -789,11 +789,11 @@ class DomainRequestTests(TestWithUser, WebTest): # Check the cisa representative yes/no field yes_no_cisa = additional_details_form["additional_details-has_cisa_representative"].value - self.assertEquals(yes_no_cisa, True) + self.assertEquals(yes_no_cisa, "True") # Check the anything else yes/no field yes_no_anything_else = additional_details_form["additional_details-has_anything_else_text"].value - self.assertEquals(yes_no_anything_else, True) + self.assertEquals(yes_no_anything_else, "True") def test_yes_no_form_inits_no_for_domain_request_with_no_other_contacts_rationale(self): """On the Other Contacts page, the yes/no form gets initialized with NO selected if the @@ -816,6 +816,142 @@ class DomainRequestTests(TestWithUser, WebTest): other_contacts_form = other_contacts_page.forms[0] self.assertEquals(other_contacts_form["other_contacts-has_other_contacts"].value, "False") + + def test_yes_no_form_for_domain_request_with_no_cisa_representative_and_anything_else(self): + """On the Additional details page, the form preselects "no" when has_cisa_representative + and anything_else is no""" + + domain_request = completed_domain_request(user=self.user, has_anything_else=False) + + # Unlike the other contacts form, the no button is tracked with these boolean fields. + # This means that we should expect this to correlate with the no button. + domain_request.has_anything_else_text = False + domain_request.has_cisa_representative = False + domain_request.save() + + # prime the form by visiting /edit + self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk})) + # django-webtest does not handle cookie-based sessions well because it keeps + # resetting the session key on each new request, thus destroying the concept + # of a "session". We are going to do it manually, saving the session ID here + # and then setting the cookie on each request. + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + additional_details_page = self.app.get(reverse("domain-request:additional_details")) + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + additional_details_form = additional_details_page.forms[0] + + # Check the cisa representative yes/no field + yes_no_cisa = additional_details_form["additional_details-has_cisa_representative"].value + self.assertEquals(yes_no_cisa, "False") + + # Check the anything else yes/no field + yes_no_anything_else = additional_details_form["additional_details-has_anything_else_text"].value + self.assertEquals(yes_no_anything_else, "False") + + def test_submitting_additional_details_deletes_cisa_representative_and_anything_else(self): + """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_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_email, "fake@faketown.gov") + + # prime the form by visiting /edit + self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk})) + # django-webtest does not handle cookie-based sessions well because it keeps + # resetting the session key on each new request, thus destroying the concept + # of a "session". We are going to do it manually, saving the session ID here + # and then setting the cookie on each request. + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + additional_details_page = self.app.get(reverse("domain-request:additional_details")) + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + additional_details_form = additional_details_page.forms[0] + + # Check the cisa representative yes/no field + yes_no_cisa = additional_details_form["additional_details-has_cisa_representative"].value + self.assertEquals(yes_no_cisa, "True") + + # Check the anything else yes/no field + yes_no_anything_else = additional_details_form["additional_details-has_anything_else_text"].value + self.assertEquals(yes_no_anything_else, "True") + + # Set fields to false + additional_details_form["additional_details-has_cisa_representative"] = "False" + additional_details_form["additional_details-has_anything_else_text"] = "False" + + # Submit the form + additional_details_form.submit() + + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + # Verify that the anything_else and cisa_representative 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_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) + + def test_submitting_additional_details_populates_cisa_representative_and_anything_else(self): + """When a user submits the Additional Details form, + the domain request's data gets submitted""" + domain_request = completed_domain_request(name="cisareps.gov", user=self.user, has_anything_else=False) + + # Make sure we have the data we need for the test + self.assertEqual(domain_request.anything_else, None) + self.assertEqual(domain_request.cisa_representative_email, None) + self.assertEqual(domain_request.has_anything_else_text, False) + self.assertEqual(domain_request.has_cisa_representative, False) + + # prime the form by visiting /edit + self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk})) + # django-webtest does not handle cookie-based sessions well because it keeps + # resetting the session key on each new request, thus destroying the concept + # of a "session". We are going to do it manually, saving the session ID here + # and then setting the cookie on each request. + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + additional_details_page = self.app.get(reverse("domain-request:additional_details")) + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + additional_details_form = additional_details_page.forms[0] + + # Check the cisa representative yes/no field + yes_no_cisa = additional_details_form["additional_details-has_cisa_representative"].value + self.assertEquals(yes_no_cisa, "True") + + # Check the anything else yes/no field + yes_no_anything_else = additional_details_form["additional_details-has_anything_else_text"].value + self.assertEquals(yes_no_anything_else, "True") + + # Set fields to false + additional_details_form["additional_details-has_cisa_representative"] = "False" + additional_details_form["additional_details-has_anything_else_text"] = "False" + + # Submit the form + additional_details_form.submit() + + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + # Verify that the anything_else and cisa_representative exist in the db + domain_request = DomainRequest.objects.get(requested_domain__name="cisareps.gov") + + self.assertEqual(domain_request.anything_else, "There is more") + self.assertEqual(domain_request.cisa_representative_email, "fake@faketown.gov") + def test_submitting_other_contacts_deletes_no_other_contacts_rationale(self): """When a user submits the Other Contacts form with other contacts selected, the domain request's From 8ae6e3e30eabb9b7430d6aa3dcb37883dddccb86 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 08:27:32 -0600 Subject: [PATCH 44/85] Add additional unit tests --- src/registrar/tests/test_views_request.py | 123 ++++++++++++++++++---- 1 file changed, 105 insertions(+), 18 deletions(-) diff --git a/src/registrar/tests/test_views_request.py b/src/registrar/tests/test_views_request.py index 352b8d0de..16234490a 100644 --- a/src/registrar/tests/test_views_request.py +++ b/src/registrar/tests/test_views_request.py @@ -769,8 +769,8 @@ class DomainRequestTests(TestWithUser, WebTest): anything_else""" domain_request = completed_domain_request(user=self.user, has_anything_else=True) - domain_request.cisa_representative_email="test@igorville.gov" - domain_request.anything_else="1234" + domain_request.cisa_representative_email = "test@igorville.gov" + domain_request.anything_else = "1234" domain_request.save() # prime the form by visiting /edit @@ -816,7 +816,7 @@ class DomainRequestTests(TestWithUser, WebTest): other_contacts_form = other_contacts_page.forms[0] self.assertEquals(other_contacts_form["other_contacts-has_other_contacts"].value, "False") - + def test_yes_no_form_for_domain_request_with_no_cisa_representative_and_anything_else(self): """On the Additional details page, the form preselects "no" when has_cisa_representative and anything_else is no""" @@ -912,8 +912,10 @@ 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_email, None) - self.assertEqual(domain_request.has_anything_else_text, False) - self.assertEqual(domain_request.has_cisa_representative, False) + + # 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) + self.assertEqual(domain_request.has_cisa_representative, None) # prime the form by visiting /edit self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk})) @@ -929,17 +931,11 @@ class DomainRequestTests(TestWithUser, WebTest): additional_details_form = additional_details_page.forms[0] - # Check the cisa representative yes/no field - yes_no_cisa = additional_details_form["additional_details-has_cisa_representative"].value - self.assertEquals(yes_no_cisa, "True") - - # Check the anything else yes/no field - yes_no_anything_else = additional_details_form["additional_details-has_anything_else_text"].value - self.assertEquals(yes_no_anything_else, "True") - - # Set fields to false - additional_details_form["additional_details-has_cisa_representative"] = "False" - additional_details_form["additional_details-has_anything_else_text"] = "False" + # Set fields to true, and set data on those fields + additional_details_form["additional_details-has_cisa_representative"] = "True" + additional_details_form["additional_details-has_anything_else_text"] = "True" + additional_details_form["additional_details-cisa_representative_email"] = "test@faketest.gov" + additional_details_form["additional_details-anything_else"] = "redandblue" # Submit the form additional_details_form.submit() @@ -949,9 +945,100 @@ class DomainRequestTests(TestWithUser, WebTest): # Verify that the anything_else and cisa_representative exist in the db domain_request = DomainRequest.objects.get(requested_domain__name="cisareps.gov") - self.assertEqual(domain_request.anything_else, "There is more") - self.assertEqual(domain_request.cisa_representative_email, "fake@faketown.gov") + self.assertEqual(domain_request.anything_else, "redandblue") + 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) + + def test_if_cisa_representative_yes_no_form_is_yes_then_field_is_required(self): + """Applicants with a cisa representative must provide a value""" + domain_request = completed_domain_request(name="cisareps.gov", user=self.user, has_anything_else=False) + + # prime the form by visiting /edit + self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk})) + # django-webtest does not handle cookie-based sessions well because it keeps + # resetting the session key on each new request, thus destroying the concept + # of a "session". We are going to do it manually, saving the session ID here + # and then setting the cookie on each request. + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + additional_details_page = self.app.get(reverse("domain-request:additional_details")) + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + additional_details_form = additional_details_page.forms[0] + + # Set fields to true, and set data on those fields + additional_details_form["additional_details-has_cisa_representative"] = "True" + additional_details_form["additional_details-has_anything_else_text"] = "False" + + # Submit the form + response = additional_details_form.submit() + + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + self.assertContains(response, "Enter the email address of your CISA regional representative.") + + def test_if_anything_else_yes_no_form_is_yes_then_field_is_required(self): + """Applicants with a anything else must provide a value""" + domain_request = completed_domain_request(name="cisareps.gov", user=self.user, has_anything_else=False) + + # prime the form by visiting /edit + self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk})) + # django-webtest does not handle cookie-based sessions well because it keeps + # resetting the session key on each new request, thus destroying the concept + # of a "session". We are going to do it manually, saving the session ID here + # and then setting the cookie on each request. + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + additional_details_page = self.app.get(reverse("domain-request:additional_details")) + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + additional_details_form = additional_details_page.forms[0] + + # Set fields to true, and set data on those fields + additional_details_form["additional_details-has_cisa_representative"] = "False" + additional_details_form["additional_details-has_anything_else_text"] = "True" + + # Submit the form + response = additional_details_form.submit() + + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + expected_message = "Provide additional details you’d like us to know. If you have nothing to add, select “No.”" + self.assertContains(response, expected_message) + + def test_additional_details_form_fields_required(self): + """When a user submits the Additional Details form without checking the + has_cisa_representative and has_anything_else_text fields, the form should deny this action""" + domain_request = completed_domain_request(name="cisareps.gov", user=self.user, has_anything_else=False) + + self.assertEqual(domain_request.has_anything_else_text, None) + self.assertEqual(domain_request.has_additional_details, None) + + # prime the form by visiting /edit + self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk})) + # django-webtest does not handle cookie-based sessions well because it keeps + # resetting the session key on each new request, thus destroying the concept + # of a "session". We are going to do it manually, saving the session ID here + # and then setting the cookie on each request. + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + additional_details_page = self.app.get(reverse("domain-request:additional_details")) + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + additional_details_form = additional_details_page.forms[0] + + # Submit the form + response = additional_details_form.submit() + + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + # We expect to see this twice for both fields + self.assertContains(response, "This question is required.", count=2) def test_submitting_other_contacts_deletes_no_other_contacts_rationale(self): """When a user submits the Other Contacts form with other contacts selected, the domain request's From 438095e669348c242672bf193f8ba1f553c58a74 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 09:18:17 -0600 Subject: [PATCH 45/85] PR suggestions --- src/registrar/admin.py | 1 + src/registrar/assets/js/get-gov.js | 24 +++++---- .../templates/domain_request_review.html | 14 +++++- .../templates/domain_request_status.html | 13 ++++- .../includes/summary_additional_details.html | 50 ------------------- .../templates/includes/summary_item.html | 3 ++ 6 files changed, 43 insertions(+), 62 deletions(-) delete mode 100644 src/registrar/templates/includes/summary_additional_details.html diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 72ba29db4..cc19d4a10 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1285,6 +1285,7 @@ class DomainRequestAdmin(ListHeaderAdmin): "no_other_contacts_rationale", "anything_else", "is_policy_acknowledged", + "cisa_representative_email", ] autocomplete_fields = [ "approved_domain", diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js index 3c1b1099f..4cefd798b 100644 --- a/src/registrar/assets/js/get-gov.js +++ b/src/registrar/assets/js/get-gov.js @@ -213,17 +213,21 @@ function HookupYesNoListener(radioButtonName, elementIdToShowIfYes, elementIdToS // Check if the element exists before accessing its value let selectedValue = radioButtonChecked ? radioButtonChecked.value : null; - switch (selectedValue) { - case 'True': - toggleTwoDomElements(elementIdToShowIfYes, elementIdToShowIfNo, 1); - break; + if (elementIdToShowIfYes && elementIdToShowIfNo){ + switch (selectedValue) { + case 'True': + toggleTwoDomElements(elementIdToShowIfYes, elementIdToShowIfNo, 1); + break; - case 'False': - toggleTwoDomElements(elementIdToShowIfYes, elementIdToShowIfNo, 2); - break; + case 'False': + toggleTwoDomElements(elementIdToShowIfYes, elementIdToShowIfNo, 2); + break; - default: - toggleTwoDomElements(elementIdToShowIfYes, elementIdToShowIfNo, 0); + default: + toggleTwoDomElements(elementIdToShowIfYes, elementIdToShowIfNo, 0); + } + }else { + console.log("No elements to show") } } @@ -833,4 +837,4 @@ function hideDeletedForms() { */ (function cisaRepresentativesFormListener() { HookupYesNoListener("additional_details-has_cisa_representative",'cisa-representative', null) -})(); \ No newline at end of file +})(); diff --git a/src/registrar/templates/domain_request_review.html b/src/registrar/templates/domain_request_review.html index 569fd717c..ce061b321 100644 --- a/src/registrar/templates/domain_request_review.html +++ b/src/registrar/templates/domain_request_review.html @@ -156,7 +156,19 @@ {% if step == Step.ADDITIONAL_DETAILS %} - {% include "includes/summary_additional_details.html" with domainRequest=domain_request %} + {% 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 regions representative' value=domain_request.cisa_representative_email heading_level=heading_level editable=True edit_link=domain_request_url %} + {% endwith %} + +

Anything else

+
    + {% if domain_request.has_anything_else_text %} + {{domain_request.anything_else}} + {% else %} + No + {% endif %} +
{% endif %} diff --git a/src/registrar/templates/domain_request_status.html b/src/registrar/templates/domain_request_status.html index cf9e7ffe3..2be2cc44b 100644 --- a/src/registrar/templates/domain_request_status.html +++ b/src/registrar/templates/domain_request_status.html @@ -116,7 +116,18 @@ {% include "includes/summary_item.html" with title='Other employees from your organization' value=DomainRequest.no_other_contacts_rationale heading_level=heading_level %} {% endif %} - {% include "includes/summary_additional_details.html" with domainRequest=DomainRequest %} + {% if DomainRequest.has_cisa_representative or domain_request.has_anything_else_text %} + {% include "includes/summary_item.html" with title='Additional details' sub_header_text='CISA regions representative' value=domain_request.cisa_representative_email heading_level=heading_level %} + +

Anything else

+
    + {% if domain_request.has_anything_else_text %} + {{domain_request.anything_else}} + {% else %} + No + {% endif %} +
+ {% endif %} {% endwith %} diff --git a/src/registrar/templates/includes/summary_additional_details.html b/src/registrar/templates/includes/summary_additional_details.html deleted file mode 100644 index 233734e7d..000000000 --- a/src/registrar/templates/includes/summary_additional_details.html +++ /dev/null @@ -1,50 +0,0 @@ -{% load static url_helpers %} - - - -
- -
-
-

- Additional details -

- - {% if domainRequest is not none %} -
-
- CISA regions representative -
-
- {% if domainRequest.has_cisa_representative %} - {{domainRequest.cisa_representative_email}} - {% else %} - (none) - {% endif %} -
-
- Anything else -
-
- {% if domainRequest.has_anything_else_text %} - {{domainRequest.anything_else}} - {% else %} - No - {% endif %} -
-
- {% else %} - ERROR Please contact technical support/dev - {% endif %} -
-
-
diff --git a/src/registrar/templates/includes/summary_item.html b/src/registrar/templates/includes/summary_item.html index 7c0d801ad..0988068df 100644 --- a/src/registrar/templates/includes/summary_item.html +++ b/src/registrar/templates/includes/summary_item.html @@ -21,6 +21,9 @@ {% else %} {% endif %} + {% if sub_header_text %} +

{{ sub_header_text }}

+ {% endif %} {% if address %} {% include "includes/organization_address.html" with organization=value %} {% elif contact %} From 4edb6ea988fb40c3215098933b35a099d47b41c6 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 09:29:30 -0600 Subject: [PATCH 46/85] Fix test --- src/registrar/tests/test_admin.py | 1 + src/registrar/tests/test_views_request.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 61e2a255f..98e5b2818 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1923,6 +1923,7 @@ class TestDomainRequestAdmin(MockEppLib): "no_other_contacts_rationale", "anything_else", "is_policy_acknowledged", + "cisa_representative_email", ] self.assertEqual(readonly_fields, expected_fields) diff --git a/src/registrar/tests/test_views_request.py b/src/registrar/tests/test_views_request.py index 16234490a..460900e28 100644 --- a/src/registrar/tests/test_views_request.py +++ b/src/registrar/tests/test_views_request.py @@ -1016,7 +1016,7 @@ class DomainRequestTests(TestWithUser, WebTest): domain_request = completed_domain_request(name="cisareps.gov", user=self.user, has_anything_else=False) self.assertEqual(domain_request.has_anything_else_text, None) - self.assertEqual(domain_request.has_additional_details, None) + self.assertEqual(domain_request.has_cisa_representative, None) # prime the form by visiting /edit self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk})) From e581d3b2caf8278bed441402d0b07689871c08e1 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 09:55:05 -0600 Subject: [PATCH 47/85] Fix js, linting --- src/registrar/assets/js/get-gov.js | 22 +++++++++------------- src/registrar/models/domain_request.py | 9 ++++++++- src/registrar/tests/test_views_request.py | 5 +++-- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js index 4cefd798b..e7260ee21 100644 --- a/src/registrar/assets/js/get-gov.js +++ b/src/registrar/assets/js/get-gov.js @@ -213,21 +213,17 @@ function HookupYesNoListener(radioButtonName, elementIdToShowIfYes, elementIdToS // Check if the element exists before accessing its value let selectedValue = radioButtonChecked ? radioButtonChecked.value : null; - if (elementIdToShowIfYes && elementIdToShowIfNo){ - switch (selectedValue) { - case 'True': - toggleTwoDomElements(elementIdToShowIfYes, elementIdToShowIfNo, 1); - break; + switch (selectedValue) { + case 'True': + toggleTwoDomElements(elementIdToShowIfYes, elementIdToShowIfNo, 1); + break; - case 'False': - toggleTwoDomElements(elementIdToShowIfYes, elementIdToShowIfNo, 2); - break; + case 'False': + toggleTwoDomElements(elementIdToShowIfYes, elementIdToShowIfNo, 2); + break; - default: - toggleTwoDomElements(elementIdToShowIfYes, elementIdToShowIfNo, 0); - } - }else { - console.log("No elements to show") + default: + toggleTwoDomElements(elementIdToShowIfYes, elementIdToShowIfNo, 0); } } diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index bfeb33c8e..02d07c9b2 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -1103,7 +1103,14 @@ class DomainRequest(TimeStampedModel): return self.other_contacts.exists() def has_additional_details(self) -> bool: - return self.has_anything_else_text() or self.has_cisa_representative + """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 + + return has_details def is_federal(self) -> Union[bool, None]: """Is this domain request for a federal agency? diff --git a/src/registrar/tests/test_views_request.py b/src/registrar/tests/test_views_request.py index 460900e28..19be5ce74 100644 --- a/src/registrar/tests/test_views_request.py +++ b/src/registrar/tests/test_views_request.py @@ -1037,8 +1037,9 @@ class DomainRequestTests(TestWithUser, WebTest): self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - # We expect to see this twice for both fields - self.assertContains(response, "This question is required.", count=2) + # We expect to see this twice for both fields. This results in a count of 4 + # due to screen reader information / html. + self.assertContains(response, "This question is required.", count=4) def test_submitting_other_contacts_deletes_no_other_contacts_rationale(self): """When a user submits the Other Contacts form with other contacts selected, the domain request's From c9f511e39713eed59294a70e5fa8dd18c27f4b2a Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 10:08:16 -0600 Subject: [PATCH 48/85] Add type ignore Overzealous linter --- src/registrar/forms/domain_request_wizard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py index 752ad7f35..8d74f6f35 100644 --- a/src/registrar/forms/domain_request_wizard.py +++ b/src/registrar/forms/domain_request_wizard.py @@ -664,7 +664,7 @@ class CisaRepresentativeForm(BaseDeletableRegistrarForm): class CisaRepresentativeYesNoForm(BaseYesNoForm): """Yes/no toggle for the CISA regions question on additional details""" - form_is_checked = property(lambda self: self.domain_request.has_cisa_representative) + form_is_checked = property(lambda self: self.domain_request.has_cisa_representative) # type: ignore field_name = "has_cisa_representative" From 31787007c9f40f9dd84b54f17e708038b8abb652 Mon Sep 17 00:00:00 2001 From: Kristina Yin Date: Mon, 22 Apr 2024 09:14:26 -0700 Subject: [PATCH 49/85] add Christina to fixtures list --- src/registrar/fixtures_users.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/registrar/fixtures_users.py b/src/registrar/fixtures_users.py index f03fb025d..c12e5c36d 100644 --- a/src/registrar/fixtures_users.py +++ b/src/registrar/fixtures_users.py @@ -92,6 +92,12 @@ class UserFixture: "last_name": "Chin", "email": "szu.chin@associates.cisa.dhs.gov", }, + { + "username": "1ca4fc9a-9358-4518-b4eb-cad7a96c05b8", + "first_name": "Christina", + "last_name": "Burnett", + "email": "christina.burnett@cisa.dhs.gov", + }, ] STAFF = [ From 97ccefba0d5679d4716619fdb1f671ccc0061a35 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 12:26:43 -0600 Subject: [PATCH 50/85] PR changes --- .../domain_request_additional_details.html | 2 +- .../templates/domain_request_review.html | 6 +++--- .../templates/domain_request_status.html | 20 +++++++++---------- .../templates/includes/summary_item.html | 6 ++++++ 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/registrar/templates/domain_request_additional_details.html b/src/registrar/templates/domain_request_additional_details.html index 44a725552..12c74aa47 100644 --- a/src/registrar/templates/domain_request_additional_details.html +++ b/src/registrar/templates/domain_request_additional_details.html @@ -14,7 +14,7 @@

Are you working with anyone from CISA regions on your domain request?

-

.gov is managed by the Cybersecurity and Infrastructure Security Agency. CISA has 10 regions that some organizations choose to work with. Regional representatives use titles like protective security advisors, cyber security advisors, or election security advisors.

+

.gov is managed by the Cybersecurity and Infrastructure Security Agency. CISA has 10 regions that some organizations choose to work with. Regional representatives use titles like protective security advisors, cyber security advisors, or election security advisors.

diff --git a/src/registrar/templates/domain_request_review.html b/src/registrar/templates/domain_request_review.html index ce061b321..7c4c293fc 100644 --- a/src/registrar/templates/domain_request_review.html +++ b/src/registrar/templates/domain_request_review.html @@ -158,15 +158,15 @@ {% 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 regions representative' value=domain_request.cisa_representative_email heading_level=heading_level editable=True edit_link=domain_request_url %} + {% include "includes/summary_item.html" with title=title sub_header_text='CISA regions representative' value=domain_request.cisa_representative_email heading_level=heading_level editable=True edit_link=domain_request_url custom_text_for_value_none='No' %} {% endwith %}

Anything else

    - {% if domain_request.has_anything_else_text %} + {% if domain_request.anything_else %} {{domain_request.anything_else}} {% else %} - No + No {% endif %}
{% endif %} diff --git a/src/registrar/templates/domain_request_status.html b/src/registrar/templates/domain_request_status.html index 2be2cc44b..1c7f74948 100644 --- a/src/registrar/templates/domain_request_status.html +++ b/src/registrar/templates/domain_request_status.html @@ -116,17 +116,17 @@ {% include "includes/summary_item.html" with title='Other employees from your organization' value=DomainRequest.no_other_contacts_rationale heading_level=heading_level %} {% endif %} - {% if DomainRequest.has_cisa_representative or domain_request.has_anything_else_text %} - {% include "includes/summary_item.html" with title='Additional details' sub_header_text='CISA regions representative' value=domain_request.cisa_representative_email heading_level=heading_level %} - -

Anything else

-
    - {% if domain_request.has_anything_else_text %} - {{domain_request.anything_else}} - {% else %} + {# We always show this field even if None #} + {% if DomainRequest %} + {% include "includes/summary_item.html" with title='Additional details' sub_header_text='CISA regions representative' value=DomainRequest.cisa_representative_email custom_text_for_value_none='No' heading_level=heading_level %} +

    Anything else

    +
      + {% if DomainRequest.anything_else %} + {{DomainRequest.anything_else}} + {% else %} No - {% endif %} -
    + {% endif %} +
{% endif %} {% endwith %} diff --git a/src/registrar/templates/includes/summary_item.html b/src/registrar/templates/includes/summary_item.html index 0988068df..a2f328e1f 100644 --- a/src/registrar/templates/includes/summary_item.html +++ b/src/registrar/templates/includes/summary_item.html @@ -42,6 +42,10 @@
{% endfor %}
+ {% elif custom_text_for_value_none %} +

+ {{ custom_text_for_value_none }} +

{% else %}

None @@ -95,6 +99,8 @@

{% if value %} {{ value }} + {% elif custom_text_for_value_none %} + {{ custom_text_for_value_none }} {% else %} None {% endif %} From 558a27d7d9ad02cad352b38c25607579c6814c8c Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 13:15:08 -0600 Subject: [PATCH 51/85] PR suggestions --- ..._domaininformation_cisa_representative_email_and_more.py | 6 +++--- src/registrar/models/domain_information.py | 2 +- src/registrar/models/domain_request.py | 2 +- .../templates/domain_request_additional_details.html | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/registrar/migrations/0087_domaininformation_cisa_representative_email_and_more.py b/src/registrar/migrations/0087_domaininformation_cisa_representative_email_and_more.py index 1e53867d8..db42348fe 100644 --- a/src/registrar/migrations/0087_domaininformation_cisa_representative_email_and_more.py +++ b/src/registrar/migrations/0087_domaininformation_cisa_representative_email_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.10 on 2024-04-22 13:16 +# Generated by Django 4.2.10 on 2024-04-22 19:12 from django.db import migrations, models @@ -13,12 +13,12 @@ class Migration(migrations.Migration): migrations.AddField( model_name="domaininformation", name="cisa_representative_email", - field=models.EmailField(blank=True, max_length=320, null=True, verbose_name="CISA region representative"), + field=models.EmailField(blank=True, max_length=320, null=True, verbose_name="CISA regional representative"), ), migrations.AddField( model_name="domainrequest", name="cisa_representative_email", - field=models.EmailField(blank=True, max_length=320, null=True, verbose_name="CISA region representative"), + field=models.EmailField(blank=True, max_length=320, null=True, verbose_name="CISA regional representative"), ), migrations.AddField( model_name="domainrequest", diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index d6fee3f79..63f99e0e4 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -232,7 +232,7 @@ class DomainInformation(TimeStampedModel): cisa_representative_email = models.EmailField( null=True, blank=True, - verbose_name="CISA region representative", + verbose_name="CISA regional representative", max_length=320, ) diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index 02d07c9b2..cf946d293 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -671,7 +671,7 @@ class DomainRequest(TimeStampedModel): cisa_representative_email = models.EmailField( null=True, blank=True, - verbose_name="CISA region representative", + verbose_name="CISA regional representative", max_length=320, ) diff --git a/src/registrar/templates/domain_request_additional_details.html b/src/registrar/templates/domain_request_additional_details.html index 12c74aa47..50e9ce97f 100644 --- a/src/registrar/templates/domain_request_additional_details.html +++ b/src/registrar/templates/domain_request_additional_details.html @@ -2,7 +2,7 @@ {% load static field_helpers %} {% block form_instructions %} -

These questions are required (*).

+ These questions are required (*). {% endblock %} {% block form_required_fields_help_text %} @@ -14,7 +14,7 @@

Are you working with anyone from CISA regions on your domain request?

-

.gov is managed by the Cybersecurity and Infrastructure Security Agency. CISA has 10 regions that some organizations choose to work with. Regional representatives use titles like protective security advisors, cyber security advisors, or election security advisors.

+

.gov is managed by the Cybersecurity and Infrastructure Security Agency. CISA has 10 regions that some organizations choose to work with. Regional representatives use titles like protective security advisors, cyber security advisors, or election security advisors.

From f4e4739923991c537792968f1b31d2d475d80b4b Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 13:22:42 -0600 Subject: [PATCH 52/85] Change regional rep --- src/registrar/templates/domain_request_review.html | 2 +- src/registrar/templates/domain_request_status.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/templates/domain_request_review.html b/src/registrar/templates/domain_request_review.html index 7c4c293fc..5f359e95f 100644 --- a/src/registrar/templates/domain_request_review.html +++ b/src/registrar/templates/domain_request_review.html @@ -158,7 +158,7 @@ {% 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 regions representative' value=domain_request.cisa_representative_email heading_level=heading_level editable=True edit_link=domain_request_url custom_text_for_value_none='No' %} + {% 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' %} {% endwith %}

Anything else

diff --git a/src/registrar/templates/domain_request_status.html b/src/registrar/templates/domain_request_status.html index 1c7f74948..0ea16e3a3 100644 --- a/src/registrar/templates/domain_request_status.html +++ b/src/registrar/templates/domain_request_status.html @@ -118,7 +118,7 @@ {# We always show this field even if None #} {% if DomainRequest %} - {% include "includes/summary_item.html" with title='Additional details' sub_header_text='CISA regions representative' value=DomainRequest.cisa_representative_email custom_text_for_value_none='No' heading_level=heading_level %} + {% include "includes/summary_item.html" with title='Additional details' sub_header_text='CISA regional representative' value=DomainRequest.cisa_representative_email custom_text_for_value_none='No' heading_level=heading_level %}

Anything else

    {% if DomainRequest.anything_else %} From a7f0340c523ec402f5a6a392c1ed55f400751b79 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:41:04 -0600 Subject: [PATCH 53/85] Add some spacing --- src/registrar/models/domain.py | 1 - .../templates/django/admin/includes/detail_table_fieldset.html | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 89bf3b7d9..f0cfaa9bf 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -163,7 +163,6 @@ class Domain(TimeStampedModel, DomainHelper): def get_admin_help_text(cls, state): """Returns a help message for a desired state for /admin. If none is found, an empty string is returned""" admin_help_texts = { - # For now, unknown has the same message as DNS_NEEDED cls.UNKNOWN: ( "The creator of the associated domain request has not logged in to " "manage the domain since it was approved. " diff --git a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html index f346ee155..9199f84cc 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -67,7 +67,7 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% block after_help_text %} {% if field.field.name == "creator" %} -
    +
    {% include "django/admin/includes/contact_detail_list.html" with user=original.creator no_title_top_padding=field.is_readonly %}
    From 2e83dc38d2664fce4a8b7abd180ae67a1ab839e8 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 14:41:42 -0600 Subject: [PATCH 54/85] Change helper text --- src/registrar/templates/domain_request_additional_details.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/templates/domain_request_additional_details.html b/src/registrar/templates/domain_request_additional_details.html index 50e9ce97f..e13d3c7ee 100644 --- a/src/registrar/templates/domain_request_additional_details.html +++ b/src/registrar/templates/domain_request_additional_details.html @@ -13,7 +13,7 @@ {% block form_fields %}
    -

    Are you working with anyone from CISA regions on your domain request?

    +

    Are you working with a CISA regional representative on your domain request?

    .gov is managed by the Cybersecurity and Infrastructure Security Agency. CISA has 10 regions that some organizations choose to work with. Regional representatives use titles like protective security advisors, cyber security advisors, or election security advisors.

    From d82c21feb12d64dfa2f2c1f17592d494a5f183c8 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 15:15:02 -0600 Subject: [PATCH 55/85] Update migrations --- ..._alter_domain_expiration_date_and_more.py} | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) rename src/registrar/migrations/{0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py => 0087_alter_domain_deleted_alter_domain_expiration_date_and_more.py} (98%) diff --git a/src/registrar/migrations/0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py b/src/registrar/migrations/0087_alter_domain_deleted_alter_domain_expiration_date_and_more.py similarity index 98% rename from src/registrar/migrations/0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py rename to src/registrar/migrations/0087_alter_domain_deleted_alter_domain_expiration_date_and_more.py index 3590d3869..c3f6f7bff 100644 --- a/src/registrar/migrations/0085_alter_domain_deleted_alter_domain_expiration_date_and_more.py +++ b/src/registrar/migrations/0087_alter_domain_deleted_alter_domain_expiration_date_and_more.py @@ -1,17 +1,16 @@ -# Generated by Django 4.2.10 on 2024-04-16 16:17 +# Generated by Django 4.2.10 on 2024-04-22 21:14 from django.conf import settings import django.core.validators from django.db import migrations, models import django.db.models.deletion import django_fsm -import registrar.models.utility.domain_field class Migration(migrations.Migration): dependencies = [ - ("registrar", "0084_create_groups_v11"), + ("registrar", "0086_domaininformation_updated_federal_agency_and_more"), ] operations = [ @@ -19,7 +18,10 @@ class Migration(migrations.Migration): model_name="domain", name="deleted", field=models.DateField( - editable=False, help_text='Will appear blank unless the domain is in "deleted" state', null=True + editable=False, + help_text='Will appear blank unless the domain is in "deleted" state', + null=True, + verbose_name="deleted on", ), ), migrations.AlterField( @@ -34,13 +36,9 @@ class Migration(migrations.Migration): editable=False, help_text='Date when this domain first moved into "ready" state; date will never change', null=True, + verbose_name="first ready on", ), ), - migrations.AlterField( - model_name="domain", - name="name", - field=registrar.models.utility.domain_field.DomainField(default=None, max_length=253, unique=True), - ), migrations.AlterField( model_name="domain", name="state", @@ -56,6 +54,7 @@ class Migration(migrations.Migration): help_text=" ", max_length=21, protected=True, + verbose_name="domain state", ), ), migrations.AlterField( @@ -66,12 +65,12 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="domaininformation", name="address_line1", - field=models.CharField(blank=True, null=True, verbose_name="Street address"), + field=models.CharField(blank=True, null=True, verbose_name="address line 1"), ), migrations.AlterField( model_name="domaininformation", name="address_line2", - field=models.CharField(blank=True, null=True, verbose_name="Street address line 2 (optional)"), + field=models.CharField(blank=True, null=True, verbose_name="address line 2"), ), migrations.AlterField( model_name="domaininformation", @@ -528,7 +527,7 @@ class Migration(migrations.Migration): ], max_length=2, null=True, - verbose_name="State, territory, or military post", + verbose_name="state / territory", ), ), migrations.AlterField( @@ -552,16 +551,13 @@ class Migration(migrations.Migration): model_name="domaininformation", name="urbanization", field=models.CharField( - blank=True, - help_text="Required for Puerto Rico only", - null=True, - verbose_name="Urbanization (required for Puerto Rico only)", + blank=True, help_text="Required for Puerto Rico only", null=True, verbose_name="urbanization" ), ), migrations.AlterField( model_name="domaininformation", name="zipcode", - field=models.CharField(blank=True, db_index=True, max_length=10, null=True), + field=models.CharField(blank=True, db_index=True, max_length=10, null=True, verbose_name="zip code"), ), migrations.AlterField( model_name="domainrequest", @@ -963,7 +959,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="domainrequest", name="is_election_board", - field=models.BooleanField(blank=True, null=True), + field=models.BooleanField(blank=True, null=True, verbose_name="election office"), ), migrations.AlterField( model_name="domainrequest", @@ -1072,6 +1068,7 @@ class Migration(migrations.Migration): ], max_length=2, null=True, + verbose_name="state / territory", ), ), migrations.AlterField( @@ -1099,7 +1096,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="domainrequest", name="zipcode", - field=models.CharField(blank=True, db_index=True, max_length=10, null=True), + field=models.CharField(blank=True, db_index=True, max_length=10, null=True, verbose_name="zip code"), ), migrations.AlterField( model_name="host", @@ -1114,13 +1111,16 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="host", name="name", - field=models.CharField(default=None, max_length=253), + field=models.CharField(default=None, max_length=253, verbose_name="host name"), ), migrations.AlterField( model_name="hostip", name="address", field=models.CharField( - default=None, max_length=46, validators=[django.core.validators.validate_ipv46_address] + default=None, + max_length=46, + validators=[django.core.validators.validate_ipv46_address], + verbose_name="IP address", ), ), migrations.AlterField( @@ -1143,6 +1143,7 @@ class Migration(migrations.Migration): help_text='Users in "restricted" status cannot make updates in the registrar or start a new request.', max_length=10, null=True, + verbose_name="user status", ), ), migrations.AlterField( From 788a190a444cccfac7b6fb028215d50547ee469f Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 22 Apr 2024 15:32:27 -0600 Subject: [PATCH 56/85] linting --- src/registrar/tests/test_admin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index bc45f661e..19783b3f2 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -33,8 +33,6 @@ from registrar.models import ( PublicContact, Host, Website, - DraftDomain, - Host, ) from registrar.models.user_domain_role import UserDomainRole from registrar.models.verified_by_staff import VerifiedByStaff From 42d2eb3ccdc53a71c31091cdbc76c4c43b394357 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 23 Apr 2024 08:19:13 -0600 Subject: [PATCH 57/85] Fix field helper text --- src/registrar/models/domain_information.py | 3 +-- src/registrar/models/domain_request.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index bf964be61..31b0ca607 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -82,7 +82,7 @@ class DomainInformation(TimeStampedModel): choices=DomainRequest.OrgChoicesElectionOffice.choices, null=True, blank=True, - help_text="Type of organization - Election office", + help_text="\"Election\" appears after the org type if it's an election office.", ) federally_recognized_tribe = models.BooleanField( @@ -115,7 +115,6 @@ class DomainInformation(TimeStampedModel): null=True, blank=True, verbose_name="election office", - help_text="Is your organization an election office?", ) organization_name = models.CharField( diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index d9f411be7..f775e5812 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -496,7 +496,7 @@ class DomainRequest(TimeStampedModel): choices=OrgChoicesElectionOffice.choices, null=True, blank=True, - help_text="Type of organization - Election office", + help_text="\"Election\" appears after the org type if it's an election office.", ) federally_recognized_tribe = models.BooleanField( From e1c5375e483ebd153e120f8e666c191155ab0a5d Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 23 Apr 2024 09:00:18 -0600 Subject: [PATCH 58/85] Add margin bottom --- .../templates/django/admin/includes/domain_fieldset.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/templates/django/admin/includes/domain_fieldset.html b/src/registrar/templates/django/admin/includes/domain_fieldset.html index a2c03189a..5e9464236 100644 --- a/src/registrar/templates/django/admin/includes/domain_fieldset.html +++ b/src/registrar/templates/django/admin/includes/domain_fieldset.html @@ -3,7 +3,7 @@ {% block help_text %} -
    +
    {% if field.field.name == "state" %}
    {{ state_help_message }}
    {% else %} From fc05542da38fd3d03583314972c39e120e833925 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 23 Apr 2024 09:03:27 -0600 Subject: [PATCH 59/85] Update migrations --- ...d_alter_domain_expiration_date_and_more.py | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/src/registrar/migrations/0087_alter_domain_deleted_alter_domain_expiration_date_and_more.py b/src/registrar/migrations/0087_alter_domain_deleted_alter_domain_expiration_date_and_more.py index c3f6f7bff..edbadcb4e 100644 --- a/src/registrar/migrations/0087_alter_domain_deleted_alter_domain_expiration_date_and_more.py +++ b/src/registrar/migrations/0087_alter_domain_deleted_alter_domain_expiration_date_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.10 on 2024-04-22 21:14 +# Generated by Django 4.2.10 on 2024-04-23 15:03 from django.conf import settings import django.core.validators @@ -436,6 +436,11 @@ class Migration(migrations.Migration): name="federally_recognized_tribe", field=models.BooleanField(null=True), ), + migrations.AlterField( + model_name="domaininformation", + name="is_election_board", + field=models.BooleanField(blank=True, null=True, verbose_name="election office"), + ), migrations.AlterField( model_name="domaininformation", name="no_other_contacts_rationale", @@ -453,6 +458,31 @@ class Migration(migrations.Migration): name="organization_name", field=models.CharField(blank=True, db_index=True, null=True), ), + migrations.AlterField( + model_name="domaininformation", + 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='"Election" appears after the org type if it\'s an election office.', + max_length=255, + null=True, + ), + ), migrations.AlterField( model_name="domaininformation", name="state_recognized_tribe", @@ -978,6 +1008,31 @@ class Migration(migrations.Migration): name="organization_name", field=models.CharField(blank=True, db_index=True, null=True), ), + migrations.AlterField( + 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='"Election" appears after the org type if it\'s an election office.', + max_length=255, + null=True, + ), + ), migrations.AlterField( model_name="domainrequest", name="purpose", From 0e096d365909a80ce76a707a49b2d58963503c9e Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 23 Apr 2024 09:15:30 -0600 Subject: [PATCH 60/85] Update helper text --- .../django/admin/includes/detail_table_fieldset.html | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html index 8e8a24427..26baddff7 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -65,6 +65,13 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% endwith %} {% endblock field_readonly %} +{% block help_text %} +
    +
    {{ field.field.help_text|safe }}
    +
    +{% endblock help_text %} + + {% block after_help_text %} {% if field.field.name == "creator" %}
    From 7578534f592a42d21b44b41e21a851aead1b5e5b Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 23 Apr 2024 09:29:13 -0600 Subject: [PATCH 61/85] Temp override on unknown state test --- src/registrar/models/domain.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 7f53bb234..448630cc8 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -1779,7 +1779,8 @@ class Domain(TimeStampedModel, DomainHelper): self._update_hosts_and_contacts(cleaned, fetch_hosts, fetch_contacts) if self.state == self.State.UNKNOWN: - self._fix_unknown_state(cleaned) + # self._fix_unknown_state(cleaned) + pass if fetch_hosts: self._update_hosts_and_ips_in_db(cleaned) if fetch_contacts: From 104f94da0721f2f281134e03620cd1c0c1e3c2df Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 23 Apr 2024 09:38:57 -0600 Subject: [PATCH 62/85] Change context order --- src/registrar/admin.py | 3 ++- src/registrar/models/domain.py | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index cee922770..1c7ff4c87 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1750,9 +1750,10 @@ class DomainAdmin(ListHeaderAdmin): if domain is not None and hasattr(domain, "domain_info"): extra_context["original_object"] = domain.domain_info + extra_context["state_help_message"] = Domain.State.get_admin_help_text(domain.state) + # Pass in what the an extended expiration date would be for the expiration date modal extra_context = self._set_expiration_date_context(domain, extra_context) - extra_context["state_help_message"] = Domain.State.get_admin_help_text(domain.state) return super().changeform_view(request, object_id, form_url, extra_context) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 448630cc8..7f53bb234 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -1779,8 +1779,7 @@ class Domain(TimeStampedModel, DomainHelper): self._update_hosts_and_contacts(cleaned, fetch_hosts, fetch_contacts) if self.state == self.State.UNKNOWN: - # self._fix_unknown_state(cleaned) - pass + self._fix_unknown_state(cleaned) if fetch_hosts: self._update_hosts_and_ips_in_db(cleaned) if fetch_contacts: From cc294c4e7dfb80d61a9f1c94b4ae76a036b7f9f0 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 23 Apr 2024 09:59:54 -0600 Subject: [PATCH 63/85] Linting --- src/registrar/models/domain_information.py | 2 +- src/registrar/models/domain_request.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index 31b0ca607..238b658f8 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -82,7 +82,7 @@ class DomainInformation(TimeStampedModel): choices=DomainRequest.OrgChoicesElectionOffice.choices, null=True, blank=True, - help_text="\"Election\" appears after the org type if it's an election office.", + help_text='"Election" appears after the org type if it\'s an election office.', ) federally_recognized_tribe = models.BooleanField( diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index f775e5812..9ed35f489 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -496,7 +496,7 @@ class DomainRequest(TimeStampedModel): choices=OrgChoicesElectionOffice.choices, null=True, blank=True, - help_text="\"Election\" appears after the org type if it's an election office.", + help_text='"Election" appears after the org type if it\'s an election office.', ) federally_recognized_tribe = models.BooleanField( From e5b3a6adb4eb2609e2bdcf117d575d0d92360e9c Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 23 Apr 2024 10:41:00 -0600 Subject: [PATCH 64/85] Add loggers --- src/registrar/admin.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 1c7ff4c87..7605e0a07 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1750,10 +1750,12 @@ class DomainAdmin(ListHeaderAdmin): if domain is not None and hasattr(domain, "domain_info"): extra_context["original_object"] = domain.domain_info + logger.info(f"changeform_view() -> state is {domain.state}") extra_context["state_help_message"] = Domain.State.get_admin_help_text(domain.state) - + logger.info(f"changeform_view() -> state is now {domain.state}") # Pass in what the an extended expiration date would be for the expiration date modal - extra_context = self._set_expiration_date_context(domain, extra_context) + self._set_expiration_date_context(domain, extra_context) + logger.info(f"changeform_view() -> state is now actually {domain.state}") return super().changeform_view(request, object_id, form_url, extra_context) @@ -1770,8 +1772,6 @@ class DomainAdmin(ListHeaderAdmin): new_date = curr_exp_date + relativedelta(years=years_to_extend_by) extra_context["extended_expiration_date"] = new_date - return extra_context - def response_change(self, request, obj): # Create dictionary of action functions ACTION_FUNCTIONS = { From 8d43031d75519f467b3fa4a78f8099cf8b14d42b Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 23 Apr 2024 10:56:30 -0600 Subject: [PATCH 65/85] Hide password --- src/registrar/admin.py | 3 +-- src/registrar/models/domain.py | 9 +++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 7605e0a07..9891a7317 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -561,7 +561,7 @@ class MyUserAdmin(BaseUserAdmin): analyst_fieldsets = ( ( None, - {"fields": ("password", "status")}, + {"fields": ("status")}, ), ("Personal Info", {"fields": ("first_name", "last_name", "email")}), ( @@ -587,7 +587,6 @@ class MyUserAdmin(BaseUserAdmin): # NOT all fields are readonly for admin, otherwise we would have # set this at the permissions level. The exception is 'status' analyst_readonly_fields = [ - "password", "Personal Info", "first_name", "last_name", diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 7f53bb234..d786fd16a 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -278,7 +278,7 @@ class Domain(TimeStampedModel, DomainHelper): """Get or set the `ex_date` element from the registry. Additionally, _get_property updates the expiration date in the registrar""" try: - return self._get_property("ex_date") + return self._get_property("ex_date", False) except Exception as e: # exception raised during the save to registrar logger.error(f"error updating expiration date in registrar: {e}") @@ -1770,7 +1770,7 @@ class Domain(TimeStampedModel, DomainHelper): technical_contact = self.get_default_technical_contact() technical_contact.save() - def _fetch_cache(self, fetch_hosts=False, fetch_contacts=False): + def _fetch_cache(self, fetch_hosts=False, fetch_contacts=False, fix_unknown=True): """Contact registry for info about a domain.""" try: data_response = self._get_or_create_domain() @@ -1778,7 +1778,7 @@ class Domain(TimeStampedModel, DomainHelper): cleaned = self._clean_cache(cache, data_response) self._update_hosts_and_contacts(cleaned, fetch_hosts, fetch_contacts) - if self.state == self.State.UNKNOWN: + if self.state == self.State.UNKNOWN and fix_unknown: self._fix_unknown_state(cleaned) if fetch_hosts: self._update_hosts_and_ips_in_db(cleaned) @@ -2008,12 +2008,13 @@ class Domain(TimeStampedModel, DomainHelper): """Remove cache data when updates are made.""" self._cache = {} - def _get_property(self, property): + def _get_property(self, property, fix_unknown=True): """Get some piece of info about a domain.""" if property not in self._cache: self._fetch_cache( fetch_hosts=(property == "hosts"), fetch_contacts=(property == "contacts"), + fix_unknown=fix_unknown, ) if property in self._cache: From 9583dbca948a54cf372ffd53c0de1d3a5ef0040d Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 23 Apr 2024 11:19:30 -0600 Subject: [PATCH 66/85] Test --- src/registrar/admin.py | 5 ++--- src/registrar/models/domain.py | 9 ++++----- .../templates/django/admin/includes/domain_fieldset.html | 6 ++++++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 9891a7317..acfbd77f7 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1749,12 +1749,11 @@ class DomainAdmin(ListHeaderAdmin): if domain is not None and hasattr(domain, "domain_info"): extra_context["original_object"] = domain.domain_info - logger.info(f"changeform_view() -> state is {domain.state}") extra_context["state_help_message"] = Domain.State.get_admin_help_text(domain.state) - logger.info(f"changeform_view() -> state is now {domain.state}") + extra_context["domain_state"] = domain.state + # Pass in what the an extended expiration date would be for the expiration date modal self._set_expiration_date_context(domain, extra_context) - logger.info(f"changeform_view() -> state is now actually {domain.state}") return super().changeform_view(request, object_id, form_url, extra_context) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index d786fd16a..7f53bb234 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -278,7 +278,7 @@ class Domain(TimeStampedModel, DomainHelper): """Get or set the `ex_date` element from the registry. Additionally, _get_property updates the expiration date in the registrar""" try: - return self._get_property("ex_date", False) + return self._get_property("ex_date") except Exception as e: # exception raised during the save to registrar logger.error(f"error updating expiration date in registrar: {e}") @@ -1770,7 +1770,7 @@ class Domain(TimeStampedModel, DomainHelper): technical_contact = self.get_default_technical_contact() technical_contact.save() - def _fetch_cache(self, fetch_hosts=False, fetch_contacts=False, fix_unknown=True): + def _fetch_cache(self, fetch_hosts=False, fetch_contacts=False): """Contact registry for info about a domain.""" try: data_response = self._get_or_create_domain() @@ -1778,7 +1778,7 @@ class Domain(TimeStampedModel, DomainHelper): cleaned = self._clean_cache(cache, data_response) self._update_hosts_and_contacts(cleaned, fetch_hosts, fetch_contacts) - if self.state == self.State.UNKNOWN and fix_unknown: + if self.state == self.State.UNKNOWN: self._fix_unknown_state(cleaned) if fetch_hosts: self._update_hosts_and_ips_in_db(cleaned) @@ -2008,13 +2008,12 @@ class Domain(TimeStampedModel, DomainHelper): """Remove cache data when updates are made.""" self._cache = {} - def _get_property(self, property, fix_unknown=True): + def _get_property(self, property): """Get some piece of info about a domain.""" if property not in self._cache: self._fetch_cache( fetch_hosts=(property == "hosts"), fetch_contacts=(property == "contacts"), - fix_unknown=fix_unknown, ) if property in self._cache: diff --git a/src/registrar/templates/django/admin/includes/domain_fieldset.html b/src/registrar/templates/django/admin/includes/domain_fieldset.html index 5e9464236..b191e17e7 100644 --- a/src/registrar/templates/django/admin/includes/domain_fieldset.html +++ b/src/registrar/templates/django/admin/includes/domain_fieldset.html @@ -2,6 +2,12 @@ {% load static url_helpers %} +{% block field_readonly %} + {% if field.field.name == "state" %} +
    {{ domain_state }}
    + {% endif %} +{% endblock %} + {% block help_text %}
    {% if field.field.name == "state" %} From e181d6838ea32918c2f0c8b0789e8b2a0c8ce0f0 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 23 Apr 2024 11:34:42 -0600 Subject: [PATCH 67/85] add state display --- src/registrar/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index acfbd77f7..ef65b37b1 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1750,7 +1750,7 @@ class DomainAdmin(ListHeaderAdmin): extra_context["original_object"] = domain.domain_info extra_context["state_help_message"] = Domain.State.get_admin_help_text(domain.state) - extra_context["domain_state"] = domain.state + extra_context["domain_state"] = domain.get_state_display() # Pass in what the an extended expiration date would be for the expiration date modal self._set_expiration_date_context(domain, extra_context) From 5a18f8c1f2b9d0dfb3d6a5dd18786b3e538a1a68 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 23 Apr 2024 11:54:16 -0600 Subject: [PATCH 68/85] Bug fix --- .../templates/django/admin/includes/domain_fieldset.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/registrar/templates/django/admin/includes/domain_fieldset.html b/src/registrar/templates/django/admin/includes/domain_fieldset.html index b191e17e7..d5f5bc1af 100644 --- a/src/registrar/templates/django/admin/includes/domain_fieldset.html +++ b/src/registrar/templates/django/admin/includes/domain_fieldset.html @@ -5,6 +5,8 @@ {% block field_readonly %} {% if field.field.name == "state" %}
    {{ domain_state }}
    + {% else %} +
    {{ field.contents }}
    {% endif %} {% endblock %} From d4ebab0f8f65cc334438aa4e3a988753061f5fde Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 23 Apr 2024 12:18:49 -0600 Subject: [PATCH 69/85] Fix unit test --- src/registrar/tests/test_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 19783b3f2..f2fdd2f1c 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -3039,7 +3039,7 @@ class TestMyUserAdmin(TestCase): request.user = create_user() fieldsets = self.admin.get_fieldsets(request) expected_fieldsets = ( - (None, {"fields": ("password", "status")}), + (None, {"fields": ("status")}), ("Personal Info", {"fields": ("first_name", "last_name", "email")}), ("Permissions", {"fields": ("is_active", "groups")}), ("Important dates", {"fields": ("last_login", "date_joined")}), From fe75724073cce60d589f8916cc5fe98ab234b327 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 23 Apr 2024 12:30:21 -0600 Subject: [PATCH 70/85] Add comma --- src/registrar/admin.py | 2 +- src/registrar/tests/test_admin.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index ef65b37b1..42d73f10d 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -561,7 +561,7 @@ class MyUserAdmin(BaseUserAdmin): analyst_fieldsets = ( ( None, - {"fields": ("status")}, + {"fields": ("status",)}, ), ("Personal Info", {"fields": ("first_name", "last_name", "email")}), ( diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index f2fdd2f1c..42baae6ef 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -3039,7 +3039,7 @@ class TestMyUserAdmin(TestCase): request.user = create_user() fieldsets = self.admin.get_fieldsets(request) expected_fieldsets = ( - (None, {"fields": ("status")}), + (None, {"fields": ("status",)}), ("Personal Info", {"fields": ("first_name", "last_name", "email")}), ("Permissions", {"fields": ("is_active", "groups")}), ("Important dates", {"fields": ("last_login", "date_joined")}), From b27fce713b06d69161357991482ba75bed19b648 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Wed, 24 Apr 2024 12:01:06 -0400 Subject: [PATCH 71/85] Make collapsible fieldsets toggle accessible --- src/registrar/admin.py | 20 +++++++++++------- src/registrar/assets/sass/_theme/_admin.scss | 18 +++++++++++----- src/registrar/templates/admin/base_site.html | 1 + src/registrar/templates/admin/fieldset.html | 18 ++++++++++++++-- src/registrar/tests/test_admin.py | 22 ++++++++++++++++++++ 5 files changed, 64 insertions(+), 15 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 42d73f10d..1e232d217 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1001,9 +1001,10 @@ class DomainInformationAdmin(ListHeaderAdmin): }, ), ( - "More details", + "Show details", { - "classes": ["collapse"], + "classes": ["collapse--dotgov"], + "description": "Extends type of organization", "fields": [ "federal_type", # "updated_federal_agency", @@ -1026,9 +1027,10 @@ class DomainInformationAdmin(ListHeaderAdmin): }, ), ( - "More details", + "Show details", { - "classes": ["collapse"], + "classes": ["collapse--dotgov"], + "description": "Extends organization name and mailing address", "fields": [ "address_line1", "address_line2", @@ -1242,9 +1244,10 @@ class DomainRequestAdmin(ListHeaderAdmin): }, ), ( - "More details", + "Show details", { - "classes": ["collapse"], + "classes": ["collapse--dotgov"], + "description": "Extends type of organization", "fields": [ "federal_type", # "updated_federal_agency", @@ -1267,9 +1270,10 @@ class DomainRequestAdmin(ListHeaderAdmin): }, ), ( - "More details", + "Show details", { - "classes": ["collapse"], + "classes": ["collapse--dotgov"], + "description": "Extends organization name and mailing address", "fields": [ "address_line1", "address_line2", diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss index f5717d067..7684b78a1 100644 --- a/src/registrar/assets/sass/_theme/_admin.scss +++ b/src/registrar/assets/sass/_theme/_admin.scss @@ -525,17 +525,25 @@ address.dja-address-contact-list { } // Collapse button styles for fieldsets -.module.collapse { +.module.collapse--dotgov { margin-top: -35px; padding-top: 0; border: none; - h2 { + button { background: none; - color: var(--body-fg)!important; text-transform: none; - } - a { color: var(--link-fg); + margin-top: 8px; + margin-left: 10px; + span { + text-decoration: underline; + } + } +} +.collapse--dotgov.collapsed .collapse-toggle--dotgov { + display: inline-block!important; + * { + display: inline-block; } } diff --git a/src/registrar/templates/admin/base_site.html b/src/registrar/templates/admin/base_site.html index 58843421a..dd680cec5 100644 --- a/src/registrar/templates/admin/base_site.html +++ b/src/registrar/templates/admin/base_site.html @@ -23,6 +23,7 @@ + {% endblock %} {% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %} diff --git a/src/registrar/templates/admin/fieldset.html b/src/registrar/templates/admin/fieldset.html index 8b8972e08..98860f264 100644 --- a/src/registrar/templates/admin/fieldset.html +++ b/src/registrar/templates/admin/fieldset.html @@ -6,9 +6,23 @@ It is not inherently customizable on its own, so we can modify this instead. https://github.com/django/django/blob/main/django/contrib/admin/templates/admin/includes/fieldset.html {% endcomment %}
    - {% if fieldset.name %}

    {{ fieldset.name }}

    {% endif %} + {% if fieldset.name %} + {# Customize the markup for the collapse toggle #} + {% if 'collapse--dotgov' in fieldset.classes %} + + {{ fieldset.description }} + {% else %} +

    {{ fieldset.name }}

    + {% endif %} + {% endif %} - {% if fieldset.description %} + {# Customize the markup for the collapse toggle: Do not show a description for the collapse fieldsets, instead we're using the description as a screen reader only legend #} + {% if fieldset.description and 'collapse--dotgov' not in fieldset.classes %}
    {{ fieldset.description|safe }}
    {% endif %} diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 42baae6ef..251a769c3 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -887,6 +887,28 @@ class TestDomainRequestAdmin(MockEppLib): ] self.test_helper.assert_response_contains_distinct_values(response, expected_values) + @less_console_noise_decorator + def test_collaspe_toggle_button_markup(self): + """ + Tests for the correct collapse toggle button markup + """ + + # Create a fake domain request and domain + domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW) + + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/domainrequest/{}/change/".format(domain_request.pk), + follow=True, + ) + + # Make sure the page loaded, and that we're on the right page + self.assertEqual(response.status_code, 200) + self.assertContains(response, domain_request.requested_domain.name) + + self.test_helper.assertContains(response, "Show details") + @less_console_noise_decorator def test_analyst_can_see_and_edit_alternative_domain(self): """Tests if an analyst can still see and edit the alternative domain field""" From 87f2dea92cb547f92fee768986b9d0b0c3df6953 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 24 Apr 2024 11:18:31 -0600 Subject: [PATCH 72/85] Fixed width bottom table --- src/registrar/assets/sass/_theme/_tables.scss | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/registrar/assets/sass/_theme/_tables.scss b/src/registrar/assets/sass/_theme/_tables.scss index 0d58b5878..3f6c56447 100644 --- a/src/registrar/assets/sass/_theme/_tables.scss +++ b/src/registrar/assets/sass/_theme/_tables.scss @@ -108,8 +108,24 @@ padding: units(2) units(2) units(2) 0; } - th:first-of-type { - padding-left: 0; + th:nth-of-type(1) { + width: 200px; + } + + th:nth-of-type(2) { + width: 158px; + } + + th:nth-of-type(3) { + width: 120px; + } + + th:nth-of-type(4) { + width: 95px; + } + + th:nth-of-type(5) { + width: 85px; } thead tr:first-child th:first-child { From fdadd68b969edd37cda9a3739a15a01688053d34 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 24 Apr 2024 11:26:43 -0600 Subject: [PATCH 73/85] More widths --- src/registrar/assets/sass/_theme/_tables.scss | 60 ++++++++++++------- src/registrar/templates/home.html | 4 +- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/src/registrar/assets/sass/_theme/_tables.scss b/src/registrar/assets/sass/_theme/_tables.scss index 3f6c56447..7214ffee0 100644 --- a/src/registrar/assets/sass/_theme/_tables.scss +++ b/src/registrar/assets/sass/_theme/_tables.scss @@ -108,28 +108,48 @@ padding: units(2) units(2) units(2) 0; } - th:nth-of-type(1) { - width: 200px; - } - - th:nth-of-type(2) { - width: 158px; - } - - th:nth-of-type(3) { - width: 120px; - } - - th:nth-of-type(4) { - width: 95px; - } - - th:nth-of-type(5) { - width: 85px; - } - thead tr:first-child th:first-child { border-top: none; } } } + +.dotgov-table__domain-requests { + th:nth-of-type(1) { + width: 200px; + } + + th:nth-of-type(2) { + width: 158px; + } + + th:nth-of-type(3) { + width: 120px; + } + + th:nth-of-type(4) { + width: 95px; + } + + th:nth-of-type(5) { + width: 85px; + } +} + +.dotgov-table__registered-domains { + th:nth-of-type(1) { + width: 200px; + } + + th:nth-of-type(2) { + width: 158px; + } + + th:nth-of-type(3) { + width: 215px; + } + + th:nth-of-type(4) { + width: 95px; + } +} diff --git a/src/registrar/templates/home.html b/src/registrar/templates/home.html index ea9276b9f..5c1bc893a 100644 --- a/src/registrar/templates/home.html +++ b/src/registrar/templates/home.html @@ -26,7 +26,7 @@

    Domains

    {% if domains %} - +
    @@ -104,7 +104,7 @@

    Domain requests

    {% if domain_requests %} -
    Your registered domains
    +
    From f589dfaa8e4dd8023bf44b9594584cbef7de601d Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 24 Apr 2024 11:59:42 -0600 Subject: [PATCH 74/85] max width --- src/registrar/assets/sass/_theme/_tables.scss | 61 ++++++++++--------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/src/registrar/assets/sass/_theme/_tables.scss b/src/registrar/assets/sass/_theme/_tables.scss index 7214ffee0..37536d2be 100644 --- a/src/registrar/assets/sass/_theme/_tables.scss +++ b/src/registrar/assets/sass/_theme/_tables.scss @@ -113,43 +113,46 @@ } } } +@media (max-width: 1040px){ + .dotgov-table__domain-requests { + th:nth-of-type(1) { + width: 200px; + } -.dotgov-table__domain-requests { - th:nth-of-type(1) { - width: 200px; - } + th:nth-of-type(2) { + width: 158px; + } - th:nth-of-type(2) { - width: 158px; - } + th:nth-of-type(3) { + width: 120px; + } - th:nth-of-type(3) { - width: 120px; - } + th:nth-of-type(4) { + width: 95px; + } - th:nth-of-type(4) { - width: 95px; - } - - th:nth-of-type(5) { - width: 85px; + th:nth-of-type(5) { + width: 85px; + } } } -.dotgov-table__registered-domains { - th:nth-of-type(1) { - width: 200px; - } +@media (max-width: 1040px){ + .dotgov-table__registered-domains { + th:nth-of-type(1) { + width: 200px; + } - th:nth-of-type(2) { - width: 158px; - } + th:nth-of-type(2) { + width: 158px; + } - th:nth-of-type(3) { - width: 215px; - } + th:nth-of-type(3) { + width: 215px; + } - th:nth-of-type(4) { - width: 95px; + th:nth-of-type(4) { + width: 95px; + } } -} +} \ No newline at end of file From 94a9ab1d03116b3b8ce77794f694a468f40ac2aa Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 24 Apr 2024 12:13:22 -0600 Subject: [PATCH 75/85] Change max width to min width --- src/registrar/assets/sass/_theme/_tables.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/assets/sass/_theme/_tables.scss b/src/registrar/assets/sass/_theme/_tables.scss index 37536d2be..5dc69e149 100644 --- a/src/registrar/assets/sass/_theme/_tables.scss +++ b/src/registrar/assets/sass/_theme/_tables.scss @@ -113,7 +113,7 @@ } } } -@media (max-width: 1040px){ +@media (min-width: 1040px){ .dotgov-table__domain-requests { th:nth-of-type(1) { width: 200px; @@ -137,7 +137,7 @@ } } -@media (max-width: 1040px){ +@media (min-width: 1040px){ .dotgov-table__registered-domains { th:nth-of-type(1) { width: 200px; From 938147f8a912384fc9471af8a0ee44193ba335ae Mon Sep 17 00:00:00 2001 From: Kristina Yin Date: Wed, 24 Apr 2024 11:27:31 -0700 Subject: [PATCH 76/85] add sandbox ID for Christina --- src/registrar/fixtures_users.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/fixtures_users.py b/src/registrar/fixtures_users.py index c12e5c36d..4bc9caf30 100644 --- a/src/registrar/fixtures_users.py +++ b/src/registrar/fixtures_users.py @@ -93,7 +93,7 @@ class UserFixture: "email": "szu.chin@associates.cisa.dhs.gov", }, { - "username": "1ca4fc9a-9358-4518-b4eb-cad7a96c05b8", + "username": "66bb1a5a-a091-4d7f-a6cf-4d772b4711c7", "first_name": "Christina", "last_name": "Burnett", "email": "christina.burnett@cisa.dhs.gov", From 3ea97e44d292c6c331e1cad7f2c444fbedc53744 Mon Sep 17 00:00:00 2001 From: Kristina Yin Date: Wed, 24 Apr 2024 11:30:55 -0700 Subject: [PATCH 77/85] adding Christina's analyst account --- src/registrar/fixtures_users.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/registrar/fixtures_users.py b/src/registrar/fixtures_users.py index 4bc9caf30..b1369ee8a 100644 --- a/src/registrar/fixtures_users.py +++ b/src/registrar/fixtures_users.py @@ -168,6 +168,12 @@ class UserFixture: "last_name": "Chin-Analyst", "email": "szu.chin@ecstech.com", }, + { + "username": "22f88aa5-3b54-4b1f-9c57-201fb02ddba7", + "first_name": "Christina-Analyst", + "last_name": "Burnett-Analyst", + "email": "christina.burnett@gwe.cisa.dhs.gov", + }, ] def load_users(cls, users, group_name): From a8c795187315f811ce0cbe6e30a325ea5359548f Mon Sep 17 00:00:00 2001 From: Kristina Yin <140533113+kristinacyin@users.noreply.github.com> Date: Wed, 24 Apr 2024 11:41:20 -0700 Subject: [PATCH 78/85] Add missing bracket --- src/registrar/fixtures_users.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/fixtures_users.py b/src/registrar/fixtures_users.py index cc16f2145..44704f721 100644 --- a/src/registrar/fixtures_users.py +++ b/src/registrar/fixtures_users.py @@ -99,7 +99,7 @@ class UserFixture: "last_name": "Burnett", "email": "christina.burnett@cisa.dhs.gov", }, - + { "username": "012f844d-8a0f-4225-9d82-cbf87bff1d3e", "first_name": "Riley", "last_name": "Orr", From f7871c086c6ef95df2322314f94da8bb5630b516 Mon Sep 17 00:00:00 2001 From: Kristina Yin <140533113+kristinacyin@users.noreply.github.com> Date: Wed, 24 Apr 2024 11:56:48 -0700 Subject: [PATCH 79/85] Added missing bracket --- src/registrar/fixtures_users.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/registrar/fixtures_users.py b/src/registrar/fixtures_users.py index 44704f721..9137ba86b 100644 --- a/src/registrar/fixtures_users.py +++ b/src/registrar/fixtures_users.py @@ -181,6 +181,7 @@ class UserFixture: "last_name": "Burnett-Analyst", "email": "christina.burnett@gwe.cisa.dhs.gov", }, + { "username": "d9839768-0c17-4fa2-9c8e-36291eef5c11", "first_name": "Alex-Analyst", "last_name": "Mcelya-Analyst", From b249cd425f406604a14bdba32ad3142634ac60ce Mon Sep 17 00:00:00 2001 From: Kristina Yin <140533113+kristinacyin@users.noreply.github.com> Date: Wed, 24 Apr 2024 12:07:33 -0700 Subject: [PATCH 80/85] remove trailing whitespace --- src/registrar/fixtures_users.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/fixtures_users.py b/src/registrar/fixtures_users.py index 9137ba86b..d01cb48e5 100644 --- a/src/registrar/fixtures_users.py +++ b/src/registrar/fixtures_users.py @@ -98,7 +98,7 @@ class UserFixture: "first_name": "Christina", "last_name": "Burnett", "email": "christina.burnett@cisa.dhs.gov", - }, + }, { "username": "012f844d-8a0f-4225-9d82-cbf87bff1d3e", "first_name": "Riley", From 5e628c925145957a98d8ed15c6172c4eeefe37cc Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 25 Apr 2024 10:45:03 -0600 Subject: [PATCH 81/85] Fix migrations --- ...nformation_cisa_representative_email_and_more.py} | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) rename src/registrar/migrations/{0087_domaininformation_cisa_representative_email_and_more.py => 0088_domaininformation_cisa_representative_email_and_more.py} (76%) diff --git a/src/registrar/migrations/0087_domaininformation_cisa_representative_email_and_more.py b/src/registrar/migrations/0088_domaininformation_cisa_representative_email_and_more.py similarity index 76% rename from src/registrar/migrations/0087_domaininformation_cisa_representative_email_and_more.py rename to src/registrar/migrations/0088_domaininformation_cisa_representative_email_and_more.py index db42348fe..95450fb3d 100644 --- a/src/registrar/migrations/0087_domaininformation_cisa_representative_email_and_more.py +++ b/src/registrar/migrations/0088_domaininformation_cisa_representative_email_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.10 on 2024-04-22 19:12 +# Generated by Django 4.2.10 on 2024-04-25 16:44 from django.db import migrations, models @@ -6,7 +6,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("registrar", "0086_domaininformation_updated_federal_agency_and_more"), + ("registrar", "0087_alter_domain_deleted_alter_domain_expiration_date_and_more"), ] operations = [ @@ -37,15 +37,11 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="domaininformation", name="anything_else", - field=models.TextField( - blank=True, help_text="Anything else?", null=True, verbose_name="Additional details" - ), + field=models.TextField(blank=True, null=True, verbose_name="Additional details"), ), migrations.AlterField( model_name="domainrequest", name="anything_else", - field=models.TextField( - blank=True, help_text="Anything else?", null=True, verbose_name="Additional details" - ), + field=models.TextField(blank=True, null=True, verbose_name="Additional details"), ), ] From 8bfa8a4c57188089e9dd86ecd84b315bd8deb8d5 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 25 Apr 2024 11:11:28 -0600 Subject: [PATCH 82/85] Update docker-compose.yml --- src/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docker-compose.yml b/src/docker-compose.yml index c69c21192..31f65bcd0 100644 --- a/src/docker-compose.yml +++ b/src/docker-compose.yml @@ -108,7 +108,7 @@ services: - pa11y owasp: - image: owasp/zap2docker-stable + image: softwaresecurityproject/zap-bare command: zap-baseline.py -t http://app:8080 -c zap.conf -I -r zap_report.html volumes: - .:/zap/wrk/ From 4c93459a79c8a284b1c90f3b8775da265a2b4a70 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 25 Apr 2024 11:27:00 -0600 Subject: [PATCH 83/85] Change image url --- src/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docker-compose.yml b/src/docker-compose.yml index 31f65bcd0..1a9064ac8 100644 --- a/src/docker-compose.yml +++ b/src/docker-compose.yml @@ -108,7 +108,7 @@ services: - pa11y owasp: - image: softwaresecurityproject/zap-bare + image: ghcr.io/zaproxy/zaproxy:stable command: zap-baseline.py -t http://app:8080 -c zap.conf -I -r zap_report.html volumes: - .:/zap/wrk/ From 9d7a1db22659663e1c15be94532f1235ace5d740 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Thu, 25 Apr 2024 16:48:43 -0400 Subject: [PATCH 84/85] Design tweaks --- src/registrar/assets/sass/_theme/_admin.scss | 5 +++++ src/registrar/templates/admin/fieldset.html | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss index 7684b78a1..2dcbfbb06 100644 --- a/src/registrar/assets/sass/_theme/_admin.scss +++ b/src/registrar/assets/sass/_theme/_admin.scss @@ -537,6 +537,11 @@ address.dja-address-contact-list { margin-left: 10px; span { text-decoration: underline; + font-size: 13px; + font-feature-settings: "kern"; + font-kerning: normal; + line-height: 13px; + font-family: -apple-system, "system-ui", "Segoe UI", system-ui, Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; } } } diff --git a/src/registrar/templates/admin/fieldset.html b/src/registrar/templates/admin/fieldset.html index 98860f264..b58bd0241 100644 --- a/src/registrar/templates/admin/fieldset.html +++ b/src/registrar/templates/admin/fieldset.html @@ -9,7 +9,7 @@ https://github.com/django/django/blob/main/django/contrib/admin/templates/admin/ {% if fieldset.name %} {# Customize the markup for the collapse toggle #} {% if 'collapse--dotgov' in fieldset.classes %} -
    Your domain requests