mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-27 21:16:28 +02:00
Be explicit when swapping between enum / portfolio types
Centralizes all config options at the start of the class
This commit is contained in:
parent
9a2e031979
commit
221e236775
3 changed files with 138 additions and 124 deletions
|
@ -279,11 +279,11 @@ class BaseYesNoForm(RegistrarForm):
|
|||
return initial_value
|
||||
|
||||
|
||||
def request_step_list(request_wizard):
|
||||
def request_step_list(request_wizard, step_enum):
|
||||
"""Dynamically generated list of steps in the form wizard."""
|
||||
step_list = []
|
||||
for step in request_wizard.StepEnum:
|
||||
condition = request_wizard.WIZARD_CONDITIONS.get(step, True)
|
||||
for step in step_enum:
|
||||
condition = request_wizard.wizard_conditions.get(step, True)
|
||||
if callable(condition):
|
||||
condition = condition(request_wizard)
|
||||
if condition:
|
||||
|
|
|
@ -43,7 +43,7 @@ class DomainRequestTests(TestWithUser, WebTest):
|
|||
super().setUp()
|
||||
self.federal_agency, _ = FederalAgency.objects.get_or_create(agency="General Services Administration")
|
||||
self.app.set_user(self.user.username)
|
||||
self.TITLES = DomainRequestWizard.TITLES
|
||||
self.TITLES = DomainRequestWizard.titles
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
|
@ -3186,7 +3186,7 @@ class TestPortfolioDomainRequestViewonly(TestWithUser, WebTest):
|
|||
super().setUp()
|
||||
self.federal_agency, _ = FederalAgency.objects.get_or_create(agency="General Services Administration")
|
||||
self.app.set_user(self.user.username)
|
||||
self.TITLES = DomainRequestWizard.TITLES
|
||||
self.TITLES = DomainRequestWizard.titles
|
||||
|
||||
def tearDown(self):
|
||||
super().tearDown()
|
||||
|
|
|
@ -43,9 +43,7 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
|
|||
although not without consulting the base implementation, first.
|
||||
"""
|
||||
|
||||
StepEnum = Step # type: ignore
|
||||
template_name = ""
|
||||
|
||||
is_portfolio = False
|
||||
|
||||
# uniquely namespace the wizard in urls.py
|
||||
|
@ -56,42 +54,136 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
|
|||
# name for accessing /domain-request/<id>/edit
|
||||
EDIT_URL_NAME = "edit-domain-request"
|
||||
NEW_URL_NAME = "/request/"
|
||||
|
||||
# region: Titles
|
||||
# We need to pass our human-readable step titles as context to the templates.
|
||||
TITLES = {
|
||||
StepEnum.ORGANIZATION_TYPE: _("Type of organization"),
|
||||
StepEnum.TRIBAL_GOVERNMENT: _("Tribal government"),
|
||||
StepEnum.ORGANIZATION_FEDERAL: _("Federal government branch"),
|
||||
StepEnum.ORGANIZATION_ELECTION: _("Election office"),
|
||||
StepEnum.ORGANIZATION_CONTACT: _("Organization"),
|
||||
StepEnum.ABOUT_YOUR_ORGANIZATION: _("About your organization"),
|
||||
StepEnum.SENIOR_OFFICIAL: _("Senior official"),
|
||||
StepEnum.CURRENT_SITES: _("Current websites"),
|
||||
StepEnum.DOTGOV_DOMAIN: _(".gov domain"),
|
||||
StepEnum.PURPOSE: _("Purpose of your domain"),
|
||||
StepEnum.OTHER_CONTACTS: _("Other employees from your organization"),
|
||||
StepEnum.ADDITIONAL_DETAILS: _("Additional details"),
|
||||
StepEnum.REQUIREMENTS: _("Requirements for operating a .gov domain"),
|
||||
StepEnum.REVIEW: _("Review and submit your domain request"),
|
||||
REGULAR_TITLES = {
|
||||
Step.ORGANIZATION_TYPE: _("Type of organization"),
|
||||
Step.TRIBAL_GOVERNMENT: _("Tribal government"),
|
||||
Step.ORGANIZATION_FEDERAL: _("Federal government branch"),
|
||||
Step.ORGANIZATION_ELECTION: _("Election office"),
|
||||
Step.ORGANIZATION_CONTACT: _("Organization"),
|
||||
Step.ABOUT_YOUR_ORGANIZATION: _("About your organization"),
|
||||
Step.SENIOR_OFFICIAL: _("Senior official"),
|
||||
Step.CURRENT_SITES: _("Current websites"),
|
||||
Step.DOTGOV_DOMAIN: _(".gov domain"),
|
||||
Step.PURPOSE: _("Purpose of your domain"),
|
||||
Step.OTHER_CONTACTS: _("Other employees from your organization"),
|
||||
Step.ADDITIONAL_DETAILS: _("Additional details"),
|
||||
Step.REQUIREMENTS: _("Requirements for operating a .gov domain"),
|
||||
Step.REVIEW: _("Review and submit your domain request"),
|
||||
}
|
||||
|
||||
# Titles for the portfolio context
|
||||
PORTFOLIO_TITLES = {
|
||||
PortfolioDomainRequestStep.REQUESTING_ENTITY: _("Requesting entity"),
|
||||
PortfolioDomainRequestStep.CURRENT_SITES: _("Current websites"),
|
||||
PortfolioDomainRequestStep.DOTGOV_DOMAIN: _(".gov domain"),
|
||||
PortfolioDomainRequestStep.PURPOSE: _("Purpose of your domain"),
|
||||
PortfolioDomainRequestStep.ADDITIONAL_DETAILS: _("Additional details"),
|
||||
PortfolioDomainRequestStep.REQUIREMENTS: _("Requirements for operating a .gov domain"),
|
||||
PortfolioDomainRequestStep.REVIEW: _("Review and submit your domain request"),
|
||||
}
|
||||
# endregion
|
||||
|
||||
# region: Wizard conditions
|
||||
# We can use a dictionary with step names and callables that return booleans
|
||||
# to show or hide particular steps based on the state of the process.
|
||||
WIZARD_CONDITIONS = {
|
||||
StepEnum.ORGANIZATION_FEDERAL: lambda w: w.from_model("show_organization_federal", False),
|
||||
StepEnum.TRIBAL_GOVERNMENT: lambda w: w.from_model("show_tribal_government", False),
|
||||
StepEnum.ORGANIZATION_ELECTION: lambda w: w.from_model("show_organization_election", False),
|
||||
StepEnum.ABOUT_YOUR_ORGANIZATION: lambda w: w.from_model("show_about_your_organization", False),
|
||||
REGULAR_WIZARD_CONDITIONS = {
|
||||
Step.ORGANIZATION_FEDERAL: lambda w: w.from_model("show_organization_federal", False),
|
||||
Step.TRIBAL_GOVERNMENT: lambda w: w.from_model("show_tribal_government", False),
|
||||
Step.ORGANIZATION_ELECTION: lambda w: w.from_model("show_organization_election", False),
|
||||
Step.ABOUT_YOUR_ORGANIZATION: lambda w: w.from_model("show_about_your_organization", False),
|
||||
}
|
||||
|
||||
PORTFOLIO_WIZARD_CONDITIONS = {}
|
||||
# endregion
|
||||
|
||||
# region: Unlocking steps
|
||||
# The conditions by which each step is "unlocked" or "locked"
|
||||
REGULAR_UNLOCKING_STEPS = {
|
||||
Step.ORGANIZATION_TYPE: lambda self: self.domain_request.generic_org_type is not None,
|
||||
Step.TRIBAL_GOVERNMENT: lambda self: self.domain_request.tribe_name is not None,
|
||||
Step.ORGANIZATION_FEDERAL: lambda self: self.domain_request.federal_type is not None,
|
||||
Step.ORGANIZATION_ELECTION: lambda self: self.domain_request.is_election_board is not None,
|
||||
Step.ORGANIZATION_CONTACT: lambda self: (
|
||||
self.domain_request.federal_agency is not None
|
||||
or self.domain_request.organization_name is not None
|
||||
or self.domain_request.address_line1 is not None
|
||||
or self.domain_request.city is not None
|
||||
or self.domain_request.state_territory is not None
|
||||
or self.domain_request.zipcode is not None
|
||||
or self.domain_request.urbanization is not None
|
||||
),
|
||||
Step.ABOUT_YOUR_ORGANIZATION: lambda self: self.domain_request.about_your_organization is not None,
|
||||
Step.SENIOR_OFFICIAL: lambda self: self.domain_request.senior_official is not None,
|
||||
Step.CURRENT_SITES: lambda self: (
|
||||
self.domain_request.current_websites.exists() or self.domain_request.requested_domain is not None
|
||||
),
|
||||
Step.DOTGOV_DOMAIN: lambda self: self.domain_request.requested_domain is not None,
|
||||
Step.PURPOSE: lambda self: self.domain_request.purpose is not None,
|
||||
Step.OTHER_CONTACTS: lambda self: (
|
||||
self.domain_request.other_contacts.exists()
|
||||
or self.domain_request.no_other_contacts_rationale is not None
|
||||
),
|
||||
Step.ADDITIONAL_DETAILS: lambda self: (
|
||||
# Additional details is complete as long as "has anything else" and "has cisa rep" are not None
|
||||
(
|
||||
self.domain_request.has_anything_else_text is not None
|
||||
and self.domain_request.has_cisa_representative is not None
|
||||
)
|
||||
),
|
||||
Step.REQUIREMENTS: lambda self: self.domain_request.is_policy_acknowledged is not None,
|
||||
Step.REVIEW: lambda self: self.domain_request.is_policy_acknowledged is not None,
|
||||
}
|
||||
|
||||
PORTFOLIO_UNLOCKING_STEPS = {
|
||||
PortfolioDomainRequestStep.REQUESTING_ENTITY: lambda self: self.domain_request.organization_name is not None,
|
||||
PortfolioDomainRequestStep.CURRENT_SITES: lambda self: (
|
||||
self.domain_request.current_websites.exists() or self.domain_request.requested_domain is not None
|
||||
),
|
||||
PortfolioDomainRequestStep.DOTGOV_DOMAIN: lambda self: self.domain_request.requested_domain is not None,
|
||||
PortfolioDomainRequestStep.PURPOSE: lambda self: self.domain_request.purpose is not None,
|
||||
PortfolioDomainRequestStep.ADDITIONAL_DETAILS: lambda self: self.domain_request.anything_else is not None,
|
||||
PortfolioDomainRequestStep.REQUIREMENTS: lambda self: self.domain_request.is_policy_acknowledged is not None,
|
||||
PortfolioDomainRequestStep.REVIEW: lambda self: self.domain_request.is_policy_acknowledged is not None,
|
||||
}
|
||||
# endregion
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.steps = StepsHelper(self)
|
||||
self.configure_step_options()
|
||||
self._domain_request = None # for caching
|
||||
|
||||
def configure_step_options(self):
|
||||
"""Changes which steps are available to the user based on self.is_portfolio.
|
||||
This may change on the fly, so we need to evaluate it on the fly.
|
||||
|
||||
Using this information, we then set three configuration variables.
|
||||
- self.titles => Returns the page titles for each step
|
||||
- self.wizard_conditions => Conditionally shows / hides certain steps
|
||||
- self.unlocking_steps => Determines what steps are locked/unlocked
|
||||
|
||||
Then, we create self.steps.
|
||||
"""
|
||||
if self.is_portfolio:
|
||||
self.titles = self.PORTFOLIO_TITLES
|
||||
self.wizard_conditions = self.PORTFOLIO_WIZARD_CONDITIONS
|
||||
self.unlocking_steps = self.PORTFOLIO_UNLOCKING_STEPS
|
||||
else:
|
||||
self.titles = self.REGULAR_TITLES
|
||||
self.wizard_conditions = self.REGULAR_WIZARD_CONDITIONS
|
||||
self.unlocking_steps = self.REGULAR_UNLOCKING_STEPS
|
||||
self.steps = StepsHelper(self)
|
||||
|
||||
def has_pk(self):
|
||||
"""Does this wizard know about a DomainRequest database record?"""
|
||||
return "domain_request_id" in self.storage
|
||||
|
||||
def get_step_enum(self):
|
||||
"""Determines which step enum we should use for the wizard"""
|
||||
return PortfolioDomainRequestStep if self.is_portfolio else Step
|
||||
|
||||
@property
|
||||
def prefix(self):
|
||||
"""Namespace the wizard to avoid clashes in session variable names."""
|
||||
|
@ -196,29 +288,12 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
|
|||
else:
|
||||
return default
|
||||
|
||||
def mark_as_portfolio_wizard(self):
|
||||
"""Swaps the wizard over to the "portfolio" view"""
|
||||
self.is_portfolio = True
|
||||
self.StepEnum = PortfolioDomainRequestStep # type: ignore
|
||||
self.TITLES = {
|
||||
self.StepEnum.REQUESTING_ENTITY: _("Requesting entity"),
|
||||
self.StepEnum.CURRENT_SITES: _("Current websites"),
|
||||
self.StepEnum.DOTGOV_DOMAIN: _(".gov domain"),
|
||||
self.StepEnum.PURPOSE: _("Purpose of your domain"),
|
||||
self.StepEnum.ADDITIONAL_DETAILS: _("Additional details"),
|
||||
self.StepEnum.REQUIREMENTS: _("Requirements for operating a .gov domain"),
|
||||
self.StepEnum.REVIEW: _("Review and submit your domain request"),
|
||||
}
|
||||
self.WIZARD_CONDITIONS = {}
|
||||
|
||||
# Regenerate the steps helper
|
||||
self.steps = StepsHelper(self)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""This method handles GET requests."""
|
||||
|
||||
if not self.is_portfolio and self.request.user.is_org_user(request):
|
||||
self.mark_as_portfolio_wizard()
|
||||
self.is_portfolio = True
|
||||
self.configure_step_options()
|
||||
|
||||
current_url = resolve(request.path_info).url_name
|
||||
|
||||
|
@ -351,68 +426,9 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
|
|||
return DomainRequest.objects.filter(creator=self.request.user, status__in=check_statuses)
|
||||
|
||||
def db_check_for_unlocking_steps(self):
|
||||
"""Helper for get_context_data
|
||||
|
||||
"""Helper for get_context_data.
|
||||
Queries the DB for a domain request and returns a list of unlocked steps."""
|
||||
|
||||
# The way this works is as follows:
|
||||
# Each step is assigned a true/false value to determine if it is
|
||||
# "unlocked" or not. This dictionary of values is looped through
|
||||
# at the end of this function and any step with a "true" value is
|
||||
# added to a simple array that is returned at the end of this function.
|
||||
# This array is eventually passed to the frontend context (eg. domain_request_sidebar.html),
|
||||
# and is used to determine how steps appear in the side nav.
|
||||
# It is worth noting that any step assigned "false" here will be EXCLUDED
|
||||
# from the list of "unlocked" steps.
|
||||
if self.is_portfolio:
|
||||
history_dict = {
|
||||
self.StepEnum.REQUESTING_ENTITY: self.domain_request.organization_name is not None,
|
||||
self.StepEnum.CURRENT_SITES: (
|
||||
self.domain_request.current_websites.exists() or self.domain_request.requested_domain is not None
|
||||
),
|
||||
self.StepEnum.DOTGOV_DOMAIN: self.domain_request.requested_domain is not None,
|
||||
self.StepEnum.PURPOSE: self.domain_request.purpose is not None,
|
||||
self.StepEnum.ADDITIONAL_DETAILS: self.domain_request.anything_else is not None,
|
||||
self.StepEnum.REQUIREMENTS: self.domain_request.is_policy_acknowledged is not None,
|
||||
self.StepEnum.REVIEW: self.domain_request.is_policy_acknowledged is not None,
|
||||
}
|
||||
else:
|
||||
history_dict = {
|
||||
self.StepEnum.ORGANIZATION_TYPE: self.domain_request.generic_org_type is not None,
|
||||
self.StepEnum.TRIBAL_GOVERNMENT: self.domain_request.tribe_name is not None,
|
||||
self.StepEnum.ORGANIZATION_FEDERAL: self.domain_request.federal_type is not None,
|
||||
self.StepEnum.ORGANIZATION_ELECTION: self.domain_request.is_election_board is not None,
|
||||
self.StepEnum.ORGANIZATION_CONTACT: (
|
||||
self.domain_request.federal_agency is not None
|
||||
or self.domain_request.organization_name is not None
|
||||
or self.domain_request.address_line1 is not None
|
||||
or self.domain_request.city is not None
|
||||
or self.domain_request.state_territory is not None
|
||||
or self.domain_request.zipcode is not None
|
||||
or self.domain_request.urbanization is not None
|
||||
),
|
||||
self.StepEnum.ABOUT_YOUR_ORGANIZATION: self.domain_request.about_your_organization is not None,
|
||||
self.StepEnum.SENIOR_OFFICIAL: self.domain_request.senior_official is not None,
|
||||
self.StepEnum.CURRENT_SITES: (
|
||||
self.domain_request.current_websites.exists() or self.domain_request.requested_domain is not None
|
||||
),
|
||||
self.StepEnum.DOTGOV_DOMAIN: self.domain_request.requested_domain is not None,
|
||||
self.StepEnum.PURPOSE: self.domain_request.purpose is not None,
|
||||
self.StepEnum.OTHER_CONTACTS: (
|
||||
self.domain_request.other_contacts.exists()
|
||||
or self.domain_request.no_other_contacts_rationale is not None
|
||||
),
|
||||
self.StepEnum.ADDITIONAL_DETAILS: (
|
||||
# Additional details is complete as long as "has anything else" and "has cisa rep" are not None
|
||||
(
|
||||
self.domain_request.has_anything_else_text is not None
|
||||
and self.domain_request.has_cisa_representative is not None
|
||||
)
|
||||
),
|
||||
self.StepEnum.REQUIREMENTS: self.domain_request.is_policy_acknowledged is not None,
|
||||
self.StepEnum.REVIEW: self.domain_request.is_policy_acknowledged is not None,
|
||||
}
|
||||
return [key for key, value in history_dict.items() if value]
|
||||
return [key for key, is_unlocked_checker in self.unlocking_steps.items() if is_unlocked_checker(self)]
|
||||
|
||||
def get_context_data(self):
|
||||
"""Define context for access on all wizard pages."""
|
||||
|
@ -426,7 +442,7 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
|
|||
modal_button = '<button type="submit" ' 'class="usa-button" ' ">Submit request</button>"
|
||||
context_stuff = {
|
||||
"not_form": False,
|
||||
"form_titles": self.TITLES,
|
||||
"form_titles": self.titles,
|
||||
"steps": self.steps,
|
||||
"visited": self.storage.get("step_history", []),
|
||||
"is_federal": self.domain_request.is_federal(),
|
||||
|
@ -443,7 +459,7 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
|
|||
modal_button = '<button type="button" class="usa-button" data-close-modal>Return to request</button>'
|
||||
context_stuff = {
|
||||
"not_form": True,
|
||||
"form_titles": self.TITLES,
|
||||
"form_titles": self.titles,
|
||||
"steps": self.steps,
|
||||
"visited": self.storage.get("step_history", []),
|
||||
"is_federal": self.domain_request.is_federal(),
|
||||
|
@ -464,7 +480,7 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
|
|||
|
||||
def get_step_list(self) -> list:
|
||||
"""Dynamically generated list of steps in the form wizard."""
|
||||
return request_step_list(self)
|
||||
return request_step_list(self, self.get_step_enum())
|
||||
|
||||
def goto(self, step):
|
||||
if step == "generic_org_type" or step == "portfolio_requesting_entity":
|
||||
|
@ -491,7 +507,8 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
|
|||
def post(self, request, *args, **kwargs) -> HttpResponse:
|
||||
"""This method handles POST requests."""
|
||||
if not self.is_portfolio and self.request.user.is_org_user(request): # type: ignore
|
||||
self.mark_as_portfolio_wizard()
|
||||
self.is_portfolio = True
|
||||
self.configure_step_options()
|
||||
|
||||
# which button did the user press?
|
||||
button: str = request.POST.get("submit_button", "")
|
||||
|
@ -554,16 +571,13 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
|
|||
|
||||
# TODO - this is a WIP until the domain request experience for portfolios is complete
|
||||
class PortfolioDomainRequestWizard(DomainRequestWizard):
|
||||
StepEnum: PortfolioDomainRequestStep = PortfolioDomainRequestStep # type: ignore
|
||||
|
||||
TITLES: dict = {
|
||||
StepEnum.REQUESTING_ENTITY: _("Requesting entity"),
|
||||
StepEnum.CURRENT_SITES: _("Current websites"),
|
||||
StepEnum.DOTGOV_DOMAIN: _(".gov domain"),
|
||||
StepEnum.PURPOSE: _("Purpose of your domain"),
|
||||
StepEnum.ADDITIONAL_DETAILS: _("Additional details"),
|
||||
StepEnum.REQUIREMENTS: _("Requirements for operating a .gov domain"),
|
||||
# Step.REVIEW: _("Review and submit your domain request"),
|
||||
PortfolioDomainRequestStep.REQUESTING_ENTITY: _("Requesting entity"),
|
||||
PortfolioDomainRequestStep.CURRENT_SITES: _("Current websites"),
|
||||
PortfolioDomainRequestStep.DOTGOV_DOMAIN: _(".gov domain"),
|
||||
PortfolioDomainRequestStep.PURPOSE: _("Purpose of your domain"),
|
||||
PortfolioDomainRequestStep.ADDITIONAL_DETAILS: _("Additional details"),
|
||||
PortfolioDomainRequestStep.REQUIREMENTS: _("Requirements for operating a .gov domain"),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
|
@ -764,7 +778,7 @@ class Review(DomainRequestWizard):
|
|||
if DomainRequest._form_complete(self.domain_request, self.request) is False:
|
||||
logger.warning("User arrived at review page with an incomplete form.")
|
||||
context = super().get_context_data()
|
||||
context["Step"] = self.StepEnum.__members__
|
||||
context["Step"] = self.get_step_enum().__members__
|
||||
context["domain_request"] = self.domain_request
|
||||
return context
|
||||
|
||||
|
@ -965,8 +979,8 @@ class PortfolioDomainRequestStatusViewOnly(DomainRequestPortfolioViewonlyView):
|
|||
# Create a temp wizard object to grab the step list
|
||||
wizard = PortfolioDomainRequestWizard()
|
||||
wizard.request = self.request
|
||||
context["Step"] = wizard.StepEnum.__members__
|
||||
context["steps"] = request_step_list(wizard)
|
||||
context["Step"] = PortfolioDomainRequestStep.__members__
|
||||
context["steps"] = request_step_list(wizard, PortfolioDomainRequestStep)
|
||||
context["form_titles"] = wizard.TITLES
|
||||
return context
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue