Compressing Nicolle and Alysia migrations with mine to be the one migration that rules them all

This commit is contained in:
Rebecca Hsieh 2023-09-13 15:28:01 -07:00
parent 46acc8382f
commit 6965d593f5
No known key found for this signature in database
GPG key ID: 644527A2F375A379
21 changed files with 204 additions and 284 deletions

View file

@ -484,8 +484,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
"federal_agency",
"federal_type",
"is_election_board",
"type_of_work",
"more_organization_information",
"about_your_organization",
]
},
),
@ -523,8 +522,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
# Read only that we'll leverage for CISA Analysts
analyst_readonly_fields = [
"creator",
"type_of_work",
"more_organization_information",
"about_your_organization",
"address_line1",
"address_line2",
"zipcode",

View file

@ -27,7 +27,7 @@ for step, view in [
(Step.ORGANIZATION_FEDERAL, views.OrganizationFederal),
(Step.ORGANIZATION_ELECTION, views.OrganizationElection),
(Step.ORGANIZATION_CONTACT, views.OrganizationContact),
(Step.TYPE_OF_WORK, views.TypeOfWork),
(Step.ABOUT_YOUR_ORGANIZATION, views.AboutYourOrganization),
(Step.AUTHORIZING_OFFICIAL, views.AuthorizingOfficial),
(Step.CURRENT_SITES, views.CurrentSites),
(Step.DOTGOV_DOMAIN, views.DotgovDomain),

View file

@ -310,28 +310,9 @@ class OrganizationContactForm(RegistrarForm):
return federal_agency
class TypeOfWorkForm(RegistrarForm):
type_of_work = forms.CharField(
# label has to end in a space to get the label_suffix to show
label="What type of work does your organization do? ",
widget=forms.Textarea(),
validators=[
MaxLengthValidator(
1000,
message="Response must be less than 1000 characters.",
)
],
error_messages={"required": "Enter the type of work your organization does."},
)
more_organization_information = forms.CharField(
# label has to end in a space to get the label_suffix to show
label=(
"Describe how your organization is a government organization that is"
" independent of a state government. Include links to authorizing"
" legislation, applicable bylaws or charter, or other documentation to"
" support your claims. "
),
class AboutYourOrganizationForm(RegistrarForm):
about_your_organization = forms.CharField(
label="About your organization",
widget=forms.Textarea(),
validators=[
MaxLengthValidator(
@ -340,9 +321,7 @@ class TypeOfWorkForm(RegistrarForm):
)
],
error_messages={
"required": (
"Describe how your organization is independent of a state government."
)
"required": ("Enter more information about your organization.")
},
)

View file

@ -1,30 +0,0 @@
# Generated by Django 4.2.1 on 2023-09-07 17:53
from django.db import migrations
import django_fsm
class Migration(migrations.Migration):
dependencies = [
("registrar", "0030_alter_user_status"),
]
operations = [
migrations.AlterField(
model_name="domain",
name="state",
field=django_fsm.FSMField(
choices=[
("created", "Created"),
("deleted", "Deleted"),
("unknown", "Unknown"),
("ready", "Ready"),
("onhold", "Onhold"),
],
default="unknown",
help_text="Very basic info about the lifecycle of this domain object",
max_length=21,
protected=True,
),
),
]

View file

@ -1,60 +0,0 @@
# Generated by Django 4.2.1 on 2023-09-11 14:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("registrar", "0030_alter_user_status"),
]
operations = [
migrations.CreateModel(
name="TransitionDomain",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"username",
models.TextField(
help_text="Username - this will be an email address",
verbose_name="Username",
),
),
(
"domain_name",
models.TextField(blank=True, null=True, verbose_name="Domain name"),
),
(
"status",
models.CharField(
blank=True,
choices=[("created", "Created"), ("hold", "Hold")],
help_text="domain status during the transfer",
max_length=255,
verbose_name="Status",
),
),
(
"email_sent",
models.BooleanField(
default=False,
help_text="indicates whether email was sent",
verbose_name="email sent",
),
),
],
options={
"abstract": False,
},
),
]

View file

@ -0,0 +1,122 @@
# Generated by Django 4.2.1 on 2023-09-13 22:25
from django.db import migrations, models
import django_fsm
class Migration(migrations.Migration):
dependencies = [
("registrar", "0030_alter_user_status"),
]
operations = [
migrations.CreateModel(
name="TransitionDomain",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"username",
models.TextField(
help_text="Username - this will be an email address",
verbose_name="Username",
),
),
(
"domain_name",
models.TextField(blank=True, null=True, verbose_name="Domain name"),
),
(
"status",
models.CharField(
blank=True,
choices=[("created", "Created"), ("hold", "Hold")],
help_text="domain status during the transfer",
max_length=255,
verbose_name="Status",
),
),
(
"email_sent",
models.BooleanField(
default=False,
help_text="indicates whether email was sent",
verbose_name="email sent",
),
),
],
options={
"abstract": False,
},
),
migrations.RemoveField(
model_name="domainapplication",
name="more_organization_information",
),
migrations.RemoveField(
model_name="domainapplication",
name="type_of_work",
),
migrations.RemoveField(
model_name="domaininformation",
name="more_organization_information",
),
migrations.RemoveField(
model_name="domaininformation",
name="type_of_work",
),
migrations.AddField(
model_name="domainapplication",
name="about_your_organization",
field=models.TextField(
blank=True, help_text="Information about your organization", null=True
),
),
migrations.AddField(
model_name="domaininformation",
name="about_your_organization",
field=models.TextField(
blank=True, help_text="Information about your organization", null=True
),
),
migrations.AlterField(
model_name="domain",
name="state",
field=django_fsm.FSMField(
choices=[
("unknown", "Unknown"),
("dns needed", "Dns Needed"),
("ready", "Ready"),
("on hold", "On Hold"),
("deleted", "Deleted"),
],
default="unknown",
help_text="Very basic info about the lifecycle of this domain object",
max_length=21,
protected=True,
),
),
migrations.AlterField(
model_name="publiccontact",
name="contact_type",
field=models.CharField(
choices=[
("registrant", "Registrant"),
("admin", "Administrative"),
("tech", "Technical"),
("security", "Security"),
],
help_text="For which type of WHOIS contact",
max_length=14,
),
),
]

View file

@ -1,12 +0,0 @@
# Generated by Django 4.2.1 on 2023-09-12 14:12
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("registrar", "0031_alter_domain_state"),
("registrar", "0031_transitiondomain"),
]
operations = []

View file

@ -1,44 +0,0 @@
# Generated by Django 4.2.1 on 2023-09-13 00:46
from django.db import migrations, models
import django_fsm
class Migration(migrations.Migration):
dependencies = [
("registrar", "0032_merge_0031_alter_domain_state_0031_transitiondomain"),
]
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="publiccontact",
name="contact_type",
field=models.CharField(
choices=[
("registrant", "Registrant"),
("admin", "Administrative"),
("tech", "Technical"),
("security", "Security"),
],
help_text="For which type of WHOIS contact",
max_length=14,
),
),
]

View file

@ -378,16 +378,10 @@ class DomainApplication(TimeStampedModel):
help_text="Urbanization (Puerto Rico only)",
)
type_of_work = models.TextField(
about_your_organization = models.TextField(
null=True,
blank=True,
help_text="Type of work of the organization",
)
more_organization_information = models.TextField(
null=True,
blank=True,
help_text="More information about your organization",
help_text="Information about your organization",
)
authorizing_official = models.ForeignKey(
@ -653,7 +647,7 @@ class DomainApplication(TimeStampedModel):
]
return bool(user_choice and user_choice not in excluded)
def show_type_of_work(self) -> bool:
def show_about_your_organization(self) -> bool:
"""Show this step if this is a special district or interstate."""
user_choice = self.organization_type
return user_choice in [

View file

@ -134,16 +134,10 @@ class DomainInformation(TimeStampedModel):
verbose_name="Urbanization (Puerto Rico only)",
)
type_of_work = models.TextField(
about_your_organization = models.TextField(
null=True,
blank=True,
help_text="Type of work of the organization",
)
more_organization_information = models.TextField(
null=True,
blank=True,
help_text="Further information about the government organization",
help_text="Information about your organization",
)
authorizing_official = models.ForeignKey(

View file

@ -0,0 +1,23 @@
{% extends 'application_form.html' %}
{% load field_helpers %}
{% block form_instructions %}
<p>Wed like to know more about your organization. Include the following in your response: </p>
<ul class="usa-list">
<li>The type of work your organization does </li>
<li>How your organization is a government organization that is independent of a state government </li>
<li>Include links to authorizing legislation, applicable bylaws or charter, or other documentation to support your claims.</li>
</ul>
</p>
{% endblock %}
{% block form_required_fields_help_text %}
<p class="text-semibold"><abbr class="usa-hint usa-hint--required" title="required">*</abbr>This question is required.</p>
{% endblock %}
{% block form_fields %}
{% with attr_maxlength=1000 add_label_class="usa-sr-only" %}
{% input_with_errors forms.0.about_your_organization %}
{% endwith %}
{% endblock %}

View file

@ -46,9 +46,8 @@
Incomplete
{% endif %}
{% endif %}
{% if step == Step.TYPE_OF_WORK %}
<p>{{ application.type_of_work|default:"Incomplete" }}</p>
<p>{{ application.more_organization_information|default:"Incomplete" }}</p>
{% if step == Step.ABOUT_YOUR_ORGANIZATION %}
<p>{{ application.about_your_organization|default:"Incomplete" }}</p>
{% endif %}
{% if step == Step.AUTHORIZING_OFFICIAL %}
{% if application.authorizing_official %}

View file

@ -77,12 +77,8 @@
{% include "includes/summary_item.html" with title='Organization name and mailing address' value=domainapplication address='true' heading_level=heading_level %}
{% endif %}
{% if domainapplication.type_of_work %}
{% include "includes/summary_item.html" with title='Type of work' value=domainapplication.type_of_work heading_level=heading_level %}
{% endif %}
{% if domainapplication.more_organization_information %}
{% include "includes/summary_item.html" with title='More information about your organization' value=domainapplication.more_organization_information heading_level=heading_level %}
{% if domainapplication.about_your_organization %}
{% include "includes/summary_item.html" with title='About your organization' value=domainapplication.about_your_organization heading_level=heading_level %}
{% endif %}
{% if domainapplication.authorizing_official %}

View file

@ -1,10 +0,0 @@
{% extends 'application_form.html' %}
{% load field_helpers %}
{% block form_fields %}
{% with attr_maxlength=1000 %}
{% input_with_errors forms.0.type_of_work %}
{% input_with_errors forms.0.more_organization_information %}
{% endwith %}
{% endblock %}

View file

@ -10,9 +10,9 @@ Organization name and mailing address:
{{ application.city }}, {{ application.state_territory }}
{{ application.zipcode }}{% if application.urbanization %}
{{ application.urbanization }}{% endif %}{% endspaceless %}
{% if application.type_of_work %}{# if block makes one newline if it's false #}
Type of work:
{% spaceless %}{{ application.type_of_work }}{% endspaceless %}
{% if application.about_your_organization %}{# if block makes one newline if it's false #}
About your organization:
{% spaceless %}{{ application.about_your_organization }}{% endspaceless %}
{% endif %}
Authorizing official:
{% spaceless %}{% include "emails/includes/contact.txt" with contact=application.authorizing_official %}{% endspaceless %}

View file

@ -250,7 +250,7 @@ class AuditedAdminMockData:
is_policy_acknowledged: boolean = True,
state_territory: str = "NY",
zipcode: str = "10002",
type_of_work: str = "e-Government",
about_your_organization: str = "e-Government",
anything_else: str = "There is more",
authorizing_official: Contact = self.dummy_contact(item_name, "authorizing_official"),
submitter: Contact = self.dummy_contact(item_name, "submitter"),
@ -267,7 +267,7 @@ class AuditedAdminMockData:
is_policy_acknowledged=True,
state_territory="NY",
zipcode="10002",
type_of_work="e-Government",
about_your_organization="e-Government",
anything_else="There is more",
authorizing_official=self.dummy_contact(item_name, "authorizing_official"),
submitter=self.dummy_contact(item_name, "submitter"),
@ -453,7 +453,7 @@ def completed_application(
has_other_contacts=True,
has_current_website=True,
has_alternative_gov_domain=True,
has_type_of_work=True,
has_about_your_organization=True,
has_anything_else=True,
status=DomainApplication.STARTED,
user=False,
@ -501,8 +501,8 @@ def completed_application(
creator=user,
status=status,
)
if has_type_of_work:
domain_application_kwargs["type_of_work"] = "e-Government"
if has_about_your_organization:
domain_application_kwargs["about_your_organization"] = "e-Government"
if has_anything_else:
domain_application_kwargs["anything_else"] = "There is more"

View file

@ -441,8 +441,7 @@ class TestDomainApplicationAdmin(TestCase):
"state_territory",
"zipcode",
"urbanization",
"type_of_work",
"more_organization_information",
"about_your_organization",
"authorizing_official",
"approved_domain",
"requested_domain",
@ -466,8 +465,7 @@ class TestDomainApplicationAdmin(TestCase):
expected_fields = [
"creator",
"type_of_work",
"more_organization_information",
"about_your_organization",
"address_line1",
"address_line2",
"zipcode",

View file

@ -48,7 +48,7 @@ class TestEmails(TestCase):
self.assertIn("Testy2 Tester2", body)
self.assertIn("Current website for your organization:", body)
self.assertIn("city.com", body)
self.assertIn("Type of work:", body)
self.assertIn("About your organization:", body)
self.assertIn("Anything else", body)
@boto3_mocking.patching
@ -126,26 +126,26 @@ class TestEmails(TestCase):
self.assertRegex(body, r"city.gov\n\nPurpose of your domain:")
@boto3_mocking.patching
def test_submission_confirmation_type_of_work_spacing(self):
"""Test line spacing with type of work."""
application = completed_application(has_type_of_work=True)
def test_submission_confirmation_about_your_organization_spacing(self):
"""Test line spacing with about your organization."""
application = completed_application(has_about_your_organization=True)
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
application.submit()
_, kwargs = self.mock_client.send_email.call_args
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
self.assertIn("Type of work:", body)
self.assertIn("About your organization:", body)
# spacing should be right between adjacent elements
self.assertRegex(body, r"10002\n\nType of work:")
self.assertRegex(body, r"10002\n\nAbout your organization:")
@boto3_mocking.patching
def test_submission_confirmation_no_type_of_work_spacing(self):
"""Test line spacing without type of work."""
application = completed_application(has_type_of_work=False)
def test_submission_confirmation_no_about_your_organization_spacing(self):
"""Test line spacing without about your organization."""
application = completed_application(has_about_your_organization=False)
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
application.submit()
_, kwargs = self.mock_client.send_email.call_args
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
self.assertNotIn("Type of work:", body)
self.assertNotIn("About your organization:", body)
# spacing should be right between adjacent elements
self.assertRegex(body, r"10002\n\nAuthorizing official:")

View file

@ -13,7 +13,7 @@ from registrar.forms.application_wizard import (
TribalGovernmentForm,
PurposeForm,
AnythingElseForm,
TypeOfWorkForm,
AboutYourOrganizationForm,
)
from registrar.forms.domain import ContactForm
@ -118,7 +118,7 @@ class TestFormValidation(TestCase):
["Response must be less than 1000 characters."],
)
def test_anything_else_form_type_of_work_character_count_invalid(self):
def test_anything_else_form_about_your_organization_character_count_invalid(self):
"""Response must be less than 1000 characters."""
form = AnythingElseForm(
data={
@ -147,43 +147,12 @@ class TestFormValidation(TestCase):
["Response must be less than 1000 characters."],
)
def test_anything_else_form_more_organization_information_character_count_invalid(
self,
):
"""Response must be less than 1000 characters."""
form = TypeOfWorkForm(
data={
"more_organization_information": "Bacon ipsum dolor amet fatback"
"shankle, drumstick doner chicken landjaeger turkey andouille."
"Buffalo biltong chuck pork chop tongue bresaola turkey. Doner"
"ground round strip steak, jowl tail chuck ribeye bacon"
"beef ribs swine filet ball tip pancetta strip steak sirloin"
"mignon ham spare ribs rump. Tail shank biltong beef ribs doner"
"buffalo swine bacon. Tongue cow picanha brisket bacon chuck"
"leberkas pork loin pork, drumstick capicola. Doner short loin"
"ground round fatback turducken chislic shoulder turducken"
"spare ribs, burgdoggen kielbasa kevin frankfurter ball tip"
"pancetta cupim. Turkey meatball andouille porchetta hamburger"
"pork chop corned beef. Brisket short ribs turducken, pork chop"
"chislic turkey ball pork chop leberkas rump, rump bacon, jowl"
"tip ham. Shankle salami tongue venison short ribs kielbasa"
"tri-tip ham hock swine hamburger. Flank meatball corned beef"
"cow sausage ball tip kielbasa ham hock. Ball tip cupim meatloaf"
"beef ribs rump jowl tenderloin swine sausage biltong"
"bacon rump tail boudin meatball boudin meatball boudin"
"strip steak pastrami."
}
)
self.assertEqual(
form.errors["more_organization_information"],
["Response must be less than 1000 characters."],
)
def test_anything_else_form_character_count_invalid(self):
"""Response must be less than 1000 characters."""
form = TypeOfWorkForm(
form = AboutYourOrganizationForm(
data={
"type_of_work": "Bacon ipsum dolor amet fatback strip steak pastrami"
"about_your_organization": "Bacon ipsum dolor amet fatback"
"strip steak pastrami"
"shankle, drumstick doner chicken landjaeger turkey andouille."
"Buffalo biltong chuck pork chop tongue bresaola turkey. Doner"
"ground round strip steak, jowl tail chuck ribeye bacon"
@ -204,7 +173,7 @@ class TestFormValidation(TestCase):
}
)
self.assertEqual(
form.errors["type_of_work"],
form.errors["about_your_organization"],
["Response must be less than 1000 characters."],
)

View file

@ -660,12 +660,14 @@ class DomainApplicationTests(TestWithUser, WebTest):
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
contact_result = org_contact_form.submit()
# the post request should return a redirect to the type of work page
# if it was successful.
# the post request should return a redirect to the
# about your organization page if it was successful.
self.assertEqual(contact_result.status_code, 302)
self.assertEqual(contact_result["Location"], "/register/type_of_work/")
self.assertEqual(
contact_result["Location"], "/register/about_your_organization/"
)
def test_application_type_of_work_special(self):
def test_application_about_your_organization_special(self):
"""Special districts have to answer an additional question."""
type_page = self.app.get(reverse("application:")).follow()
# django-webtest does not handle cookie-based sessions well because it keeps
@ -684,7 +686,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
contact_page = type_result.follow()
self.assertContains(contact_page, self.TITLES[Step.TYPE_OF_WORK])
self.assertContains(contact_page, self.TITLES[Step.ABOUT_YOUR_ORGANIZATION])
def test_application_no_other_contacts(self):
"""Applicants with no other contacts have to give a reason."""
@ -704,7 +706,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
actual_url_slug = no_contacts_page.request.path.split("/")[-2]
self.assertEqual(expected_url_slug, actual_url_slug)
def test_application_type_of_work_interstate(self):
def test_application_about_your_organiztion_interstate(self):
"""Special districts have to answer an additional question."""
type_page = self.app.get(reverse("application:")).follow()
# django-webtest does not handle cookie-based sessions well because it keeps
@ -723,7 +725,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
contact_page = type_result.follow()
self.assertContains(contact_page, self.TITLES[Step.TYPE_OF_WORK])
self.assertContains(contact_page, self.TITLES[Step.ABOUT_YOUR_ORGANIZATION])
def test_application_tribal_government(self):
"""Tribal organizations have to answer an additional question."""

View file

@ -30,7 +30,7 @@ class Step(StrEnum):
ORGANIZATION_FEDERAL = "organization_federal"
ORGANIZATION_ELECTION = "organization_election"
ORGANIZATION_CONTACT = "organization_contact"
TYPE_OF_WORK = "type_of_work"
ABOUT_YOUR_ORGANIZATION = "about_your_organization"
AUTHORIZING_OFFICIAL = "authorizing_official"
CURRENT_SITES = "current_sites"
DOTGOV_DOMAIN = "dotgov_domain"
@ -77,7 +77,7 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
Step.ORGANIZATION_FEDERAL: _("Federal government branch"),
Step.ORGANIZATION_ELECTION: _("Election office"),
Step.ORGANIZATION_CONTACT: _("Organization name and mailing address"),
Step.TYPE_OF_WORK: _("Type of work"),
Step.ABOUT_YOUR_ORGANIZATION: _("About your organization"),
Step.AUTHORIZING_OFFICIAL: _("Authorizing official"),
Step.CURRENT_SITES: _("Current website for your organization"),
Step.DOTGOV_DOMAIN: _(".gov domain"),
@ -100,7 +100,9 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
Step.ORGANIZATION_ELECTION: lambda w: w.from_model(
"show_organization_election", False
),
Step.TYPE_OF_WORK: lambda w: w.from_model("show_type_of_work", False),
Step.ABOUT_YOUR_ORGANIZATION: lambda w: w.from_model(
"show_about_your_organization", False
),
Step.NO_OTHER_CONTACTS: lambda w: w.from_model(
"show_no_other_contacts_rationale", False
),
@ -373,9 +375,9 @@ class OrganizationContact(ApplicationWizard):
forms = [forms.OrganizationContactForm]
class TypeOfWork(ApplicationWizard):
template_name = "application_type_of_work.html"
forms = [forms.TypeOfWorkForm]
class AboutYourOrganization(ApplicationWizard):
template_name = "application_about_your_organization.html"
forms = [forms.AboutYourOrganizationForm]
class AuthorizingOfficial(ApplicationWizard):