From 88fb945573b2324b934c4fdaceb1e077052b0b7b Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 14:17:30 -0700 Subject: [PATCH 01/20] Change to sentence case --- src/registrar/models/transition_domain.py | 2 +- src/registrar/templates/application_status.html | 2 +- src/registrar/templates/domain_detail.html | 2 +- src/registrar/templates/home.html | 2 +- src/registrar/tests/test_models_domain.py | 2 +- src/registrar/tests/test_views.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/registrar/models/transition_domain.py b/src/registrar/models/transition_domain.py index 9e6d40cf1..4be1836e8 100644 --- a/src/registrar/models/transition_domain.py +++ b/src/registrar/models/transition_domain.py @@ -4,7 +4,7 @@ from .utility.time_stamped_model import TimeStampedModel class StatusChoices(models.TextChoices): READY = "ready", "Ready" - ON_HOLD = "on hold", "On Hold" + ON_HOLD = "on hold", "On hold" UNKNOWN = "unknown", "Unknown" diff --git a/src/registrar/templates/application_status.html b/src/registrar/templates/application_status.html index 79d0f7ff9..e766d406f 100644 --- a/src/registrar/templates/application_status.html +++ b/src/registrar/templates/application_status.html @@ -31,7 +31,7 @@ Status: {% if domainapplication.status == 'approved' %} Approved - {% elif domainapplication.status == 'in review' %} In Review + {% elif domainapplication.status == 'in review' %} In review {% elif domainapplication.status == 'rejected' %} Rejected {% elif domainapplication.status == 'submitted' %} Submitted {% elif domainapplication.status == 'ineligible' %} Ineligible diff --git a/src/registrar/templates/domain_detail.html b/src/registrar/templates/domain_detail.html index 81a350f82..470df7537 100644 --- a/src/registrar/templates/domain_detail.html +++ b/src/registrar/templates/domain_detail.html @@ -18,7 +18,7 @@ Status: {% if domain.state == domain.State.UNKNOWN or domain.state == domain.State.DNS_NEEDED%} - DNS Needed + DNS needed {% else %} {{ domain.state|title }} {% endif %} diff --git a/src/registrar/templates/home.html b/src/registrar/templates/home.html index 0bc792cef..8480c671b 100644 --- a/src/registrar/templates/home.html +++ b/src/registrar/templates/home.html @@ -53,7 +53,7 @@ {{ domain.created_time|date }} {% if domain.state == "unknown" or domain.state == "dns needed"%} - DNS Needed + DNS needed {% else %} {{ domain.state|title }} {% endif %} diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index 4a2023243..fac1c0fcd 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -1506,7 +1506,7 @@ class TestRegistrantNameservers(MockEppLib): ] def test_setting_not_allowed(self): - """Scenario: A domain state is not Ready or DNS Needed + """Scenario: A domain state is not Ready or DNS needed then setting nameservers is not allowed""" domain, _ = Domain.objects.get_or_create(name="onholdDomain.gov", state=Domain.State.ON_HOLD) with self.assertRaises(ActionNotAllowed): diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index 6599a2436..8ddfa8999 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -100,7 +100,7 @@ class LoggedInTests(TestWithUser): response = self.client.get("/") # count = 2 because it is also in screenreader content self.assertContains(response, "igorville.gov", count=2) - self.assertContains(response, "DNS Needed") + self.assertContains(response, "DNS needed") # clean up role.delete() From 2a22be413157c7218c70cce2af8866cb9ab75231 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 14:47:12 -0700 Subject: [PATCH 02/20] Change fixtures --- src/registrar/fixtures_applications.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/registrar/fixtures_applications.py b/src/registrar/fixtures_applications.py index aea153aef..679e5f7fe 100644 --- a/src/registrar/fixtures_applications.py +++ b/src/registrar/fixtures_applications.py @@ -50,15 +50,15 @@ class DomainApplicationFixture: DA = [ { "status": "started", - "organization_name": "Example - Finished but not Submitted", + "organization_name": "Example - Finished but not submitted", }, { "status": "submitted", - "organization_name": "Example - Submitted but pending Investigation", + "organization_name": "Example - Submitted but pending investigation", }, { "status": "in review", - "organization_name": "Example - In Investigation", + "organization_name": "Example - In investigation", }, { "status": "in review", @@ -70,7 +70,7 @@ class DomainApplicationFixture: }, { "status": "action needed", - "organization_name": "Example - Action Needed", + "organization_name": "Example - Action needed", }, { "status": "rejected", From cb2ea02d425242bed5bce0cf0fc964e3e86803ca Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 1 Dec 2023 14:57:36 -0700 Subject: [PATCH 03/20] Create 0049_alter_transitiondomain_status.py --- .../0049_alter_transitiondomain_status.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/registrar/migrations/0049_alter_transitiondomain_status.py diff --git a/src/registrar/migrations/0049_alter_transitiondomain_status.py b/src/registrar/migrations/0049_alter_transitiondomain_status.py new file mode 100644 index 000000000..0c358e78e --- /dev/null +++ b/src/registrar/migrations/0049_alter_transitiondomain_status.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.7 on 2023-12-01 21:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("registrar", "0048_alter_transitiondomain_status"), + ] + + operations = [ + migrations.AlterField( + model_name="transitiondomain", + name="status", + field=models.CharField( + blank=True, + choices=[("ready", "Ready"), ("on hold", "On hold"), ("unknown", "Unknown")], + default="ready", + help_text="domain status during the transfer", + max_length=255, + verbose_name="Status", + ), + ), + ] From 6fe9af18d971a0c16d348d56a49e15541ded2a15 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 13:10:36 -0700 Subject: [PATCH 04/20] Update settings.py --- src/registrar/config/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index 7f20c8129..b9172f72b 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -64,7 +64,7 @@ secret_registry_cert = b64decode(secret("REGISTRY_CERT", "")) secret_registry_key = b64decode(secret("REGISTRY_KEY", "")) secret_registry_key_passphrase = secret("REGISTRY_KEY_PASSPHRASE", "") secret_registry_hostname = secret("REGISTRY_HOSTNAME") - +# WILL REMOVE (TO PUSH TO SANDBOX) # region: Basic Django Config-----------------------------------------------### # Build paths inside the project like this: BASE_DIR / "subdir". From cdc4e366a072fb480d3d47c605fba45a5eb3dedf Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 13:13:58 -0700 Subject: [PATCH 05/20] Fix migrations --- .../0049_alter_domainapplication_current_websites_and_more.py | 2 +- ...ondomain_status.py => 0050_alter_transitiondomain_status.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/registrar/migrations/{0049_alter_transitiondomain_status.py => 0050_alter_transitiondomain_status.py} (100%) diff --git a/src/registrar/migrations/0049_alter_domainapplication_current_websites_and_more.py b/src/registrar/migrations/0049_alter_domainapplication_current_websites_and_more.py index 4341bdad6..42dc76d7e 100644 --- a/src/registrar/migrations/0049_alter_domainapplication_current_websites_and_more.py +++ b/src/registrar/migrations/0049_alter_domainapplication_current_websites_and_more.py @@ -5,7 +5,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("registrar", "0048_alter_transitiondomain_status"), + ("registrar", "0049_alter_domainapplication_current_websites_and_more"), ] operations = [ diff --git a/src/registrar/migrations/0049_alter_transitiondomain_status.py b/src/registrar/migrations/0050_alter_transitiondomain_status.py similarity index 100% rename from src/registrar/migrations/0049_alter_transitiondomain_status.py rename to src/registrar/migrations/0050_alter_transitiondomain_status.py From b58dc77788fe47e24c4bf06d1c7202e2f65e1390 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 13:17:20 -0700 Subject: [PATCH 06/20] Fix migrations (typo) --- .../0049_alter_domainapplication_current_websites_and_more.py | 2 +- src/registrar/migrations/0050_alter_transitiondomain_status.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/migrations/0049_alter_domainapplication_current_websites_and_more.py b/src/registrar/migrations/0049_alter_domainapplication_current_websites_and_more.py index 42dc76d7e..4341bdad6 100644 --- a/src/registrar/migrations/0049_alter_domainapplication_current_websites_and_more.py +++ b/src/registrar/migrations/0049_alter_domainapplication_current_websites_and_more.py @@ -5,7 +5,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("registrar", "0049_alter_domainapplication_current_websites_and_more"), + ("registrar", "0048_alter_transitiondomain_status"), ] operations = [ diff --git a/src/registrar/migrations/0050_alter_transitiondomain_status.py b/src/registrar/migrations/0050_alter_transitiondomain_status.py index 0c358e78e..72bebb7b5 100644 --- a/src/registrar/migrations/0050_alter_transitiondomain_status.py +++ b/src/registrar/migrations/0050_alter_transitiondomain_status.py @@ -5,7 +5,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("registrar", "0048_alter_transitiondomain_status"), + ("registrar", "0049_alter_domainapplication_current_websites_and_more"), ] operations = [ From 8c9068d3cc01fec297d832db239975e0c3dbb931 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 14:10:01 -0700 Subject: [PATCH 07/20] Fix title issue --- src/registrar/templates/home.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/templates/home.html b/src/registrar/templates/home.html index 8480c671b..f3089dfd0 100644 --- a/src/registrar/templates/home.html +++ b/src/registrar/templates/home.html @@ -111,7 +111,7 @@ {{ application.requested_domain.name|default:"New domain request" }} {{ application.created_at|date }} - {{ application.status|title }} + {{ application.status|capfirst }} {% if application.status == "started" or application.status == "action needed" or application.status == "withdrawn" %} From 2fbbb34fb2c990ab2c914e9e5eb72d436f9abdb8 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 14:33:09 -0700 Subject: [PATCH 08/20] Fix casing issue --- .../migrations/0051_alter_domain_state.py | 30 ++++++++++++++++++ .../0052_alter_domainapplication_status.py | 31 +++++++++++++++++++ src/registrar/models/domain.py | 4 +-- src/registrar/models/domain_application.py | 16 +++++----- 4 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 src/registrar/migrations/0051_alter_domain_state.py create mode 100644 src/registrar/migrations/0052_alter_domainapplication_status.py diff --git a/src/registrar/migrations/0051_alter_domain_state.py b/src/registrar/migrations/0051_alter_domain_state.py new file mode 100644 index 000000000..df1240d93 --- /dev/null +++ b/src/registrar/migrations/0051_alter_domain_state.py @@ -0,0 +1,30 @@ +# Generated by Django 4.2.7 on 2023-12-05 21:21 + +from django.db import migrations +import django_fsm + + +class Migration(migrations.Migration): + dependencies = [ + ("registrar", "0050_alter_transitiondomain_status"), + ] + + 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="Very basic info about the lifecycle of this domain object", + max_length=21, + protected=True, + ), + ), + ] diff --git a/src/registrar/migrations/0052_alter_domainapplication_status.py b/src/registrar/migrations/0052_alter_domainapplication_status.py new file mode 100644 index 000000000..5a274d731 --- /dev/null +++ b/src/registrar/migrations/0052_alter_domainapplication_status.py @@ -0,0 +1,31 @@ +# Generated by Django 4.2.7 on 2023-12-05 21:25 + +from django.db import migrations +import django_fsm + + +class Migration(migrations.Migration): + dependencies = [ + ("registrar", "0051_alter_domain_state"), + ] + + operations = [ + migrations.AlterField( + model_name="domainapplication", + name="status", + field=django_fsm.FSMField( + choices=[ + ("started", "Started"), + ("submitted", "Submitted"), + ("in review", "In review"), + ("action needed", "Action needed"), + ("approved", "Approved"), + ("withdrawn", "Withdrawn"), + ("rejected", "Rejected"), + ("ineligible", "Ineligible"), + ], + default="started", + max_length=50, + ), + ), + ] diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 94430fb36..16c918f84 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -126,13 +126,13 @@ class Domain(TimeStampedModel, DomainHelper): # The domain object exists in the registry # but nameservers don't exist for it yet - DNS_NEEDED = "dns needed" + DNS_NEEDED = "dns needed", "Dns needed" # Domain has had nameservers set, may or may not be active READY = "ready" # Registrar manually changed state to client hold - ON_HOLD = "on hold" + ON_HOLD = "on hold", "On hold" # previously existed but has been deleted from the registry DELETED = "deleted" diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index 9ab3908d4..b35ad28c8 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -29,14 +29,14 @@ class DomainApplication(TimeStampedModel): REJECTED = "rejected" INELIGIBLE = "ineligible" STATUS_CHOICES = [ - (STARTED, STARTED), - (SUBMITTED, SUBMITTED), - (IN_REVIEW, IN_REVIEW), - (ACTION_NEEDED, ACTION_NEEDED), - (APPROVED, APPROVED), - (WITHDRAWN, WITHDRAWN), - (REJECTED, REJECTED), - (INELIGIBLE, INELIGIBLE), + (STARTED, STARTED.capitalize()), + (SUBMITTED, SUBMITTED.capitalize()), + (IN_REVIEW, IN_REVIEW.capitalize()), + (ACTION_NEEDED, ACTION_NEEDED.capitalize()), + (APPROVED, APPROVED.capitalize()), + (WITHDRAWN, WITHDRAWN.capitalize()), + (REJECTED, REJECTED.capitalize()), + (INELIGIBLE, INELIGIBLE.capitalize()), ] class StateTerritoryChoices(models.TextChoices): From f32952f6be3743cd83b9c8414376032aca023dc0 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 14:59:25 -0700 Subject: [PATCH 09/20] Fix status dropdowns --- src/registrar/assets/js/get-gov-admin.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js index 53eeb22a3..bdbd89c23 100644 --- a/src/registrar/assets/js/get-gov-admin.js +++ b/src/registrar/assets/js/get-gov-admin.js @@ -63,6 +63,25 @@ function openInNewTab(el, removeAttribute = false){ checkToListThenInitWidget('id_alternative_domains_to', 0); })(); +/** An IIFE to capitalize statuses in the Domain Application status dropdown +*/ +(function (){ + function capitalizeFirstLetterInDropdownOptions(dropdown_id) { + // Grabs the status dropdown + var selectElement = document.getElementById(dropdown_id); + if (selectElement) { + var options = selectElement.options; + // Loop through each option, and convert to sentence case + for (var i = 0; i < options.length; i++) { + var option = options[i]; + option.text = option.text.charAt(0).toUpperCase() + option.text.slice(1).toLowerCase(); + } + } + } + + capitalizeFirstLetterInDropdownOptions('id_status'); +})(); + // Function to check for the existence of the "to" select list element in the DOM, and if and when found, // initialize the associated widget function checkToListThenInitWidget(toListId, attempts) { From 58bc602742f94915776d7ec113e566abccde3756 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 5 Dec 2023 15:07:46 -0700 Subject: [PATCH 10/20] Fix migration merge conflict --- .../0050_alter_transitiondomain_status.py | 24 -------- .../migrations/0051_alter_domain_state.py | 30 ---------- .../0052_alter_domainapplication_status.py | 31 ---------- ...alter_domainapplication_status_and_more.py | 60 +++++++++++++++++++ 4 files changed, 60 insertions(+), 85 deletions(-) delete mode 100644 src/registrar/migrations/0050_alter_transitiondomain_status.py delete mode 100644 src/registrar/migrations/0051_alter_domain_state.py delete mode 100644 src/registrar/migrations/0052_alter_domainapplication_status.py create mode 100644 src/registrar/migrations/0053_alter_domain_state_alter_domainapplication_status_and_more.py diff --git a/src/registrar/migrations/0050_alter_transitiondomain_status.py b/src/registrar/migrations/0050_alter_transitiondomain_status.py deleted file mode 100644 index 72bebb7b5..000000000 --- a/src/registrar/migrations/0050_alter_transitiondomain_status.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 4.2.7 on 2023-12-01 21:57 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("registrar", "0049_alter_domainapplication_current_websites_and_more"), - ] - - operations = [ - migrations.AlterField( - model_name="transitiondomain", - name="status", - field=models.CharField( - blank=True, - choices=[("ready", "Ready"), ("on hold", "On hold"), ("unknown", "Unknown")], - default="ready", - help_text="domain status during the transfer", - max_length=255, - verbose_name="Status", - ), - ), - ] diff --git a/src/registrar/migrations/0051_alter_domain_state.py b/src/registrar/migrations/0051_alter_domain_state.py deleted file mode 100644 index df1240d93..000000000 --- a/src/registrar/migrations/0051_alter_domain_state.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 4.2.7 on 2023-12-05 21:21 - -from django.db import migrations -import django_fsm - - -class Migration(migrations.Migration): - dependencies = [ - ("registrar", "0050_alter_transitiondomain_status"), - ] - - 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="Very basic info about the lifecycle of this domain object", - max_length=21, - protected=True, - ), - ), - ] diff --git a/src/registrar/migrations/0052_alter_domainapplication_status.py b/src/registrar/migrations/0052_alter_domainapplication_status.py deleted file mode 100644 index 5a274d731..000000000 --- a/src/registrar/migrations/0052_alter_domainapplication_status.py +++ /dev/null @@ -1,31 +0,0 @@ -# Generated by Django 4.2.7 on 2023-12-05 21:25 - -from django.db import migrations -import django_fsm - - -class Migration(migrations.Migration): - dependencies = [ - ("registrar", "0051_alter_domain_state"), - ] - - operations = [ - migrations.AlterField( - model_name="domainapplication", - name="status", - field=django_fsm.FSMField( - choices=[ - ("started", "Started"), - ("submitted", "Submitted"), - ("in review", "In review"), - ("action needed", "Action needed"), - ("approved", "Approved"), - ("withdrawn", "Withdrawn"), - ("rejected", "Rejected"), - ("ineligible", "Ineligible"), - ], - default="started", - max_length=50, - ), - ), - ] diff --git a/src/registrar/migrations/0053_alter_domain_state_alter_domainapplication_status_and_more.py b/src/registrar/migrations/0053_alter_domain_state_alter_domainapplication_status_and_more.py new file mode 100644 index 000000000..856ce7f0f --- /dev/null +++ b/src/registrar/migrations/0053_alter_domain_state_alter_domainapplication_status_and_more.py @@ -0,0 +1,60 @@ +# Generated by Django 4.2.7 on 2023-12-05 22:07 + +from django.db import migrations, models +import django_fsm + + +class Migration(migrations.Migration): + dependencies = [ + ("registrar", "0052_alter_domainapplication_anything_else_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="Very basic info about the lifecycle of this domain object", + max_length=21, + protected=True, + ), + ), + migrations.AlterField( + model_name="domainapplication", + name="status", + field=django_fsm.FSMField( + choices=[ + ("started", "Started"), + ("submitted", "Submitted"), + ("in review", "In review"), + ("action needed", "Action needed"), + ("approved", "Approved"), + ("withdrawn", "Withdrawn"), + ("rejected", "Rejected"), + ("ineligible", "Ineligible"), + ], + default="started", + max_length=50, + ), + ), + migrations.AlterField( + model_name="transitiondomain", + name="status", + field=models.CharField( + blank=True, + choices=[("ready", "Ready"), ("on hold", "On hold"), ("unknown", "Unknown")], + default="ready", + help_text="domain status during the transfer", + max_length=255, + verbose_name="Status", + ), + ), + ] From d3b732f7350e68e63891509f72e503cb9ca9910b Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Tue, 5 Dec 2023 19:18:11 -0500 Subject: [PATCH 11/20] Revise Domain Application model to change the status constants to use tuples --- src/registrar/admin.py | 26 +++--- src/registrar/assets/js/get-gov-admin.js | 30 +++--- src/registrar/fixtures_applications.py | 2 +- .../0054_alter_domainapplication_status.py | 31 +++++++ src/registrar/models/domain_application.py | 59 +++++------- src/registrar/tests/common.py | 14 +-- src/registrar/tests/test_admin.py | 54 +++++------ src/registrar/tests/test_models.py | 92 +++++++++---------- src/registrar/tests/test_models_domain.py | 2 +- src/registrar/tests/test_views.py | 12 +-- src/registrar/views/application.py | 12 +-- src/registrar/views/utility/mixins.py | 8 +- 12 files changed, 182 insertions(+), 160 deletions(-) create mode 100644 src/registrar/migrations/0054_alter_domainapplication_status.py diff --git a/src/registrar/admin.py b/src/registrar/admin.py index aca246a08..3db706c94 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -515,14 +515,14 @@ class DomainApplicationAdminForm(forms.ModelForm): current_state = application.status # first option in status transitions is current state - available_transitions = [(current_state, current_state)] + available_transitions = [(current_state, application.get_status_display())] transitions = get_available_FIELD_transitions( application, models.DomainApplication._meta.get_field("status") ) for transition in transitions: - available_transitions.append((transition.target, transition.target)) + available_transitions.append((transition.target, transition.target.label)) # only set the available transitions if the user is not restricted # from editing the domain application; otherwise, the form will be @@ -638,10 +638,10 @@ class DomainApplicationAdmin(ListHeaderAdmin): if ( obj - and original_obj.status == models.DomainApplication.APPROVED + and original_obj.status == models.DomainApplication.ApplicationStatus.APPROVED and ( - obj.status == models.DomainApplication.REJECTED - or obj.status == models.DomainApplication.INELIGIBLE + obj.status == models.DomainApplication.ApplicationStatus.REJECTED + or obj.status == models.DomainApplication.ApplicationStatus.INELIGIBLE ) and not obj.domain_is_not_active() ): @@ -663,14 +663,14 @@ class DomainApplicationAdmin(ListHeaderAdmin): else: if obj.status != original_obj.status: status_method_mapping = { - models.DomainApplication.STARTED: None, - models.DomainApplication.SUBMITTED: obj.submit, - models.DomainApplication.IN_REVIEW: obj.in_review, - models.DomainApplication.ACTION_NEEDED: obj.action_needed, - models.DomainApplication.APPROVED: obj.approve, - models.DomainApplication.WITHDRAWN: obj.withdraw, - models.DomainApplication.REJECTED: obj.reject, - models.DomainApplication.INELIGIBLE: (obj.reject_with_prejudice), + models.DomainApplication.ApplicationStatus.STARTED: None, + models.DomainApplication.ApplicationStatus.SUBMITTED: obj.submit, + models.DomainApplication.ApplicationStatus.IN_REVIEW: obj.in_review, + models.DomainApplication.ApplicationStatus.ACTION_NEEDED: obj.action_needed, + models.DomainApplication.ApplicationStatus.APPROVED: obj.approve, + models.DomainApplication.ApplicationStatus.WITHDRAWN: obj.withdraw, + models.DomainApplication.ApplicationStatus.REJECTED: obj.reject, + models.DomainApplication.ApplicationStatus.INELIGIBLE: (obj.reject_with_prejudice), } selected_method = status_method_mapping.get(obj.status) if selected_method is None: diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js index bdbd89c23..7b92f1192 100644 --- a/src/registrar/assets/js/get-gov-admin.js +++ b/src/registrar/assets/js/get-gov-admin.js @@ -65,22 +65,22 @@ function openInNewTab(el, removeAttribute = false){ /** An IIFE to capitalize statuses in the Domain Application status dropdown */ -(function (){ - function capitalizeFirstLetterInDropdownOptions(dropdown_id) { - // Grabs the status dropdown - var selectElement = document.getElementById(dropdown_id); - if (selectElement) { - var options = selectElement.options; - // Loop through each option, and convert to sentence case - for (var i = 0; i < options.length; i++) { - var option = options[i]; - option.text = option.text.charAt(0).toUpperCase() + option.text.slice(1).toLowerCase(); - } - } - } +// (function (){ +// function capitalizeFirstLetterInDropdownOptions(dropdown_id) { +// // Grabs the status dropdown +// var selectElement = document.getElementById(dropdown_id); +// if (selectElement) { +// var options = selectElement.options; +// // Loop through each option, and convert to sentence case +// for (var i = 0; i < options.length; i++) { +// var option = options[i]; +// option.text = option.text.charAt(0).toUpperCase() + option.text.slice(1).toLowerCase(); +// } +// } +// } - capitalizeFirstLetterInDropdownOptions('id_status'); -})(); +// capitalizeFirstLetterInDropdownOptions('id_status'); +// })(); // Function to check for the existence of the "to" select list element in the DOM, and if and when found, // initialize the associated widget diff --git a/src/registrar/fixtures_applications.py b/src/registrar/fixtures_applications.py index 679e5f7fe..7f6a63719 100644 --- a/src/registrar/fixtures_applications.py +++ b/src/registrar/fixtures_applications.py @@ -214,7 +214,7 @@ class DomainFixture(DomainApplicationFixture): for user in users: # approve one of each users in review status domains - application = DomainApplication.objects.filter(creator=user, status=DomainApplication.IN_REVIEW).last() + application = DomainApplication.objects.filter(creator=user, status=DomainApplication.ApplicationStatus.IN_REVIEW).last() logger.debug(f"Approving {application} for {user}") application.approve() application.save() diff --git a/src/registrar/migrations/0054_alter_domainapplication_status.py b/src/registrar/migrations/0054_alter_domainapplication_status.py new file mode 100644 index 000000000..c0c427179 --- /dev/null +++ b/src/registrar/migrations/0054_alter_domainapplication_status.py @@ -0,0 +1,31 @@ +# Generated by Django 4.2.7 on 2023-12-05 23:14 + +from django.db import migrations +import django_fsm + + +class Migration(migrations.Migration): + dependencies = [ + ("registrar", "0053_alter_domain_state_alter_domainapplication_status_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="domainapplication", + name="status", + field=django_fsm.FSMField( + choices=[ + ("started", "Started"), + ("submitted", "Submitted"), + ("in_review", "In review"), + ("action_needed", "Action needed"), + ("approved", "Approved"), + ("withdrawn", "Withdrawn"), + ("rejected", "Rejected"), + ("ineligible", "Ineligible"), + ], + default="started", + max_length=50, + ), + ), + ] diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index 8d9083573..32cd239dc 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -18,26 +18,17 @@ logger = logging.getLogger(__name__) class DomainApplication(TimeStampedModel): """A registrant's application for a new domain.""" - - # #### Constants for choice fields #### - STARTED = "started" - SUBMITTED = "submitted" - IN_REVIEW = "in review" - ACTION_NEEDED = "action needed" - APPROVED = "approved" - WITHDRAWN = "withdrawn" - REJECTED = "rejected" - INELIGIBLE = "ineligible" - STATUS_CHOICES = [ - (STARTED, STARTED.capitalize()), - (SUBMITTED, SUBMITTED.capitalize()), - (IN_REVIEW, IN_REVIEW.capitalize()), - (ACTION_NEEDED, ACTION_NEEDED.capitalize()), - (APPROVED, APPROVED.capitalize()), - (WITHDRAWN, WITHDRAWN.capitalize()), - (REJECTED, REJECTED.capitalize()), - (INELIGIBLE, INELIGIBLE.capitalize()), - ] + + # Constants for choice fields + class ApplicationStatus(models.TextChoices): + STARTED = "started", "Started" + SUBMITTED = "submitted", "Submitted" + IN_REVIEW = "in_review", "In review" + ACTION_NEEDED = "action_needed", "Action needed" + APPROVED = "approved", "Approved" + WITHDRAWN = "withdrawn", "Withdrawn" + REJECTED = "rejected", "Rejected" + INELIGIBLE = "ineligible", "Ineligible" class StateTerritoryChoices(models.TextChoices): ALABAMA = "AL", "Alabama (AL)" @@ -363,8 +354,8 @@ class DomainApplication(TimeStampedModel): # #### Internal fields about the application ##### status = FSMField( - choices=STATUS_CHOICES, # possible states as an array of constants - default=STARTED, # sensible default + choices=ApplicationStatus.choices, # possible states as an array of constants + default=ApplicationStatus.STARTED, # sensible default protected=False, # can change state directly, particularly in Django admin ) # This is the application user who created this application. The contact @@ -590,7 +581,7 @@ class DomainApplication(TimeStampedModel): except EmailSendingError: logger.warning("Failed to send confirmation email", exc_info=True) - @transition(field="status", source=[STARTED, ACTION_NEEDED, WITHDRAWN], target=SUBMITTED) + @transition(field="status", source=[ApplicationStatus.STARTED, ApplicationStatus.ACTION_NEEDED, ApplicationStatus.WITHDRAWN], target=ApplicationStatus.SUBMITTED) def submit(self): """Submit an application that is started. @@ -616,7 +607,7 @@ class DomainApplication(TimeStampedModel): "emails/submission_confirmation_subject.txt", ) - @transition(field="status", source=SUBMITTED, target=IN_REVIEW) + @transition(field="status", source=ApplicationStatus.SUBMITTED, target=ApplicationStatus.IN_REVIEW) def in_review(self): """Investigate an application that has been submitted. @@ -628,7 +619,7 @@ class DomainApplication(TimeStampedModel): "emails/status_change_in_review_subject.txt", ) - @transition(field="status", source=[IN_REVIEW, REJECTED], target=ACTION_NEEDED) + @transition(field="status", source=[ApplicationStatus.IN_REVIEW, ApplicationStatus.REJECTED], target=ApplicationStatus.ACTION_NEEDED) def action_needed(self): """Send back an application that is under investigation or rejected. @@ -642,8 +633,8 @@ class DomainApplication(TimeStampedModel): @transition( field="status", - source=[SUBMITTED, IN_REVIEW, REJECTED, INELIGIBLE], - target=APPROVED, + source=[ApplicationStatus.SUBMITTED, ApplicationStatus.IN_REVIEW, ApplicationStatus.REJECTED, ApplicationStatus.INELIGIBLE], + target=ApplicationStatus.APPROVED, ) def approve(self): """Approve an application that has been submitted. @@ -676,7 +667,7 @@ class DomainApplication(TimeStampedModel): "emails/status_change_approved_subject.txt", ) - @transition(field="status", source=[SUBMITTED, IN_REVIEW], target=WITHDRAWN) + @transition(field="status", source=[ApplicationStatus.SUBMITTED, ApplicationStatus.IN_REVIEW], target=ApplicationStatus.WITHDRAWN) def withdraw(self): """Withdraw an application that has been submitted.""" self._send_status_update_email( @@ -687,8 +678,8 @@ class DomainApplication(TimeStampedModel): @transition( field="status", - source=[IN_REVIEW, APPROVED], - target=REJECTED, + source=[ApplicationStatus.IN_REVIEW, ApplicationStatus.APPROVED], + target=ApplicationStatus.REJECTED, conditions=[domain_is_not_active], ) def reject(self): @@ -696,7 +687,7 @@ class DomainApplication(TimeStampedModel): As side effects this will delete the domain and domain_information (will cascade), and send an email notification.""" - if self.status == self.APPROVED: + if self.status == self.ApplicationStatus.APPROVED: domain_state = self.approved_domain.state # Only reject if it exists on EPP if domain_state != Domain.State.UNKNOWN: @@ -712,8 +703,8 @@ class DomainApplication(TimeStampedModel): @transition( field="status", - source=[IN_REVIEW, APPROVED], - target=INELIGIBLE, + source=[ApplicationStatus.IN_REVIEW, ApplicationStatus.APPROVED], + target=ApplicationStatus.INELIGIBLE, conditions=[domain_is_not_active], ) def reject_with_prejudice(self): @@ -725,7 +716,7 @@ class DomainApplication(TimeStampedModel): permissions classes test against. This will also delete the domain and domain_information (will cascade) when they exist.""" - if self.status == self.APPROVED: + if self.status == self.ApplicationStatus.APPROVED: domain_state = self.approved_domain.state # Only reject if it exists on EPP if domain_state != Domain.State.UNKNOWN: diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index f17e0f9fa..89967ee90 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -294,7 +294,7 @@ class AuditedAdminMockData: self, domain_type, item_name, - status=DomainApplication.STARTED, + status=DomainApplication.ApplicationStatus.STARTED, org_type="federal", federal_type="executive", purpose="Purpose of the site", @@ -311,7 +311,7 @@ class AuditedAdminMockData: title, email, and username. status (str - optional): Defines the status for DomainApplication, - e.g. DomainApplication.STARTED + e.g. DomainApplication.ApplicationStatus.STARTED org_type (str - optional): Sets a domains org_type @@ -348,19 +348,19 @@ class AuditedAdminMockData: ) return full_arg_dict - def create_full_dummy_domain_application(self, item_name, status=DomainApplication.STARTED): + def create_full_dummy_domain_application(self, item_name, status=DomainApplication.ApplicationStatus.STARTED): """Creates a dummy domain application object""" domain_application_kwargs = self.dummy_kwarg_boilerplate(self.APPLICATION, item_name, status) application = DomainApplication.objects.get_or_create(**domain_application_kwargs)[0] return application - def create_full_dummy_domain_information(self, item_name, status=DomainApplication.STARTED): + def create_full_dummy_domain_information(self, item_name, status=DomainApplication.ApplicationStatus.STARTED): """Creates a dummy domain information object""" domain_application_kwargs = self.dummy_kwarg_boilerplate(self.INFORMATION, item_name, status) application = DomainInformation.objects.get_or_create(**domain_application_kwargs)[0] return application - def create_full_dummy_domain_invitation(self, item_name, status=DomainApplication.STARTED): + def create_full_dummy_domain_invitation(self, item_name, status=DomainApplication.ApplicationStatus.STARTED): """Creates a dummy domain invitation object""" domain_application_kwargs = self.dummy_kwarg_boilerplate(self.INVITATION, item_name, status) application = DomainInvitation.objects.get_or_create(**domain_application_kwargs)[0] @@ -374,7 +374,7 @@ class AuditedAdminMockData: has_other_contacts=True, has_current_website=True, has_alternative_gov_domain=True, - status=DomainApplication.STARTED, + status=DomainApplication.ApplicationStatus.STARTED, ): """A helper to create a dummy domain application object""" application = None @@ -455,7 +455,7 @@ def completed_application( has_alternative_gov_domain=True, has_about_your_organization=True, has_anything_else=True, - status=DomainApplication.STARTED, + status=DomainApplication.ApplicationStatus.STARTED, user=False, name="city.gov", ): diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 526a9ea2a..5db01492a 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -61,7 +61,7 @@ class TestDomainAdmin(MockEppLib): Make sure the short name is displaying in admin on the list page """ self.client.force_login(self.superuser) - application = completed_application(status=DomainApplication.IN_REVIEW) + application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) application.approve() response = self.client.get("/admin/registrar/domain/") @@ -282,7 +282,7 @@ class TestDomainApplicationAdminForm(TestCase): form = DomainApplicationAdminForm(instance=self.application) # Verify that the form choices match the available transitions for started - expected_choices = [("started", "started"), ("submitted", "submitted")] + expected_choices = [("started", "Started"), ("submitted", "Submitted")] self.assertEqual(form.fields["status"].widget.choices, expected_choices) def test_form_choices_when_no_instance(self): @@ -355,7 +355,7 @@ class TestDomainApplicationAdmin(MockEppLib): request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) # Modify the application's property - application.status = DomainApplication.SUBMITTED + application.status = DomainApplication.ApplicationStatus.SUBMITTED # Use the model admin's save_model method self.admin.save_model(request, application, form=None, change=True) @@ -390,13 +390,13 @@ class TestDomainApplicationAdmin(MockEppLib): with boto3_mocking.clients.handler_for("sesv2", mock_client): # Create a sample application - application = completed_application(status=DomainApplication.SUBMITTED) + application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED) # Create a mock request request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) # Modify the application's property - application.status = DomainApplication.IN_REVIEW + application.status = DomainApplication.ApplicationStatus.IN_REVIEW # Use the model admin's save_model method self.admin.save_model(request, application, form=None, change=True) @@ -431,13 +431,13 @@ class TestDomainApplicationAdmin(MockEppLib): with boto3_mocking.clients.handler_for("sesv2", mock_client): # Create a sample application - application = completed_application(status=DomainApplication.IN_REVIEW) + application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) # Create a mock request request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) # Modify the application's property - application.status = DomainApplication.APPROVED + application.status = DomainApplication.ApplicationStatus.APPROVED # Use the model admin's save_model method self.admin.save_model(request, application, form=None, change=True) @@ -467,13 +467,13 @@ class TestDomainApplicationAdmin(MockEppLib): User.objects.filter(email=EMAIL).delete() # Create a sample application - application = completed_application(status=DomainApplication.IN_REVIEW) + application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) # Create a mock request request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) # Modify the application's property - application.status = DomainApplication.APPROVED + application.status = DomainApplication.ApplicationStatus.APPROVED # Use the model admin's save_model method self.admin.save_model(request, application, form=None, change=True) @@ -492,13 +492,13 @@ class TestDomainApplicationAdmin(MockEppLib): with boto3_mocking.clients.handler_for("sesv2", mock_client): # Create a sample application - application = completed_application(status=DomainApplication.IN_REVIEW) + application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) # Create a mock request request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) # Modify the application's property - application.status = DomainApplication.ACTION_NEEDED + application.status = DomainApplication.ApplicationStatus.ACTION_NEEDED # Use the model admin's save_model method self.admin.save_model(request, application, form=None, change=True) @@ -533,13 +533,13 @@ class TestDomainApplicationAdmin(MockEppLib): with boto3_mocking.clients.handler_for("sesv2", mock_client): # Create a sample application - application = completed_application(status=DomainApplication.IN_REVIEW) + application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) # Create a mock request request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) # Modify the application's property - application.status = DomainApplication.REJECTED + application.status = DomainApplication.ApplicationStatus.REJECTED # Use the model admin's save_model method self.admin.save_model(request, application, form=None, change=True) @@ -569,13 +569,13 @@ class TestDomainApplicationAdmin(MockEppLib): User.objects.filter(email=EMAIL).delete() # Create a sample application - application = completed_application(status=DomainApplication.IN_REVIEW) + application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) # Create a mock request request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk)) # Modify the application's property - application.status = DomainApplication.INELIGIBLE + application.status = DomainApplication.ApplicationStatus.INELIGIBLE # Use the model admin's save_model method self.admin.save_model(request, application, form=None, change=True) @@ -584,7 +584,7 @@ class TestDomainApplicationAdmin(MockEppLib): self.assertEqual(application.creator.status, "restricted") def test_readonly_when_restricted_creator(self): - application = completed_application(status=DomainApplication.IN_REVIEW) + application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) application.creator.status = User.RESTRICTED application.creator.save() @@ -662,7 +662,7 @@ class TestDomainApplicationAdmin(MockEppLib): def test_saving_when_restricted_creator(self): # Create an instance of the model - application = completed_application(status=DomainApplication.IN_REVIEW) + application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) application.creator.status = User.RESTRICTED application.creator.save() @@ -681,11 +681,11 @@ class TestDomainApplicationAdmin(MockEppLib): ) # Assert that the status has not changed - self.assertEqual(application.status, DomainApplication.IN_REVIEW) + self.assertEqual(application.status, DomainApplication.ApplicationStatus.IN_REVIEW) def test_change_view_with_restricted_creator(self): # Create an instance of the model - application = completed_application(status=DomainApplication.IN_REVIEW) + application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) application.creator.status = User.RESTRICTED application.creator.save() @@ -704,7 +704,7 @@ class TestDomainApplicationAdmin(MockEppLib): def test_error_when_saving_approved_to_rejected_and_domain_is_active(self): # Create an instance of the model - application = completed_application(status=DomainApplication.APPROVED) + application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) domain = Domain.objects.create(name=application.requested_domain.name) application.approved_domain = domain application.save() @@ -724,7 +724,7 @@ class TestDomainApplicationAdmin(MockEppLib): stack.enter_context(patch.object(messages, "error")) # Simulate saving the model - application.status = DomainApplication.REJECTED + application.status = DomainApplication.ApplicationStatus.REJECTED self.admin.save_model(request, application, None, True) # Assert that the error message was called with the correct argument @@ -735,7 +735,7 @@ class TestDomainApplicationAdmin(MockEppLib): def test_side_effects_when_saving_approved_to_rejected(self): # Create an instance of the model - application = completed_application(status=DomainApplication.APPROVED) + application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) domain = Domain.objects.create(name=application.requested_domain.name) domain_information = DomainInformation.objects.create(creator=self.superuser, domain=domain) application.approved_domain = domain @@ -756,7 +756,7 @@ class TestDomainApplicationAdmin(MockEppLib): stack.enter_context(patch.object(messages, "error")) # Simulate saving the model - application.status = DomainApplication.REJECTED + application.status = DomainApplication.ApplicationStatus.REJECTED self.admin.save_model(request, application, None, True) # Assert that the error message was never called @@ -774,7 +774,7 @@ class TestDomainApplicationAdmin(MockEppLib): def test_error_when_saving_approved_to_ineligible_and_domain_is_active(self): # Create an instance of the model - application = completed_application(status=DomainApplication.APPROVED) + application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) domain = Domain.objects.create(name=application.requested_domain.name) application.approved_domain = domain application.save() @@ -794,7 +794,7 @@ class TestDomainApplicationAdmin(MockEppLib): stack.enter_context(patch.object(messages, "error")) # Simulate saving the model - application.status = DomainApplication.INELIGIBLE + application.status = DomainApplication.ApplicationStatus.INELIGIBLE self.admin.save_model(request, application, None, True) # Assert that the error message was called with the correct argument @@ -805,7 +805,7 @@ class TestDomainApplicationAdmin(MockEppLib): def test_side_effects_when_saving_approved_to_ineligible(self): # Create an instance of the model - application = completed_application(status=DomainApplication.APPROVED) + application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) domain = Domain.objects.create(name=application.requested_domain.name) domain_information = DomainInformation.objects.create(creator=self.superuser, domain=domain) application.approved_domain = domain @@ -826,7 +826,7 @@ class TestDomainApplicationAdmin(MockEppLib): stack.enter_context(patch.object(messages, "error")) # Simulate saving the model - application.status = DomainApplication.INELIGIBLE + application.status = DomainApplication.ApplicationStatus.INELIGIBLE self.admin.save_model(request, application, None, True) # Assert that the error message was never called diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py index d397cb129..c2820696d 100644 --- a/src/registrar/tests/test_models.py +++ b/src/registrar/tests/test_models.py @@ -35,7 +35,7 @@ class TestDomainApplication(TestCase): """Can create with just a creator.""" user, _ = User.objects.get_or_create() application = DomainApplication.objects.create(creator=user) - self.assertEqual(application.status, DomainApplication.STARTED) + self.assertEqual(application.status, DomainApplication.ApplicationStatus.STARTED) def test_full_create(self): """Can create with all fields.""" @@ -108,7 +108,7 @@ class TestDomainApplication(TestCase): # no submitter email so this emits a log warning with less_console_noise(): application.submit() - self.assertEqual(application.status, application.SUBMITTED) + self.assertEqual(application.status, application.ApplicationStatus.SUBMITTED) def test_submit_sends_email(self): """Create an application and submit it and see if email was sent.""" @@ -139,7 +139,7 @@ class TestDomainApplication(TestCase): """Create an application with status submitted and call submit against transition rules""" - application = completed_application(status=DomainApplication.SUBMITTED) + application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED) with self.assertRaises(TransitionNotAllowed): application.submit() @@ -148,7 +148,7 @@ class TestDomainApplication(TestCase): """Create an application with status in review and call submit against transition rules""" - application = completed_application(status=DomainApplication.IN_REVIEW) + application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) with self.assertRaises(TransitionNotAllowed): application.submit() @@ -157,7 +157,7 @@ class TestDomainApplication(TestCase): """Create an application with status approved and call submit against transition rules""" - application = completed_application(status=DomainApplication.APPROVED) + application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) with self.assertRaises(TransitionNotAllowed): application.submit() @@ -166,7 +166,7 @@ class TestDomainApplication(TestCase): """Create an application with status rejected and call submit against transition rules""" - application = completed_application(status=DomainApplication.REJECTED) + application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED) with self.assertRaises(TransitionNotAllowed): application.submit() @@ -175,7 +175,7 @@ class TestDomainApplication(TestCase): """Create an application with status ineligible and call submit against transition rules""" - application = completed_application(status=DomainApplication.INELIGIBLE) + application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE) with self.assertRaises(TransitionNotAllowed): application.submit() @@ -184,7 +184,7 @@ class TestDomainApplication(TestCase): """Create an application with status started and call in_review against transition rules""" - application = completed_application(status=DomainApplication.STARTED) + application = completed_application(status=DomainApplication.ApplicationStatus.STARTED) with self.assertRaises(TransitionNotAllowed): application.in_review() @@ -193,7 +193,7 @@ class TestDomainApplication(TestCase): """Create an application with status in review and call in_review against transition rules""" - application = completed_application(status=DomainApplication.IN_REVIEW) + application = completed_application(status=DomainApplication.ApplicationStatus.IN_REVIEW) with self.assertRaises(TransitionNotAllowed): application.in_review() @@ -202,7 +202,7 @@ class TestDomainApplication(TestCase): """Create an application with status approved and call in_review against transition rules""" - application = completed_application(status=DomainApplication.APPROVED) + application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) with self.assertRaises(TransitionNotAllowed): application.in_review() @@ -211,7 +211,7 @@ class TestDomainApplication(TestCase): """Create an application with status action needed and call in_review against transition rules""" - application = completed_application(status=DomainApplication.ACTION_NEEDED) + application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED) with self.assertRaises(TransitionNotAllowed): application.in_review() @@ -220,7 +220,7 @@ class TestDomainApplication(TestCase): """Create an application with status rejected and call in_review against transition rules""" - application = completed_application(status=DomainApplication.REJECTED) + application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED) with self.assertRaises(TransitionNotAllowed): application.in_review() @@ -229,7 +229,7 @@ class TestDomainApplication(TestCase): """Create an application with status withdrawn and call in_review against transition rules""" - application = completed_application(status=DomainApplication.WITHDRAWN) + application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN) with self.assertRaises(TransitionNotAllowed): application.in_review() @@ -238,7 +238,7 @@ class TestDomainApplication(TestCase): """Create an application with status ineligible and call in_review against transition rules""" - application = completed_application(status=DomainApplication.INELIGIBLE) + application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE) with self.assertRaises(TransitionNotAllowed): application.in_review() @@ -247,7 +247,7 @@ class TestDomainApplication(TestCase): """Create an application with status started and call action_needed against transition rules""" - application = completed_application(status=DomainApplication.STARTED) + application = completed_application(status=DomainApplication.ApplicationStatus.STARTED) with self.assertRaises(TransitionNotAllowed): application.action_needed() @@ -256,7 +256,7 @@ class TestDomainApplication(TestCase): """Create an application with status submitted and call action_needed against transition rules""" - application = completed_application(status=DomainApplication.SUBMITTED) + application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED) with self.assertRaises(TransitionNotAllowed): application.action_needed() @@ -265,7 +265,7 @@ class TestDomainApplication(TestCase): """Create an application with status action needed and call action_needed against transition rules""" - application = completed_application(status=DomainApplication.ACTION_NEEDED) + application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED) with self.assertRaises(TransitionNotAllowed): application.action_needed() @@ -274,7 +274,7 @@ class TestDomainApplication(TestCase): """Create an application with status approved and call action_needed against transition rules""" - application = completed_application(status=DomainApplication.APPROVED) + application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) with self.assertRaises(TransitionNotAllowed): application.action_needed() @@ -283,7 +283,7 @@ class TestDomainApplication(TestCase): """Create an application with status withdrawn and call action_needed against transition rules""" - application = completed_application(status=DomainApplication.WITHDRAWN) + application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN) with self.assertRaises(TransitionNotAllowed): application.action_needed() @@ -292,7 +292,7 @@ class TestDomainApplication(TestCase): """Create an application with status ineligible and call action_needed against transition rules""" - application = completed_application(status=DomainApplication.INELIGIBLE) + application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE) with self.assertRaises(TransitionNotAllowed): application.action_needed() @@ -301,7 +301,7 @@ class TestDomainApplication(TestCase): """Create an application with status started and call approve against transition rules""" - application = completed_application(status=DomainApplication.STARTED) + application = completed_application(status=DomainApplication.ApplicationStatus.STARTED) with self.assertRaises(TransitionNotAllowed): application.approve() @@ -310,7 +310,7 @@ class TestDomainApplication(TestCase): """Create an application with status approved and call approve against transition rules""" - application = completed_application(status=DomainApplication.APPROVED) + application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) with self.assertRaises(TransitionNotAllowed): application.approve() @@ -319,7 +319,7 @@ class TestDomainApplication(TestCase): """Create an application with status action needed and call approve against transition rules""" - application = completed_application(status=DomainApplication.ACTION_NEEDED) + application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED) with self.assertRaises(TransitionNotAllowed): application.approve() @@ -328,7 +328,7 @@ class TestDomainApplication(TestCase): """Create an application with status withdrawn and call approve against transition rules""" - application = completed_application(status=DomainApplication.WITHDRAWN) + application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN) with self.assertRaises(TransitionNotAllowed): application.approve() @@ -337,7 +337,7 @@ class TestDomainApplication(TestCase): """Create an application with status started and call withdraw against transition rules""" - application = completed_application(status=DomainApplication.STARTED) + application = completed_application(status=DomainApplication.ApplicationStatus.STARTED) with self.assertRaises(TransitionNotAllowed): application.withdraw() @@ -346,7 +346,7 @@ class TestDomainApplication(TestCase): """Create an application with status approved and call withdraw against transition rules""" - application = completed_application(status=DomainApplication.APPROVED) + application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) with self.assertRaises(TransitionNotAllowed): application.withdraw() @@ -355,7 +355,7 @@ class TestDomainApplication(TestCase): """Create an application with status action needed and call withdraw against transition rules""" - application = completed_application(status=DomainApplication.ACTION_NEEDED) + application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED) with self.assertRaises(TransitionNotAllowed): application.withdraw() @@ -364,7 +364,7 @@ class TestDomainApplication(TestCase): """Create an application with status rejected and call withdraw against transition rules""" - application = completed_application(status=DomainApplication.REJECTED) + application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED) with self.assertRaises(TransitionNotAllowed): application.withdraw() @@ -373,7 +373,7 @@ class TestDomainApplication(TestCase): """Create an application with status withdrawn and call withdraw against transition rules""" - application = completed_application(status=DomainApplication.WITHDRAWN) + application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN) with self.assertRaises(TransitionNotAllowed): application.withdraw() @@ -382,7 +382,7 @@ class TestDomainApplication(TestCase): """Create an application with status ineligible and call withdraw against transition rules""" - application = completed_application(status=DomainApplication.INELIGIBLE) + application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE) with self.assertRaises(TransitionNotAllowed): application.withdraw() @@ -391,7 +391,7 @@ class TestDomainApplication(TestCase): """Create an application with status started and call reject against transition rules""" - application = completed_application(status=DomainApplication.STARTED) + application = completed_application(status=DomainApplication.ApplicationStatus.STARTED) with self.assertRaises(TransitionNotAllowed): application.reject() @@ -400,7 +400,7 @@ class TestDomainApplication(TestCase): """Create an application with status submitted and call reject against transition rules""" - application = completed_application(status=DomainApplication.SUBMITTED) + application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED) with self.assertRaises(TransitionNotAllowed): application.reject() @@ -409,7 +409,7 @@ class TestDomainApplication(TestCase): """Create an application with status action needed and call reject against transition rules""" - application = completed_application(status=DomainApplication.ACTION_NEEDED) + application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED) with self.assertRaises(TransitionNotAllowed): application.reject() @@ -418,7 +418,7 @@ class TestDomainApplication(TestCase): """Create an application with status withdrawn and call reject against transition rules""" - application = completed_application(status=DomainApplication.WITHDRAWN) + application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN) with self.assertRaises(TransitionNotAllowed): application.reject() @@ -427,7 +427,7 @@ class TestDomainApplication(TestCase): """Create an application with status rejected and call reject against transition rules""" - application = completed_application(status=DomainApplication.REJECTED) + application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED) with self.assertRaises(TransitionNotAllowed): application.reject() @@ -436,7 +436,7 @@ class TestDomainApplication(TestCase): """Create an application with status ineligible and call reject against transition rules""" - application = completed_application(status=DomainApplication.INELIGIBLE) + application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE) with self.assertRaises(TransitionNotAllowed): application.reject() @@ -445,7 +445,7 @@ class TestDomainApplication(TestCase): """Create an application with status approved, create a matching domain that is active, and call reject against transition rules""" - application = completed_application(status=DomainApplication.APPROVED) + application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) domain = Domain.objects.create(name=application.requested_domain.name) application.approved_domain = domain application.save() @@ -464,7 +464,7 @@ class TestDomainApplication(TestCase): """Create an application with status started and call reject against transition rules""" - application = completed_application(status=DomainApplication.STARTED) + application = completed_application(status=DomainApplication.ApplicationStatus.STARTED) with self.assertRaises(TransitionNotAllowed): application.reject_with_prejudice() @@ -473,7 +473,7 @@ class TestDomainApplication(TestCase): """Create an application with status submitted and call reject against transition rules""" - application = completed_application(status=DomainApplication.SUBMITTED) + application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED) with self.assertRaises(TransitionNotAllowed): application.reject_with_prejudice() @@ -482,7 +482,7 @@ class TestDomainApplication(TestCase): """Create an application with status action needed and call reject against transition rules""" - application = completed_application(status=DomainApplication.ACTION_NEEDED) + application = completed_application(status=DomainApplication.ApplicationStatus.ACTION_NEEDED) with self.assertRaises(TransitionNotAllowed): application.reject_with_prejudice() @@ -491,7 +491,7 @@ class TestDomainApplication(TestCase): """Create an application with status withdrawn and call reject against transition rules""" - application = completed_application(status=DomainApplication.WITHDRAWN) + application = completed_application(status=DomainApplication.ApplicationStatus.WITHDRAWN) with self.assertRaises(TransitionNotAllowed): application.reject_with_prejudice() @@ -500,7 +500,7 @@ class TestDomainApplication(TestCase): """Create an application with status rejected and call reject against transition rules""" - application = completed_application(status=DomainApplication.REJECTED) + application = completed_application(status=DomainApplication.ApplicationStatus.REJECTED) with self.assertRaises(TransitionNotAllowed): application.reject_with_prejudice() @@ -509,7 +509,7 @@ class TestDomainApplication(TestCase): """Create an application with status ineligible and call reject against transition rules""" - application = completed_application(status=DomainApplication.INELIGIBLE) + application = completed_application(status=DomainApplication.ApplicationStatus.INELIGIBLE) with self.assertRaises(TransitionNotAllowed): application.reject_with_prejudice() @@ -518,7 +518,7 @@ class TestDomainApplication(TestCase): """Create an application with status approved, create a matching domain that is active, and call reject_with_prejudice against transition rules""" - application = completed_application(status=DomainApplication.APPROVED) + application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED) domain = Domain.objects.create(name=application.requested_domain.name) application.approved_domain = domain application.save() @@ -543,7 +543,7 @@ class TestPermissions(TestCase): user, _ = User.objects.get_or_create() application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain) # skip using the submit method - application.status = DomainApplication.SUBMITTED + application.status = DomainApplication.ApplicationStatus.SUBMITTED application.approve() # should be a role for this user @@ -560,7 +560,7 @@ class TestDomainInfo(TestCase): user, _ = User.objects.get_or_create() application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain) # skip using the submit method - application.status = DomainApplication.SUBMITTED + application.status = DomainApplication.ApplicationStatus.SUBMITTED application.approve() # should be an information present for this domain diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index fac1c0fcd..39f63c942 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -261,7 +261,7 @@ class TestDomainCreation(MockEppLib): user, _ = User.objects.get_or_create() application = DomainApplication.objects.create(creator=user, requested_domain=draft_domain) # skip using the submit method - application.status = DomainApplication.SUBMITTED + application.status = DomainApplication.ApplicationStatus.SUBMITTED # transition to approve state application.approve() # should have information present for this domain diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index e0e2c8c97..b5b2f1f76 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -1079,7 +1079,7 @@ class DomainApplicationTests(TestWithUser, WebTest): Make sure the long name is displaying in the application summary page (manage your application) """ - completed_application(status=DomainApplication.SUBMITTED, user=self.user) + completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED, user=self.user) home_page = self.app.get("/") self.assertContains(home_page, "city.gov") # click the "Edit" link @@ -2117,7 +2117,7 @@ class TestApplicationStatus(TestWithUser, WebTest): def test_application_status(self): """Checking application status page""" - application = completed_application(status=DomainApplication.SUBMITTED, user=self.user) + application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED, user=self.user) application.save() home_page = self.app.get("/") @@ -2137,7 +2137,7 @@ class TestApplicationStatus(TestWithUser, WebTest): self.user.status = "ineligible" self.user.save() - application = completed_application(status=DomainApplication.SUBMITTED, user=self.user) + application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED, user=self.user) application.save() home_page = self.app.get("/") @@ -2152,7 +2152,7 @@ class TestApplicationStatus(TestWithUser, WebTest): def test_application_withdraw(self): """Checking application status page""" - application = completed_application(status=DomainApplication.SUBMITTED, user=self.user) + application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED, user=self.user) application.save() home_page = self.app.get("/") @@ -2182,7 +2182,7 @@ class TestApplicationStatus(TestWithUser, WebTest): def test_application_status_no_permissions(self): """Can't access applications without being the creator.""" - application = completed_application(status=DomainApplication.SUBMITTED, user=self.user) + application = completed_application(status=DomainApplication.ApplicationStatus.SUBMITTED, user=self.user) other_user = User() other_user.save() application.creator = other_user @@ -2202,7 +2202,7 @@ class TestApplicationStatus(TestWithUser, WebTest): def test_approved_application_not_in_active_requests(self): """An approved application is not shown in the Active Requests table on home.html.""" - application = completed_application(status=DomainApplication.APPROVED, user=self.user) + application = completed_application(status=DomainApplication.ApplicationStatus.APPROVED, user=self.user) application.save() home_page = self.app.get("/") diff --git a/src/registrar/views/application.py b/src/registrar/views/application.py index 32633a89b..f6489bedf 100644 --- a/src/registrar/views/application.py +++ b/src/registrar/views/application.py @@ -293,9 +293,9 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView): return self.pending_applications() def approved_applications_exist(self): - """Checks if user is creator of applications with APPROVED status""" + """Checks if user is creator of applications with ApplicationStatus.APPROVED status""" approved_application_count = DomainApplication.objects.filter( - creator=self.request.user, status=DomainApplication.APPROVED + creator=self.request.user, status=DomainApplication.ApplicationStatus.APPROVED ).count() return approved_application_count > 0 @@ -308,11 +308,11 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView): def pending_applications(self): """Returns a List of user's applications with one of the following states: - SUBMITTED, IN_REVIEW, ACTION_NEEDED""" - # if the current application has ACTION_NEEDED status, this check should not be performed - if self.application.status == DomainApplication.ACTION_NEEDED: + ApplicationStatus.SUBMITTED, ApplicationStatus.IN_REVIEW, ApplicationStatus.ACTION_NEEDED""" + # if the current application has ApplicationStatus.ACTION_NEEDED status, this check should not be performed + if self.application.status == DomainApplication.ApplicationStatus.ACTION_NEEDED: return [] - check_statuses = [DomainApplication.SUBMITTED, DomainApplication.IN_REVIEW, DomainApplication.ACTION_NEEDED] + check_statuses = [DomainApplication.ApplicationStatus.SUBMITTED, DomainApplication.ApplicationStatus.IN_REVIEW, DomainApplication.ApplicationStatus.ACTION_NEEDED] return DomainApplication.objects.filter(creator=self.request.user, status__in=check_statuses) def get_context_data(self): diff --git a/src/registrar/views/utility/mixins.py b/src/registrar/views/utility/mixins.py index aaa2e849c..37c0f6e98 100644 --- a/src/registrar/views/utility/mixins.py +++ b/src/registrar/views/utility/mixins.py @@ -101,10 +101,10 @@ class DomainPermission(PermissionsLoginMixin): # Analysts may manage domains, when they are in these statuses: valid_domain_statuses = [ - DomainApplication.APPROVED, - DomainApplication.IN_REVIEW, - DomainApplication.REJECTED, - DomainApplication.ACTION_NEEDED, + DomainApplication.ApplicationStatus.APPROVED, + DomainApplication.ApplicationStatus.IN_REVIEW, + DomainApplication.ApplicationStatus.REJECTED, + DomainApplication.ApplicationStatus.ACTION_NEEDED, # Edge case - some domains do not have # a status or DomainInformation... aka a status of 'None'. # It is necessary to access those to correct errors. From af1b598699d0c2463ae95b12c12b86ce44ffe2c7 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Tue, 5 Dec 2023 19:21:04 -0500 Subject: [PATCH 12/20] Complete Domain State options revisions to tuples --- src/registrar/models/domain.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 16c918f84..632a6200d 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -122,20 +122,20 @@ class Domain(TimeStampedModel, DomainHelper): """These capture (some of) the states a domain object can be in.""" # the state is indeterminate - UNKNOWN = "unknown" + UNKNOWN = "unknown", "Unknown" # The domain object exists in the registry # but nameservers don't exist for it yet DNS_NEEDED = "dns needed", "Dns needed" # Domain has had nameservers set, may or may not be active - READY = "ready" + READY = "ready", "Ready" # Registrar manually changed state to client hold ON_HOLD = "on hold", "On hold" # previously existed but has been deleted from the registry - DELETED = "deleted" + DELETED = "deleted", "Deleted" class Cache(property): """ From 00c04783b595b8d9283534e41c889c6f2862d046 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Tue, 5 Dec 2023 19:28:20 -0500 Subject: [PATCH 13/20] Change choices for Domain INvitation status to tuple constants --- .../commands/load_domain_invitations.py | 2 +- src/registrar/models/domain_invitation.py | 16 ++++++++-------- src/registrar/models/user.py | 2 +- src/registrar/tests/common.py | 2 +- src/registrar/tests/test_models.py | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/registrar/management/commands/load_domain_invitations.py b/src/registrar/management/commands/load_domain_invitations.py index 28eb09def..32a63d860 100644 --- a/src/registrar/management/commands/load_domain_invitations.py +++ b/src/registrar/management/commands/load_domain_invitations.py @@ -62,7 +62,7 @@ class Command(BaseCommand): DomainInvitation( email=email_address.lower(), domain=domain, - status=DomainInvitation.INVITED, + status=DomainInvitation.DomainInvitationStatus.INVITED, ) ) logger.info("Creating %d invitations", len(to_create)) diff --git a/src/registrar/models/domain_invitation.py b/src/registrar/models/domain_invitation.py index 1e0b7fec8..395244df5 100644 --- a/src/registrar/models/domain_invitation.py +++ b/src/registrar/models/domain_invitation.py @@ -15,8 +15,11 @@ logger = logging.getLogger(__name__) class DomainInvitation(TimeStampedModel): - INVITED = "invited" - RETRIEVED = "retrieved" + + # Constants for status field + class DomainInvitationStatus(models.TextChoices): + INVITED = "invited", "Invited" + RETRIEVED = "retrieved", "Retrieved" email = models.EmailField( null=False, @@ -31,18 +34,15 @@ class DomainInvitation(TimeStampedModel): ) status = FSMField( - choices=[ - (INVITED, INVITED), - (RETRIEVED, RETRIEVED), - ], - default=INVITED, + choices=DomainInvitationStatus.choices, + default=DomainInvitationStatus.INVITED, protected=True, # can't alter state except through transition methods! ) def __str__(self): return f"Invitation for {self.email} on {self.domain} is {self.status}" - @transition(field="status", source=INVITED, target=RETRIEVED) + @transition(field="status", source=DomainInvitationStatus.INVITED, target=DomainInvitationStatus.RETRIEVED) def retrieve(self): """When an invitation is retrieved, create the corresponding permission. diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index 2daa3c253..346a97aa6 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -67,7 +67,7 @@ class User(AbstractUser): def check_domain_invitations_on_login(self): """When a user first arrives on the site, we need to retrieve any domain invitations that match their email address.""" - for invitation in DomainInvitation.objects.filter(email=self.email, status=DomainInvitation.INVITED): + for invitation in DomainInvitation.objects.filter(email=self.email, status=DomainInvitation.DomainInvitationStatus.INVITED): try: invitation.retrieve() invitation.save() diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 89967ee90..f459cab60 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -344,7 +344,7 @@ class AuditedAdminMockData: full_arg_dict = dict( email="test_mail@mail.com", domain=self.dummy_domain(item_name, True), - status=DomainInvitation.INVITED, + status=DomainInvitation.DomainInvitationStatus.INVITED, ) return full_arg_dict diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py index c2820696d..0a02ccb90 100644 --- a/src/registrar/tests/test_models.py +++ b/src/registrar/tests/test_models.py @@ -597,7 +597,7 @@ class TestInvitations(TestCase): # this is not an error but does produce a console warning with less_console_noise(): self.invitation.retrieve() - self.assertEqual(self.invitation.status, DomainInvitation.RETRIEVED) + self.assertEqual(self.invitation.status, DomainInvitation.DomainInvitationStatus.RETRIEVED) def test_retrieve_on_each_login(self): """A user's authenticate on_each_login callback retrieves their invitations.""" From a27eb7c3ae3ee54bd4583b61ad806407fd3bbaf6 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Tue, 5 Dec 2023 19:38:19 -0500 Subject: [PATCH 14/20] Fix tests for DomainInvitationAdmin --- src/registrar/tests/test_admin.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 5db01492a..8a67fc191 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -877,12 +877,14 @@ class DomainInvitationAdminTest(TestCase): ) # Assert that the filters are added - self.assertContains(response, "invited", count=4) - self.assertContains(response, "retrieved", count=4) + self.assertContains(response, "invited", count=2) + self.assertContains(response, "Invited", count=2) + self.assertContains(response, "retrieved", count=2) + self.assertContains(response, "Retrieved", count=2) # Check for the HTML context specificially - invited_html = 'invited' - retrieved_html = 'retrieved' + invited_html = 'Invited' + retrieved_html = 'Retrieved' self.assertContains(response, invited_html, count=1) self.assertContains(response, retrieved_html, count=1) From ebb257e6b0dc7e81f9e30cb4a0017df513ea64f9 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 6 Dec 2023 09:37:13 -0700 Subject: [PATCH 15/20] Fix fixtures --- src/registrar/fixtures_applications.py | 12 +++---- ...alter_domainapplication_status_and_more.py | 12 ++++++- .../0054_alter_domainapplication_status.py | 31 ------------------- src/registrar/models/domain_application.py | 4 +-- 4 files changed, 19 insertions(+), 40 deletions(-) delete mode 100644 src/registrar/migrations/0054_alter_domainapplication_status.py diff --git a/src/registrar/fixtures_applications.py b/src/registrar/fixtures_applications.py index 7f6a63719..69dfd0e50 100644 --- a/src/registrar/fixtures_applications.py +++ b/src/registrar/fixtures_applications.py @@ -49,27 +49,27 @@ class DomainApplicationFixture: # }, DA = [ { - "status": "started", + "status": DomainApplication.ApplicationStatus.STARTED, "organization_name": "Example - Finished but not submitted", }, { - "status": "submitted", + "status": DomainApplication.ApplicationStatus.SUBMITTED, "organization_name": "Example - Submitted but pending investigation", }, { - "status": "in review", + "status": DomainApplication.ApplicationStatus.IN_REVIEW, "organization_name": "Example - In investigation", }, { - "status": "in review", + "status": DomainApplication.ApplicationStatus.IN_REVIEW, "organization_name": "Example - Approved", }, { - "status": "withdrawn", + "status": DomainApplication.ApplicationStatus.WITHDRAWN, "organization_name": "Example - Withdrawn", }, { - "status": "action needed", + "status": DomainApplication.ApplicationStatus.ACTION_NEEDED, "organization_name": "Example - Action needed", }, { diff --git a/src/registrar/migrations/0053_alter_domain_state_alter_domainapplication_status_and_more.py b/src/registrar/migrations/0053_alter_domain_state_alter_domainapplication_status_and_more.py index 856ce7f0f..2abf31364 100644 --- a/src/registrar/migrations/0053_alter_domain_state_alter_domainapplication_status_and_more.py +++ b/src/registrar/migrations/0053_alter_domain_state_alter_domainapplication_status_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.7 on 2023-12-05 22:07 +# Generated by Django 4.2.7 on 2023-12-06 16:16 from django.db import migrations, models import django_fsm @@ -45,6 +45,16 @@ class Migration(migrations.Migration): max_length=50, ), ), + migrations.AlterField( + model_name="domaininvitation", + name="status", + field=django_fsm.FSMField( + choices=[("invited", "Invited"), ("retrieved", "Retrieved")], + default="invited", + max_length=50, + protected=True, + ), + ), migrations.AlterField( model_name="transitiondomain", name="status", diff --git a/src/registrar/migrations/0054_alter_domainapplication_status.py b/src/registrar/migrations/0054_alter_domainapplication_status.py deleted file mode 100644 index c0c427179..000000000 --- a/src/registrar/migrations/0054_alter_domainapplication_status.py +++ /dev/null @@ -1,31 +0,0 @@ -# Generated by Django 4.2.7 on 2023-12-05 23:14 - -from django.db import migrations -import django_fsm - - -class Migration(migrations.Migration): - dependencies = [ - ("registrar", "0053_alter_domain_state_alter_domainapplication_status_and_more"), - ] - - operations = [ - migrations.AlterField( - model_name="domainapplication", - name="status", - field=django_fsm.FSMField( - choices=[ - ("started", "Started"), - ("submitted", "Submitted"), - ("in_review", "In review"), - ("action_needed", "Action needed"), - ("approved", "Approved"), - ("withdrawn", "Withdrawn"), - ("rejected", "Rejected"), - ("ineligible", "Ineligible"), - ], - default="started", - max_length=50, - ), - ), - ] diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index 32cd239dc..8a56fb883 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -23,8 +23,8 @@ class DomainApplication(TimeStampedModel): class ApplicationStatus(models.TextChoices): STARTED = "started", "Started" SUBMITTED = "submitted", "Submitted" - IN_REVIEW = "in_review", "In review" - ACTION_NEEDED = "action_needed", "Action needed" + IN_REVIEW = "in review", "In review" + ACTION_NEEDED = "action needed", "Action needed" APPROVED = "approved", "Approved" WITHDRAWN = "withdrawn", "Withdrawn" REJECTED = "rejected", "Rejected" From 6e2bba5dc97cb2ec0a8d680c0c61ebc84f7c253f Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 7 Dec 2023 07:56:18 -0700 Subject: [PATCH 16/20] Fix migrations --- ...ter_domain_state_alter_domainapplication_status_and_more.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/registrar/migrations/{0053_alter_domain_state_alter_domainapplication_status_and_more.py => 0055_alter_domain_state_alter_domainapplication_status_and_more.py} (96%) diff --git a/src/registrar/migrations/0053_alter_domain_state_alter_domainapplication_status_and_more.py b/src/registrar/migrations/0055_alter_domain_state_alter_domainapplication_status_and_more.py similarity index 96% rename from src/registrar/migrations/0053_alter_domain_state_alter_domainapplication_status_and_more.py rename to src/registrar/migrations/0055_alter_domain_state_alter_domainapplication_status_and_more.py index 2abf31364..9b6bac48c 100644 --- a/src/registrar/migrations/0053_alter_domain_state_alter_domainapplication_status_and_more.py +++ b/src/registrar/migrations/0055_alter_domain_state_alter_domainapplication_status_and_more.py @@ -6,7 +6,7 @@ import django_fsm class Migration(migrations.Migration): dependencies = [ - ("registrar", "0052_alter_domainapplication_anything_else_and_more"), + ("registrar", "0054_alter_domainapplication_federal_agency_and_more"), ] operations = [ From b85f1e9d1b52bf619526fba8d68b27a5c065b759 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 8 Dec 2023 11:53:48 -0700 Subject: [PATCH 17/20] Linting --- src/registrar/fixtures_applications.py | 4 +++- src/registrar/models/domain_application.py | 27 ++++++++++++++++++---- src/registrar/models/domain_invitation.py | 1 - src/registrar/models/user.py | 4 +++- src/registrar/views/application.py | 6 ++++- 5 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/registrar/fixtures_applications.py b/src/registrar/fixtures_applications.py index 69dfd0e50..ad3ae0820 100644 --- a/src/registrar/fixtures_applications.py +++ b/src/registrar/fixtures_applications.py @@ -214,7 +214,9 @@ class DomainFixture(DomainApplicationFixture): for user in users: # approve one of each users in review status domains - application = DomainApplication.objects.filter(creator=user, status=DomainApplication.ApplicationStatus.IN_REVIEW).last() + application = DomainApplication.objects.filter( + creator=user, status=DomainApplication.ApplicationStatus.IN_REVIEW + ).last() logger.debug(f"Approving {application} for {user}") application.approve() application.save() diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index e810f5240..12eda4caf 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -18,7 +18,7 @@ logger = logging.getLogger(__name__) class DomainApplication(TimeStampedModel): """A registrant's application for a new domain.""" - + # Constants for choice fields class ApplicationStatus(models.TextChoices): STARTED = "started", "Started" @@ -583,7 +583,11 @@ class DomainApplication(TimeStampedModel): except EmailSendingError: logger.warning("Failed to send confirmation email", exc_info=True) - @transition(field="status", source=[ApplicationStatus.STARTED, ApplicationStatus.ACTION_NEEDED, ApplicationStatus.WITHDRAWN], target=ApplicationStatus.SUBMITTED) + @transition( + field="status", + source=[ApplicationStatus.STARTED, ApplicationStatus.ACTION_NEEDED, ApplicationStatus.WITHDRAWN], + target=ApplicationStatus.SUBMITTED, + ) def submit(self): """Submit an application that is started. @@ -621,7 +625,11 @@ class DomainApplication(TimeStampedModel): "emails/status_change_in_review_subject.txt", ) - @transition(field="status", source=[ApplicationStatus.IN_REVIEW, ApplicationStatus.REJECTED], target=ApplicationStatus.ACTION_NEEDED) + @transition( + field="status", + source=[ApplicationStatus.IN_REVIEW, ApplicationStatus.REJECTED], + target=ApplicationStatus.ACTION_NEEDED, + ) def action_needed(self): """Send back an application that is under investigation or rejected. @@ -635,7 +643,12 @@ class DomainApplication(TimeStampedModel): @transition( field="status", - source=[ApplicationStatus.SUBMITTED, ApplicationStatus.IN_REVIEW, ApplicationStatus.REJECTED, ApplicationStatus.INELIGIBLE], + source=[ + ApplicationStatus.SUBMITTED, + ApplicationStatus.IN_REVIEW, + ApplicationStatus.REJECTED, + ApplicationStatus.INELIGIBLE, + ], target=ApplicationStatus.APPROVED, ) def approve(self): @@ -669,7 +682,11 @@ class DomainApplication(TimeStampedModel): "emails/status_change_approved_subject.txt", ) - @transition(field="status", source=[ApplicationStatus.SUBMITTED, ApplicationStatus.IN_REVIEW], target=ApplicationStatus.WITHDRAWN) + @transition( + field="status", + source=[ApplicationStatus.SUBMITTED, ApplicationStatus.IN_REVIEW], + target=ApplicationStatus.WITHDRAWN, + ) def withdraw(self): """Withdraw an application that has been submitted.""" self._send_status_update_email( diff --git a/src/registrar/models/domain_invitation.py b/src/registrar/models/domain_invitation.py index 395244df5..12082142d 100644 --- a/src/registrar/models/domain_invitation.py +++ b/src/registrar/models/domain_invitation.py @@ -15,7 +15,6 @@ logger = logging.getLogger(__name__) class DomainInvitation(TimeStampedModel): - # Constants for status field class DomainInvitationStatus(models.TextChoices): INVITED = "invited", "Invited" diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index 346a97aa6..1f83870df 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -67,7 +67,9 @@ class User(AbstractUser): def check_domain_invitations_on_login(self): """When a user first arrives on the site, we need to retrieve any domain invitations that match their email address.""" - for invitation in DomainInvitation.objects.filter(email=self.email, status=DomainInvitation.DomainInvitationStatus.INVITED): + for invitation in DomainInvitation.objects.filter( + email=self.email, status=DomainInvitation.DomainInvitationStatus.INVITED + ): try: invitation.retrieve() invitation.save() diff --git a/src/registrar/views/application.py b/src/registrar/views/application.py index f6489bedf..bb1b3aee6 100644 --- a/src/registrar/views/application.py +++ b/src/registrar/views/application.py @@ -312,7 +312,11 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView): # if the current application has ApplicationStatus.ACTION_NEEDED status, this check should not be performed if self.application.status == DomainApplication.ApplicationStatus.ACTION_NEEDED: return [] - check_statuses = [DomainApplication.ApplicationStatus.SUBMITTED, DomainApplication.ApplicationStatus.IN_REVIEW, DomainApplication.ApplicationStatus.ACTION_NEEDED] + check_statuses = [ + DomainApplication.ApplicationStatus.SUBMITTED, + DomainApplication.ApplicationStatus.IN_REVIEW, + DomainApplication.ApplicationStatus.ACTION_NEEDED, + ] return DomainApplication.objects.filter(creator=self.request.user, status__in=check_statuses) def get_context_data(self): From 2b36f1891b04df5e5b048229fb900a91c2c513d1 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 8 Dec 2023 15:56:23 -0700 Subject: [PATCH 18/20] Pr suggestions --- src/registrar/assets/js/get-gov-admin.js | 19 ------------------- src/registrar/config/settings.py | 2 +- src/registrar/tests/test_reports.py | 2 +- 3 files changed, 2 insertions(+), 21 deletions(-) diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js index 7b92f1192..53eeb22a3 100644 --- a/src/registrar/assets/js/get-gov-admin.js +++ b/src/registrar/assets/js/get-gov-admin.js @@ -63,25 +63,6 @@ function openInNewTab(el, removeAttribute = false){ checkToListThenInitWidget('id_alternative_domains_to', 0); })(); -/** An IIFE to capitalize statuses in the Domain Application status dropdown -*/ -// (function (){ -// function capitalizeFirstLetterInDropdownOptions(dropdown_id) { -// // Grabs the status dropdown -// var selectElement = document.getElementById(dropdown_id); -// if (selectElement) { -// var options = selectElement.options; -// // Loop through each option, and convert to sentence case -// for (var i = 0; i < options.length; i++) { -// var option = options[i]; -// option.text = option.text.charAt(0).toUpperCase() + option.text.slice(1).toLowerCase(); -// } -// } -// } - -// capitalizeFirstLetterInDropdownOptions('id_status'); -// })(); - // Function to check for the existence of the "to" select list element in the DOM, and if and when found, // initialize the associated widget function checkToListThenInitWidget(toListId, attempts) { diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index 53ccd9f5d..cc779911a 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -79,7 +79,7 @@ secret_registry_cert = b64decode(secret("REGISTRY_CERT", "")) secret_registry_key = b64decode(secret("REGISTRY_KEY", "")) secret_registry_key_passphrase = secret("REGISTRY_KEY_PASSPHRASE", "") secret_registry_hostname = secret("REGISTRY_HOSTNAME") -# WILL REMOVE (TO PUSH TO SANDBOX) + # region: Basic Django Config-----------------------------------------------### # Build paths inside the project like this: BASE_DIR / "subdir". diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index b94316248..c9bbc3628 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -258,7 +258,7 @@ class ExportDataTest(TestCase): ) def tearDown(self): - # Dummy push - will remove + Domain.objects.all().delete() DomainInformation.objects.all().delete() User.objects.all().delete() From 21a5f9de70e9c0b0a225512abcd32f4910334a3a Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 11 Dec 2023 08:05:45 -0700 Subject: [PATCH 19/20] Update src/registrar/templates/home.html Co-authored-by: rachidatecs <107004823+rachidatecs@users.noreply.github.com> --- src/registrar/templates/home.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/templates/home.html b/src/registrar/templates/home.html index f3089dfd0..26668b1a9 100644 --- a/src/registrar/templates/home.html +++ b/src/registrar/templates/home.html @@ -111,7 +111,7 @@ {{ application.requested_domain.name|default:"New domain request" }} {{ application.created_at|date }} - {{ application.status|capfirst }} + {{ application.get_status_display }} {% if application.status == "started" or application.status == "action needed" or application.status == "withdrawn" %} From a2eb2c75a47b45fea76a78902623dc9d361bc82c Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 11 Dec 2023 08:32:51 -0700 Subject: [PATCH 20/20] Fix linting --- src/registrar/tests/test_reports.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index c9bbc3628..112b2ba34 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -258,7 +258,6 @@ class ExportDataTest(TestCase): ) def tearDown(self): - Domain.objects.all().delete() DomainInformation.objects.all().delete() User.objects.all().delete()