From ff4d8fd758906ca3e343cd99969833822fcd6d75 Mon Sep 17 00:00:00 2001 From: Kegan Maher Date: Tue, 22 Oct 2024 16:30:03 +0000 Subject: [PATCH] refactor(models): simplify template fields on EnrollmentFlow - compute some template fields by default - allow overriding templates --- ...sitagency_enrollmentflow_optionalfields.py | 52 +++++++++++++++++++ benefits/core/migrations/local_fixtures.json | 18 ++----- benefits/core/models.py | 35 ++++++++++--- tests/pytest/conftest.py | 7 +-- tests/pytest/core/test_models.py | 22 ++++++++ tests/pytest/eligibility/test_verify.py | 2 +- tests/pytest/eligibility/test_views.py | 6 +-- 7 files changed, 113 insertions(+), 29 deletions(-) diff --git a/benefits/core/migrations/0031_transitagency_enrollmentflow_optionalfields.py b/benefits/core/migrations/0031_transitagency_enrollmentflow_optionalfields.py index 9913e974a5..473e795fdc 100644 --- a/benefits/core/migrations/0031_transitagency_enrollmentflow_optionalfields.py +++ b/benefits/core/migrations/0031_transitagency_enrollmentflow_optionalfields.py @@ -81,4 +81,56 @@ class Migration(migrations.Migration): help_text="Used for URL navigation for this agency, e.g. the agency homepage url is /{slug}" ), ), + migrations.RenameField( + model_name="enrollmentflow", + old_name="eligibility_start_template", + new_name="eligibility_start_template_override", + ), + migrations.RenameField( + model_name="enrollmentflow", + old_name="enrollment_success_template", + new_name="enrollment_success_template_override", + ), + migrations.RenameField( + model_name="enrollmentflow", + old_name="selection_label_template", + new_name="selection_label_template_override", + ), + migrations.AlterField( + model_name="enrollmentflow", + name="system_name", + field=models.SlugField( + help_text="Primary internal system name for this EnrollmentFlow instance, e.g. in analytics and Eligibility API requests.", # noqa: E501 + ), + ), + migrations.AlterField( + model_name="enrollmentflow", + name="eligibility_start_template_override", + field=models.TextField( + blank=True, + default=None, + help_text="Override the default template for the informational page of this flow.", + null=True, + ), + ), + migrations.AlterField( + model_name="enrollmentflow", + name="enrollment_success_template_override", + field=models.TextField( + blank=True, + default="enrollment/success.html", + help_text="Override the default template for a successful enrollment associated with the enrollment flow", + null=True, + ), + ), + migrations.AlterField( + model_name="enrollmentflow", + name="selection_label_template_override", + field=models.TextField( + blank=True, + default=None, + help_text="Override the default template that defines the end-user UI for selecting this flow among other options.", # noqa + null=True, + ), + ), ] diff --git a/benefits/core/migrations/local_fixtures.json b/benefits/core/migrations/local_fixtures.json index 1b68a70218..368b2d7dd3 100644 --- a/benefits/core/migrations/local_fixtures.json +++ b/benefits/core/migrations/local_fixtures.json @@ -77,11 +77,8 @@ "system_name": "senior", "label": "Older Adult", "group_id": "group123", - "enrollment_success_template": "enrollment/success--cst.html", "display_order": 2, "claims_provider": 1, - "selection_label_template": "eligibility/includes/selection-label--senior.html", - "eligibility_start_template": "eligibility/start--senior.html", "claims_scope": "verify:senior", "claims_eligibility_claim": "senior", "supported_enrollment_methods": ["digital", "in_person"], @@ -95,11 +92,8 @@ "system_name": "veteran", "label": "U.S. Veteran", "group_id": "group123", - "enrollment_success_template": "enrollment/success--cst.html", "display_order": 4, "claims_provider": 1, - "selection_label_template": "eligibility/includes/selection-label--veteran.html", - "eligibility_start_template": "eligibility/start--veteran.html", "claims_scope": "verify:veteran", "claims_eligibility_claim": "veteran", "supported_enrollment_methods": ["digital", "in_person"], @@ -114,7 +108,7 @@ "label": "Agency Cardholder", "group_id": "group123", "enrollment_index_template": "enrollment/index--agency-card.html", - "enrollment_success_template": "enrollment/success--cst-agency-card.html", + "enrollment_success_template_override": "enrollment/success--cst-agency-card.html", "display_order": 4, "eligibility_api_url": "http://server:8000/verify", "eligibility_api_auth_header": "X-Server-API-Key", @@ -123,8 +117,8 @@ "eligibility_api_jwe_cek_enc": "A256CBC-HS512", "eligibility_api_jwe_encryption_alg": "RSA-OAEP", "eligibility_api_jws_signing_alg": "RS256", - "selection_label_template": "eligibility/includes/selection-label--cst-agency-card.html", - "eligibility_start_template": "eligibility/start--cst-agency-card.html", + "selection_label_template_override": "eligibility/includes/selection-label--cst-agency-card.html", + "eligibility_start_template_override": "eligibility/start--cst-agency-card.html", "eligibility_form_class": "benefits.eligibility.forms.CSTAgencyCard", "eligibility_unverified_template": "eligibility/unverified--cst-agency-card.html", "help_template": "core/includes/help--cst-agency-card.html", @@ -143,11 +137,8 @@ "expiration_days": 5, "expiration_reenrollment_days": 3, "reenrollment_error_template": "enrollment/reenrollment-error--calfresh.html", - "enrollment_success_template": "enrollment/success--cst.html", "display_order": 2, "claims_provider": 1, - "selection_label_template": "eligibility/includes/selection-label--calfresh.html", - "eligibility_start_template": "eligibility/start--calfresh.html", "help_template": "core/includes/help--calfresh.html", "claims_scope": "verify:calfresh", "claims_eligibility_claim": "calfresh", @@ -162,11 +153,8 @@ "system_name": "medicare", "label": "Medicare Cardholder", "group_id": "group123", - "enrollment_success_template": "enrollment/success--cst.html", "display_order": 1, "claims_provider": 2, - "selection_label_template": "eligibility/includes/selection-label--medicare.html", - "eligibility_start_template": "eligibility/start--medicare.html", "help_template": "core/includes/help--medicare.html", "claims_scope": "verify:medicare", "claims_eligibility_claim": "medicare", diff --git a/benefits/core/models.py b/benefits/core/models.py index 0f58233574..2410e4306f 100644 --- a/benefits/core/models.py +++ b/benefits/core/models.py @@ -296,7 +296,7 @@ class EnrollmentFlow(models.Model): """Represents a user journey through the Benefits app for a single eligibility type.""" id = models.AutoField(primary_key=True) - system_name = models.TextField( + system_name = models.SlugField( help_text="Primary internal system name for this EnrollmentFlow instance, e.g. in analytics and Eligibility API requests." # noqa: 501 ) display_order = models.PositiveSmallIntegerField(default=0, blank=False, null=False) @@ -359,11 +359,17 @@ class EnrollmentFlow(models.Model): blank=True, help_text="The JWS-compatible signing algorithm to use in Eligibility API requests for this flow.", ) - selection_label_template = models.TextField( - help_text="Path to a Django template that defines the end-user UI for selecting this flow among other options." + selection_label_template_override = models.TextField( + null=True, + blank=True, + default=None, + help_text="Override the default template that defines the end-user UI for selecting this flow among other options.", ) - eligibility_start_template = models.TextField( - default="eligibility/start.html", help_text="Path to a Django template for the informational page of this flow." + eligibility_start_template_override = models.TextField( + null=True, + blank=True, + default=None, + help_text="Override the default template for the informational page of this flow.", ) eligibility_form_class = models.TextField( null=True, @@ -402,8 +408,11 @@ class EnrollmentFlow(models.Model): reenrollment_error_template = models.TextField( null=True, blank=True, help_text="Template for a re-enrollment error associated with the enrollment flow" ) - enrollment_success_template = models.TextField( - default="enrollment/success.html", help_text="Template for a successful enrollment associated with the enrollment flow" + enrollment_success_template_override = models.TextField( + null=True, + blank=True, + default=None, + help_text="Override the default template for a successful enrollment associated with the enrollment flow", ) supported_enrollment_methods = MultiSelectField( choices=SUPPORTED_METHODS, @@ -432,6 +441,14 @@ def eligibility_api_public_key_data(self): """This flow's Eligibility API public key as a string.""" return self.eligibility_api_public_key.data + @property + def selection_label_template(self): + return self.selection_label_template_override or f"eligibility/includes/selection-label--{self.system_name}.html" + + @property + def eligibility_start_template(self): + return self.eligibility_start_template_override or f"eligibility/start--{self.system_name}.html" + @property def uses_claims_verification(self): """True if this flow verifies via the claims provider and has a scope and claim. False otherwise.""" @@ -448,6 +465,10 @@ def eligibility_verifier(self): else: return self.eligibility_api_url + @property + def enrollment_success_template(self): + return self.enrollment_success_template_override or f"enrollment/success--{self.transit_agency.slug}.html" + def eligibility_form_instance(self, *args, **kwargs): """Return an instance of this flow's EligibilityForm, or None.""" if not bool(self.eligibility_form_class): diff --git a/tests/pytest/conftest.py b/tests/pytest/conftest.py index 0959a50a9c..b88e1fd40c 100644 --- a/tests/pytest/conftest.py +++ b/tests/pytest/conftest.py @@ -78,11 +78,12 @@ def model_ClaimsProvider_no_sign_out(model_ClaimsProvider): @pytest.fixture def model_EnrollmentFlow(model_TransitAgency): flow = EnrollmentFlow.objects.create( - system_name="Test Flow", - selection_label_template="eligibility/includes/selection-label.html", + system_name="test", + selection_label_template_override="eligibility/includes/selection-label.html", + eligibility_start_template_override="eligibility/start.html", label="Test flow label", group_id="group123", - enrollment_success_template="enrollment/success.html", + enrollment_success_template_override="enrollment/success.html", transit_agency=model_TransitAgency, ) diff --git a/tests/pytest/core/test_models.py b/tests/pytest/core/test_models.py index f0bad35aba..f6ce89a7e1 100644 --- a/tests/pytest/core/test_models.py +++ b/tests/pytest/core/test_models.py @@ -311,6 +311,28 @@ def test_EnrollmentFlow_with_claims_scheme(model_EnrollmentFlow_with_claims_sche assert model_EnrollmentFlow_with_claims_scheme.claims_scheme == "scheme" +@pytest.mark.django_db +def test_EnrollmentFlow_template_overrides(model_EnrollmentFlow): + assert model_EnrollmentFlow.selection_label_template == model_EnrollmentFlow.selection_label_template_override + assert model_EnrollmentFlow.eligibility_start_template == model_EnrollmentFlow.eligibility_start_template_override + assert model_EnrollmentFlow.enrollment_success_template == model_EnrollmentFlow.enrollment_success_template_override + + model_EnrollmentFlow.selection_label_template_override = None + model_EnrollmentFlow.eligibility_start_template_override = None + model_EnrollmentFlow.enrollment_success_template_override = None + model_EnrollmentFlow.save() + + assert ( + model_EnrollmentFlow.selection_label_template + == f"eligibility/includes/selection-label--{model_EnrollmentFlow.system_name}.html" + ) + assert model_EnrollmentFlow.eligibility_start_template == f"eligibility/start--{model_EnrollmentFlow.system_name}.html" + assert ( + model_EnrollmentFlow.enrollment_success_template + == f"enrollment/success--{model_EnrollmentFlow.transit_agency.slug}.html" + ) + + @pytest.mark.django_db def test_TransitProcessor_str(model_TransitProcessor): assert str(model_TransitProcessor) == model_TransitProcessor.name diff --git a/tests/pytest/eligibility/test_verify.py b/tests/pytest/eligibility/test_verify.py index fcb2c3d73b..a43e45473f 100644 --- a/tests/pytest/eligibility/test_verify.py +++ b/tests/pytest/eligibility/test_verify.py @@ -31,7 +31,7 @@ def test_eligibility_from_api_error( def test_eligibility_from_api_verified_types( mocker, model_TransitAgency, model_EnrollmentFlow_with_eligibility_api, mock_api_client_verify, form ): - verified_types = ["Test Flow"] + verified_types = ["test"] api_response = mocker.Mock(eligibility=verified_types, error=None) mock_api_client_verify.return_value = api_response diff --git a/tests/pytest/eligibility/test_views.py b/tests/pytest/eligibility/test_views.py index f3e5e3c6fb..3c0d9e1de0 100644 --- a/tests/pytest/eligibility/test_views.py +++ b/tests/pytest/eligibility/test_views.py @@ -78,19 +78,19 @@ def test_index_filtering_flows(mocker, model_TransitAgency, client): transit_agency=model_TransitAgency, supported_enrollment_methods=[models.EnrollmentMethods.DIGITAL], label="Digital", - selection_label_template="eligibility/includes/selection-label.html", + selection_label_template_override="eligibility/includes/selection-label.html", ) in_person = models.EnrollmentFlow.objects.create( transit_agency=model_TransitAgency, supported_enrollment_methods=[models.EnrollmentMethods.IN_PERSON], label="In-Person", - selection_label_template="eligibility/includes/selection-label.html", + selection_label_template_override="eligibility/includes/selection-label.html", ) both = models.EnrollmentFlow.objects.create( transit_agency=model_TransitAgency, supported_enrollment_methods=[models.EnrollmentMethods.DIGITAL, models.EnrollmentMethods.IN_PERSON], label="Both", - selection_label_template="eligibility/includes/selection-label.html", + selection_label_template_override="eligibility/includes/selection-label.html", ) mocker.patch("benefits.core.session.agency", autospec=True, return_value=model_TransitAgency)