From a081225664590434b12cef4ae1e10b7571597b8d Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Wed, 26 Apr 2023 11:12:04 -0600 Subject: [PATCH 01/35] created domain information --- src/registrar/models/domain_information.py | 381 +++++++++++++++++++++ 1 file changed, 381 insertions(+) create mode 100644 src/registrar/models/domain_information.py diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py new file mode 100644 index 000000000..c97cfbccb --- /dev/null +++ b/src/registrar/models/domain_information.py @@ -0,0 +1,381 @@ +from __future__ import annotations +from typing import Union + +import logging + +from django.apps import apps +from django.db import models +from django_fsm import FSMField, transition # type: ignore + +from .utility.time_stamped_model import TimeStampedModel +from ..utility.email import send_templated_email, EmailSendingError + + +logger = logging.getLogger(__name__) + +class DomainInformation(TimeStampedModel): + """Once approved, all information from application are then + transfer to here. Also it can be updated via Domain Management page""" + + domain = models.ForeignKey( + "registrar.Domain", + on_delete=models.CASCADE, # delete domain, then get rid of the informations + null=False, + related_name="informations", + ) + + class StateTerritoryChoices(models.TextChoices): + ALABAMA = "AL", "Alabama (AL)" + ALASKA = "AK", "Alaska (AK)" + AMERICAN_SAMOA = "AS", "American Samoa (AS)" + ARIZONA = "AZ", "Arizona (AZ)" + ARKANSAS = "AR", "Arkansas (AR)" + CALIFORNIA = "CA", "California (CA)" + COLORADO = "CO", "Colorado (CO)" + CONNECTICUT = "CT", "Connecticut (CT)" + DELAWARE = "DE", "Delaware (DE)" + DISTRICT_OF_COLUMBIA = "DC", "District of Columbia (DC)" + FLORIDA = "FL", "Florida (FL)" + GEORGIA = "GA", "Georgia (GA)" + GUAM = "GU", "Guam (GU)" + HAWAII = "HI", "Hawaii (HI)" + IDAHO = "ID", "Idaho (ID)" + ILLINOIS = "IL", "Illinois (IL)" + INDIANA = "IN", "Indiana (IN)" + IOWA = "IA", "Iowa (IA)" + KANSAS = "KS", "Kansas (KS)" + KENTUCKY = "KY", "Kentucky (KY)" + LOUISIANA = "LA", "Louisiana (LA)" + MAINE = "ME", "Maine (ME)" + MARYLAND = "MD", "Maryland (MD)" + MASSACHUSETTS = "MA", "Massachusetts (MA)" + MICHIGAN = "MI", "Michigan (MI)" + MINNESOTA = "MN", "Minnesota (MN)" + MISSISSIPPI = "MS", "Mississippi (MS)" + MISSOURI = "MO", "Missouri (MO)" + MONTANA = "MT", "Montana (MT)" + NEBRASKA = "NE", "Nebraska (NE)" + NEVADA = "NV", "Nevada (NV)" + NEW_HAMPSHIRE = "NH", "New Hampshire (NH)" + NEW_JERSEY = "NJ", "New Jersey (NJ)" + NEW_MEXICO = "NM", "New Mexico (NM)" + NEW_YORK = "NY", "New York (NY)" + NORTH_CAROLINA = "NC", "North Carolina (NC)" + NORTH_DAKOTA = "ND", "North Dakota (ND)" + NORTHERN_MARIANA_ISLANDS = "MP", "Northern Mariana Islands (MP)" + OHIO = "OH", "Ohio (OH)" + OKLAHOMA = "OK", "Oklahoma (OK)" + OREGON = "OR", "Oregon (OR)" + PENNSYLVANIA = "PA", "Pennsylvania (PA)" + PUERTO_RICO = "PR", "Puerto Rico (PR)" + RHODE_ISLAND = "RI", "Rhode Island (RI)" + SOUTH_CAROLINA = "SC", "South Carolina (SC)" + SOUTH_DAKOTA = "SD", "South Dakota (SD)" + TENNESSEE = "TN", "Tennessee (TN)" + TEXAS = "TX", "Texas (TX)" + UNITED_STATES_MINOR_OUTLYING_ISLANDS = ( + "UM", + "United States Minor Outlying Islands (UM)", + ) + UTAH = "UT", "Utah (UT)" + VERMONT = "VT", "Vermont (VT)" + VIRGIN_ISLANDS = "VI", "Virgin Islands (VI)" + VIRGINIA = "VA", "Virginia (VA)" + WASHINGTON = "WA", "Washington (WA)" + WEST_VIRGINIA = "WV", "West Virginia (WV)" + WISCONSIN = "WI", "Wisconsin (WI)" + WYOMING = "WY", "Wyoming (WY)" + ARMED_FORCES_AA = "AA", "Armed Forces Americas (AA)" + ARMED_FORCES_AE = "AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)" + ARMED_FORCES_AP = "AP", "Armed Forces Pacific (AP)" + + class OrganizationChoices(models.TextChoices): + FEDERAL = ( + "federal", + "Federal: an agency of the U.S. government's executive, legislative, " + "or judicial branches", + ) + INTERSTATE = "interstate", "Interstate: an organization of two or more states" + STATE_OR_TERRITORY = "state_or_territory", ( + "State or territory: one of the 50 U.S. states, the District of " + "Columbia, American Samoa, Guam, Northern Mariana Islands, " + "Puerto Rico, or the U.S. Virgin Islands" + ) + TRIBAL = "tribal", ( + "Tribal: a tribal government recognized by the federal or " + "a state government" + ) + COUNTY = "county", "County: a county, parish, or borough" + CITY = "city", "City: a city, town, township, village, etc." + SPECIAL_DISTRICT = "special_district", ( + "Special district: an independent organization within a single state" + ) + SCHOOL_DISTRICT = "school_district", ( + "School district: a school district that is not part of a local government" + ) + + class BranchChoices(models.TextChoices): + EXECUTIVE = "executive", "Executive" + JUDICIAL = "judicial", "Judicial" + LEGISLATIVE = "legislative", "Legislative" + + AGENCIES = [ + "Administrative Conference of the United States", + "Advisory Council on Historic Preservation", + "American Battle Monuments Commission", + "Appalachian Regional Commission", + ( + "Appraisal Subcommittee of the Federal Financial " + "Institutions Examination Council" + ), + "Armed Forces Retirement Home", + "Barry Goldwater Scholarship and Excellence in Education Program", + "Central Intelligence Agency", + "Christopher Columbus Fellowship Foundation", + "Commission for the Preservation of America's Heritage Abroad", + "Commission of Fine Arts", + "Committee for Purchase From People Who Are Blind or Severely Disabled", + "Commodity Futures Trading Commission", + "Consumer Financial Protection Bureau", + "Consumer Product Safety Commission", + "Corporation for National and Community Service", + "Council of Inspectors General on Integrity and Efficiency", + "DC Court Services and Offender Supervision Agency", + "DC Pre-trial Services", + "Defense Nuclear Facilities Safety Board", + "Delta Regional Authority", + "Denali Commission", + "Department of Agriculture", + "Department of Commerce", + "Department of Defense", + "Department of Education", + "Department of Energy", + "Department of Health and Human Services", + "Department of Homeland Security", + "Department of Housing and Urban Development", + "Department of Justice", + "Department of Labor", + "Department of State", + "Department of the Interior", + "Department of the Treasury", + "Department of Transportation", + "Department of Veterans Affairs", + "Director of National Intelligence", + "Dwight D. Eisenhower Memorial Commission", + "Election Assistance Commission", + "Environmental Protection Agency", + "Equal Employment Opportunity Commission", + "Export-Import Bank of the United States", + "Farm Credit Administration", + "Farm Credit System Insurance Corporation", + "Federal Communications Commission", + "Federal Deposit Insurance Corporation", + "Federal Election Commission", + "Federal Financial Institutions Examination Council", + "Federal Housing Finance Agency", + "Federal Judiciary", + "Federal Labor Relations Authority", + "Federal Maritime Commission", + "Federal Mediation and Conciliation Service", + "Federal Mine Safety and Health Review Commission", + "Federal Reserve System", + "Federal Trade Commission", + "General Services Administration", + "Gulf Coast Ecosystem Restoration Council", + "Harry S Truman Scholarship Foundation", + "Institute of Peace", + "Inter-American Foundation", + "International Boundary and Water Commission: United States and Mexico", + "International Boundary Commission: United States and Canada", + "International Joint Commission: United States and Canada", + "James Madison Memorial Fellowship Foundation", + "Japan-United States Friendship Commission", + "John F. Kennedy Center for the Performing Arts", + "Legal Services Corporation", + "Legislative Branch", + "Marine Mammal Commission", + "Medicare Payment Advisory Commission", + "Merit Systems Protection Board", + "Millennium Challenge Corporation", + "National Aeronautics and Space Administration", + "National Archives and Records Administration", + "National Capital Planning Commission", + "National Council on Disability", + "National Credit Union Administration", + "National Foundation on the Arts and the Humanities", + "National Gallery of Art", + "National Labor Relations Board", + "National Mediation Board", + "National Science Foundation", + "National Transportation Safety Board", + "Northern Border Regional Commission", + "Nuclear Regulatory Commission", + "Nuclear Safety Oversight Committee", + "Nuclear Waste Technical Review Board", + "Occupational Safety and Health Review Commission", + "Office of Compliance", + "Office of Government Ethics", + "Office of Navajo and Hopi Indian Relocation", + "Office of Personnel Management", + "Overseas Private Investment Corporation", + "Peace Corps", + "Pension Benefit Guaranty Corporation", + "Postal Regulatory Commission", + "Privacy and Civil Liberties Oversight Board", + "Public Defender Service for the District of Columbia", + "Railroad Retirement Board", + "Securities and Exchange Commission", + "Selective Service System", + "Small Business Administration", + "Smithsonian Institution", + "Social Security Administration", + "State Justice Institute", + "State, Local, and Tribal Government", + "Stennis Center for Public Service", + "Surface Transportation Board", + "Tennessee Valley Authority", + "The Executive Office of the President", + "U.S. Access Board", + "U.S. Agency for Global Media", + "U.S. Agency for International Development", + "U.S. Chemical Safety Board", + "U.S. China Economic and Security Review Commission", + "U.S. Commission on Civil Rights", + "U.S. Commission on International Religious Freedom", + "U.S. Interagency Council on Homelessness", + "U.S. International Trade Commission", + "U.S. Office of Special Counsel", + "U.S. Postal Service", + "U.S. Trade and Development Agency", + "Udall Foundation", + "United States African Development Foundation", + "United States Arctic Research Commission", + "United States Holocaust Memorial Museum", + "Utah Reclamation Mitigation and Conservation Commission", + "Vietnam Education Foundation", + "Woodrow Wilson International Center for Scholars", + "World War I Centennial Commission", + ] + AGENCY_CHOICES = [(v, v) for v in AGENCIES] + + # This is the application user who created this application. The contact + # information that they gave is in the `submitter` field + creator = models.ForeignKey( + "registrar.User", + on_delete=models.PROTECT, + related_name="applications_created", + ) + investigator = models.ForeignKey( + "registrar.User", + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="applications_investigating", + ) + + # ##### data fields from the initial form ##### + organization_type = models.CharField( + max_length=255, + choices=OrganizationChoices.choices, + null=True, + blank=True, + help_text="Type of Organization", + ) + + federally_recognized_tribe = models.BooleanField( + null=True, + help_text="Is the tribe federally recognized", + ) + + state_recognized_tribe = models.BooleanField( + null=True, + help_text="Is the tribe recognized by a state", + ) + + tribe_name = models.TextField( + null=True, + blank=True, + help_text="Name of tribe", + ) + + federal_agency = models.TextField( + null=True, + blank=True, + help_text="Federal agency", + ) + + federal_type = models.CharField( + max_length=50, + choices=BranchChoices.choices, + null=True, + blank=True, + help_text="Federal government branch", + ) + + is_election_board = models.BooleanField( + null=True, + blank=True, + help_text="Is your organization an election office?", + ) + + organization_name = models.TextField( + null=True, + blank=True, + help_text="Organization name", + db_index=True, + ) + address_line1 = models.TextField( + null=True, + blank=True, + help_text="Street address", + ) + address_line2 = models.CharField( + max_length=15, + null=True, + blank=True, + help_text="Street address line 2", + ) + city = models.TextField( + null=True, + blank=True, + help_text="City", + ) + state_territory = models.CharField( + max_length=2, + null=True, + blank=True, + help_text="State, territory, or military post", + ) + zipcode = models.CharField( + max_length=10, + null=True, + blank=True, + help_text="Zip code", + db_index=True, + ) + urbanization = models.TextField( + null=True, + blank=True, + help_text="Urbanization (Puerto Rico only)", + ) + + type_of_work = 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", + ) + + authorizing_official = models.ForeignKey( + "registrar.Contact", + null=True, + blank=True, + related_name="authorizing_official", + on_delete=models.PROTECT, + ) + From f4d30b5780d56947f891ee50093a0abf7e5e0613 Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Wed, 26 Apr 2023 11:29:31 -0600 Subject: [PATCH 02/35] removed a line - DRAFT --- src/registrar/models/domain_application.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index 898615e27..32364ca52 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -5,11 +5,6 @@ import logging from django.apps import apps from django.db import models -from django_fsm import FSMField, transition # type: ignore - -from .utility.time_stamped_model import TimeStampedModel -from ..utility.email import send_templated_email, EmailSendingError - logger = logging.getLogger(__name__) From 2aa5582a47888e38478bf8011b45dc9a789078ec Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Wed, 26 Apr 2023 11:30:22 -0600 Subject: [PATCH 03/35] removed a line - DRAFT2 --- src/registrar/models/domain_application.py | 5 +++++ src/registrar/models/domain_information.py | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index 32364ca52..898615e27 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -5,6 +5,11 @@ import logging from django.apps import apps from django.db import models +from django_fsm import FSMField, transition # type: ignore + +from .utility.time_stamped_model import TimeStampedModel +from ..utility.email import send_templated_email, EmailSendingError + logger = logging.getLogger(__name__) diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index c97cfbccb..905ae8ce9 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -5,10 +5,6 @@ import logging from django.apps import apps from django.db import models -from django_fsm import FSMField, transition # type: ignore - -from .utility.time_stamped_model import TimeStampedModel -from ..utility.email import send_templated_email, EmailSendingError logger = logging.getLogger(__name__) From 0d4c5ef65ae08e97f5f7812d5c6be0f083157e1d Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Tue, 2 May 2023 12:28:52 -0600 Subject: [PATCH 04/35] adding DomainInformation --- src/registrar/models/domain_information.py | 369 +-------------------- 1 file changed, 6 insertions(+), 363 deletions(-) diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index 905ae8ce9..4a1afc45c 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -1,5 +1,6 @@ from __future__ import annotations from typing import Union +from domain_application import DomainApplication import logging @@ -9,369 +10,11 @@ from django.db import models logger = logging.getLogger(__name__) -class DomainInformation(TimeStampedModel): - """Once approved, all information from application are then - transfer to here. Also it can be updated via Domain Management page""" - - domain = models.ForeignKey( - "registrar.Domain", - on_delete=models.CASCADE, # delete domain, then get rid of the informations - null=False, - related_name="informations", - ) - - class StateTerritoryChoices(models.TextChoices): - ALABAMA = "AL", "Alabama (AL)" - ALASKA = "AK", "Alaska (AK)" - AMERICAN_SAMOA = "AS", "American Samoa (AS)" - ARIZONA = "AZ", "Arizona (AZ)" - ARKANSAS = "AR", "Arkansas (AR)" - CALIFORNIA = "CA", "California (CA)" - COLORADO = "CO", "Colorado (CO)" - CONNECTICUT = "CT", "Connecticut (CT)" - DELAWARE = "DE", "Delaware (DE)" - DISTRICT_OF_COLUMBIA = "DC", "District of Columbia (DC)" - FLORIDA = "FL", "Florida (FL)" - GEORGIA = "GA", "Georgia (GA)" - GUAM = "GU", "Guam (GU)" - HAWAII = "HI", "Hawaii (HI)" - IDAHO = "ID", "Idaho (ID)" - ILLINOIS = "IL", "Illinois (IL)" - INDIANA = "IN", "Indiana (IN)" - IOWA = "IA", "Iowa (IA)" - KANSAS = "KS", "Kansas (KS)" - KENTUCKY = "KY", "Kentucky (KY)" - LOUISIANA = "LA", "Louisiana (LA)" - MAINE = "ME", "Maine (ME)" - MARYLAND = "MD", "Maryland (MD)" - MASSACHUSETTS = "MA", "Massachusetts (MA)" - MICHIGAN = "MI", "Michigan (MI)" - MINNESOTA = "MN", "Minnesota (MN)" - MISSISSIPPI = "MS", "Mississippi (MS)" - MISSOURI = "MO", "Missouri (MO)" - MONTANA = "MT", "Montana (MT)" - NEBRASKA = "NE", "Nebraska (NE)" - NEVADA = "NV", "Nevada (NV)" - NEW_HAMPSHIRE = "NH", "New Hampshire (NH)" - NEW_JERSEY = "NJ", "New Jersey (NJ)" - NEW_MEXICO = "NM", "New Mexico (NM)" - NEW_YORK = "NY", "New York (NY)" - NORTH_CAROLINA = "NC", "North Carolina (NC)" - NORTH_DAKOTA = "ND", "North Dakota (ND)" - NORTHERN_MARIANA_ISLANDS = "MP", "Northern Mariana Islands (MP)" - OHIO = "OH", "Ohio (OH)" - OKLAHOMA = "OK", "Oklahoma (OK)" - OREGON = "OR", "Oregon (OR)" - PENNSYLVANIA = "PA", "Pennsylvania (PA)" - PUERTO_RICO = "PR", "Puerto Rico (PR)" - RHODE_ISLAND = "RI", "Rhode Island (RI)" - SOUTH_CAROLINA = "SC", "South Carolina (SC)" - SOUTH_DAKOTA = "SD", "South Dakota (SD)" - TENNESSEE = "TN", "Tennessee (TN)" - TEXAS = "TX", "Texas (TX)" - UNITED_STATES_MINOR_OUTLYING_ISLANDS = ( - "UM", - "United States Minor Outlying Islands (UM)", - ) - UTAH = "UT", "Utah (UT)" - VERMONT = "VT", "Vermont (VT)" - VIRGIN_ISLANDS = "VI", "Virgin Islands (VI)" - VIRGINIA = "VA", "Virginia (VA)" - WASHINGTON = "WA", "Washington (WA)" - WEST_VIRGINIA = "WV", "West Virginia (WV)" - WISCONSIN = "WI", "Wisconsin (WI)" - WYOMING = "WY", "Wyoming (WY)" - ARMED_FORCES_AA = "AA", "Armed Forces Americas (AA)" - ARMED_FORCES_AE = "AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)" - ARMED_FORCES_AP = "AP", "Armed Forces Pacific (AP)" - - class OrganizationChoices(models.TextChoices): - FEDERAL = ( - "federal", - "Federal: an agency of the U.S. government's executive, legislative, " - "or judicial branches", - ) - INTERSTATE = "interstate", "Interstate: an organization of two or more states" - STATE_OR_TERRITORY = "state_or_territory", ( - "State or territory: one of the 50 U.S. states, the District of " - "Columbia, American Samoa, Guam, Northern Mariana Islands, " - "Puerto Rico, or the U.S. Virgin Islands" - ) - TRIBAL = "tribal", ( - "Tribal: a tribal government recognized by the federal or " - "a state government" - ) - COUNTY = "county", "County: a county, parish, or borough" - CITY = "city", "City: a city, town, township, village, etc." - SPECIAL_DISTRICT = "special_district", ( - "Special district: an independent organization within a single state" - ) - SCHOOL_DISTRICT = "school_district", ( - "School district: a school district that is not part of a local government" - ) - - class BranchChoices(models.TextChoices): - EXECUTIVE = "executive", "Executive" - JUDICIAL = "judicial", "Judicial" - LEGISLATIVE = "legislative", "Legislative" - - AGENCIES = [ - "Administrative Conference of the United States", - "Advisory Council on Historic Preservation", - "American Battle Monuments Commission", - "Appalachian Regional Commission", - ( - "Appraisal Subcommittee of the Federal Financial " - "Institutions Examination Council" - ), - "Armed Forces Retirement Home", - "Barry Goldwater Scholarship and Excellence in Education Program", - "Central Intelligence Agency", - "Christopher Columbus Fellowship Foundation", - "Commission for the Preservation of America's Heritage Abroad", - "Commission of Fine Arts", - "Committee for Purchase From People Who Are Blind or Severely Disabled", - "Commodity Futures Trading Commission", - "Consumer Financial Protection Bureau", - "Consumer Product Safety Commission", - "Corporation for National and Community Service", - "Council of Inspectors General on Integrity and Efficiency", - "DC Court Services and Offender Supervision Agency", - "DC Pre-trial Services", - "Defense Nuclear Facilities Safety Board", - "Delta Regional Authority", - "Denali Commission", - "Department of Agriculture", - "Department of Commerce", - "Department of Defense", - "Department of Education", - "Department of Energy", - "Department of Health and Human Services", - "Department of Homeland Security", - "Department of Housing and Urban Development", - "Department of Justice", - "Department of Labor", - "Department of State", - "Department of the Interior", - "Department of the Treasury", - "Department of Transportation", - "Department of Veterans Affairs", - "Director of National Intelligence", - "Dwight D. Eisenhower Memorial Commission", - "Election Assistance Commission", - "Environmental Protection Agency", - "Equal Employment Opportunity Commission", - "Export-Import Bank of the United States", - "Farm Credit Administration", - "Farm Credit System Insurance Corporation", - "Federal Communications Commission", - "Federal Deposit Insurance Corporation", - "Federal Election Commission", - "Federal Financial Institutions Examination Council", - "Federal Housing Finance Agency", - "Federal Judiciary", - "Federal Labor Relations Authority", - "Federal Maritime Commission", - "Federal Mediation and Conciliation Service", - "Federal Mine Safety and Health Review Commission", - "Federal Reserve System", - "Federal Trade Commission", - "General Services Administration", - "Gulf Coast Ecosystem Restoration Council", - "Harry S Truman Scholarship Foundation", - "Institute of Peace", - "Inter-American Foundation", - "International Boundary and Water Commission: United States and Mexico", - "International Boundary Commission: United States and Canada", - "International Joint Commission: United States and Canada", - "James Madison Memorial Fellowship Foundation", - "Japan-United States Friendship Commission", - "John F. Kennedy Center for the Performing Arts", - "Legal Services Corporation", - "Legislative Branch", - "Marine Mammal Commission", - "Medicare Payment Advisory Commission", - "Merit Systems Protection Board", - "Millennium Challenge Corporation", - "National Aeronautics and Space Administration", - "National Archives and Records Administration", - "National Capital Planning Commission", - "National Council on Disability", - "National Credit Union Administration", - "National Foundation on the Arts and the Humanities", - "National Gallery of Art", - "National Labor Relations Board", - "National Mediation Board", - "National Science Foundation", - "National Transportation Safety Board", - "Northern Border Regional Commission", - "Nuclear Regulatory Commission", - "Nuclear Safety Oversight Committee", - "Nuclear Waste Technical Review Board", - "Occupational Safety and Health Review Commission", - "Office of Compliance", - "Office of Government Ethics", - "Office of Navajo and Hopi Indian Relocation", - "Office of Personnel Management", - "Overseas Private Investment Corporation", - "Peace Corps", - "Pension Benefit Guaranty Corporation", - "Postal Regulatory Commission", - "Privacy and Civil Liberties Oversight Board", - "Public Defender Service for the District of Columbia", - "Railroad Retirement Board", - "Securities and Exchange Commission", - "Selective Service System", - "Small Business Administration", - "Smithsonian Institution", - "Social Security Administration", - "State Justice Institute", - "State, Local, and Tribal Government", - "Stennis Center for Public Service", - "Surface Transportation Board", - "Tennessee Valley Authority", - "The Executive Office of the President", - "U.S. Access Board", - "U.S. Agency for Global Media", - "U.S. Agency for International Development", - "U.S. Chemical Safety Board", - "U.S. China Economic and Security Review Commission", - "U.S. Commission on Civil Rights", - "U.S. Commission on International Religious Freedom", - "U.S. Interagency Council on Homelessness", - "U.S. International Trade Commission", - "U.S. Office of Special Counsel", - "U.S. Postal Service", - "U.S. Trade and Development Agency", - "Udall Foundation", - "United States African Development Foundation", - "United States Arctic Research Commission", - "United States Holocaust Memorial Museum", - "Utah Reclamation Mitigation and Conservation Commission", - "Vietnam Education Foundation", - "Woodrow Wilson International Center for Scholars", - "World War I Centennial Commission", - ] - AGENCY_CHOICES = [(v, v) for v in AGENCIES] - - # This is the application user who created this application. The contact - # information that they gave is in the `submitter` field - creator = models.ForeignKey( - "registrar.User", - on_delete=models.PROTECT, - related_name="applications_created", - ) - investigator = models.ForeignKey( - "registrar.User", +class DomainInformation(DomainApplication): + security_email = models.CharField( + max_length=320, null=True, blank=True, - on_delete=models.SET_NULL, - related_name="applications_investigating", + help_text="Security email for public use", ) - - # ##### data fields from the initial form ##### - organization_type = models.CharField( - max_length=255, - choices=OrganizationChoices.choices, - null=True, - blank=True, - help_text="Type of Organization", - ) - - federally_recognized_tribe = models.BooleanField( - null=True, - help_text="Is the tribe federally recognized", - ) - - state_recognized_tribe = models.BooleanField( - null=True, - help_text="Is the tribe recognized by a state", - ) - - tribe_name = models.TextField( - null=True, - blank=True, - help_text="Name of tribe", - ) - - federal_agency = models.TextField( - null=True, - blank=True, - help_text="Federal agency", - ) - - federal_type = models.CharField( - max_length=50, - choices=BranchChoices.choices, - null=True, - blank=True, - help_text="Federal government branch", - ) - - is_election_board = models.BooleanField( - null=True, - blank=True, - help_text="Is your organization an election office?", - ) - - organization_name = models.TextField( - null=True, - blank=True, - help_text="Organization name", - db_index=True, - ) - address_line1 = models.TextField( - null=True, - blank=True, - help_text="Street address", - ) - address_line2 = models.CharField( - max_length=15, - null=True, - blank=True, - help_text="Street address line 2", - ) - city = models.TextField( - null=True, - blank=True, - help_text="City", - ) - state_territory = models.CharField( - max_length=2, - null=True, - blank=True, - help_text="State, territory, or military post", - ) - zipcode = models.CharField( - max_length=10, - null=True, - blank=True, - help_text="Zip code", - db_index=True, - ) - urbanization = models.TextField( - null=True, - blank=True, - help_text="Urbanization (Puerto Rico only)", - ) - - type_of_work = 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", - ) - - authorizing_official = models.ForeignKey( - "registrar.Contact", - null=True, - blank=True, - related_name="authorizing_official", - on_delete=models.PROTECT, - ) - + \ No newline at end of file From c99a9598ef8cd228a3f2bca45312931d42f02da6 Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Tue, 2 May 2023 12:35:27 -0600 Subject: [PATCH 05/35] adding DomainInformation to init --- src/registrar/models/__init__.py | 2 ++ src/registrar/models/domain_information.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/registrar/models/__init__.py b/src/registrar/models/__init__.py index 0fcfeca40..9351e312d 100644 --- a/src/registrar/models/__init__.py +++ b/src/registrar/models/__init__.py @@ -2,6 +2,7 @@ from auditlog.registry import auditlog # type: ignore from .contact import Contact from .domain_application import DomainApplication +from .domain_information import DomainInformation from .domain import Domain from .host_ip import HostIP from .host import Host @@ -15,6 +16,7 @@ from .website import Website __all__ = [ "Contact", "DomainApplication", + "DomainInformation", "Domain", "DomainInvitation", "HostIP", diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index 4a1afc45c..d16025357 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -1,6 +1,6 @@ from __future__ import annotations from typing import Union -from domain_application import DomainApplication +from .domain_application import DomainApplication import logging From c9b0890b9dd47bbe3f158a3ef4b14d4035ffb787 Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Tue, 2 May 2023 12:38:15 -0600 Subject: [PATCH 06/35] tweaking email field so it can be validated --- src/registrar/models/domain_information.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index d16025357..16a0f6236 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -11,7 +11,7 @@ from django.db import models logger = logging.getLogger(__name__) class DomainInformation(DomainApplication): - security_email = models.CharField( + security_email = models.EmailField( max_length=320, null=True, blank=True, From 23fc0f0e8fc82cd7e05be0792b4563fcfb0af7d0 Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Tue, 2 May 2023 13:31:10 -0600 Subject: [PATCH 07/35] Update before the migrate --- src/registrar/models/domain_information.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index 16a0f6236..4dcd7d402 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -17,4 +17,9 @@ class DomainInformation(DomainApplication): blank=True, help_text="Security email for public use", ) - \ No newline at end of file + + other_contacts_info = models.ManyToManyField( + "registrar.ContactInformation", + blank=True, + related_name="contact_information", + ) \ No newline at end of file From 90abf6e2a528105b46b310d739cdaf86da3e3104 Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Tue, 2 May 2023 13:32:42 -0600 Subject: [PATCH 08/35] tweak a bit more --- src/registrar/models/domain_information.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index 4dcd7d402..16a0f6236 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -17,9 +17,4 @@ class DomainInformation(DomainApplication): blank=True, help_text="Security email for public use", ) - - other_contacts_info = models.ManyToManyField( - "registrar.ContactInformation", - blank=True, - related_name="contact_information", - ) \ No newline at end of file + \ No newline at end of file From da2d4c5f3146f35d2d7dbbcaaa493dfcbd6ea273 Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Tue, 2 May 2023 13:42:39 -0600 Subject: [PATCH 09/35] New migration file --- .../migrations/0018_domaininformation.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/registrar/migrations/0018_domaininformation.py diff --git a/src/registrar/migrations/0018_domaininformation.py b/src/registrar/migrations/0018_domaininformation.py new file mode 100644 index 000000000..ea91de52a --- /dev/null +++ b/src/registrar/migrations/0018_domaininformation.py @@ -0,0 +1,42 @@ +# Generated by Django 4.1.6 on 2023-05-02 19:38 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("registrar", "0017_alter_domainapplication_status_and_more"), + ] + + operations = [ + migrations.CreateModel( + name="DomainInformation", + fields=[ + ( + "domainapplication_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="registrar.domainapplication", + ), + ), + ( + "security_email", + models.EmailField( + blank=True, + help_text="Security email for public use", + max_length=320, + null=True, + ), + ), + ], + options={ + "abstract": False, + }, + bases=("registrar.domainapplication",), + ), + ] From d0bdc194d27291620e2992b254bc315ed6ef49ec Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Mon, 8 May 2023 09:12:55 -0400 Subject: [PATCH 10/35] Domain Information~ --- src/registrar/admin.py | 1 + .../migrations/0018_domaininformation.py | 252 +++++++++- src/registrar/models/domain_application.py | 34 +- src/registrar/models/domain_information.py | 450 +++++++++++++++++- 4 files changed, 726 insertions(+), 11 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 30c8e8b89..439dfd9f9 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -55,6 +55,7 @@ admin.site.register(models.UserDomainRole, AuditedAdmin) admin.site.register(models.Contact, AuditedAdmin) admin.site.register(models.DomainInvitation, AuditedAdmin) admin.site.register(models.DomainApplication, AuditedAdmin) +admin.site.register(models.DomainInformation, AuditedAdmin) admin.site.register(models.Domain, AuditedAdmin) admin.site.register(models.Host, MyHostAdmin) admin.site.register(models.Nameserver, MyHostAdmin) diff --git a/src/registrar/migrations/0018_domaininformation.py b/src/registrar/migrations/0018_domaininformation.py index ea91de52a..c85943990 100644 --- a/src/registrar/migrations/0018_domaininformation.py +++ b/src/registrar/migrations/0018_domaininformation.py @@ -1,5 +1,6 @@ -# Generated by Django 4.1.6 on 2023-05-02 19:38 +# Generated by Django 4.1.6 on 2023-05-08 12:48 +from django.conf import settings from django.db import migrations, models import django.db.models.deletion @@ -14,14 +15,187 @@ class Migration(migrations.Migration): name="DomainInformation", fields=[ ( - "domainapplication_ptr", - models.OneToOneField( + "id", + models.BigAutoField( auto_created=True, - on_delete=django.db.models.deletion.CASCADE, - parent_link=True, primary_key=True, serialize=False, - to="registrar.domainapplication", + verbose_name="ID", + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "organization_type", + models.CharField( + blank=True, + choices=[ + ( + "federal", + "Federal: an agency of the U.S. government's executive, legislative, or judicial branches", + ), + ( + "interstate", + "Interstate: an organization of two or more states", + ), + ( + "state_or_territory", + "State or territory: one of the 50 U.S. states, the District of Columbia, American Samoa, Guam, Northern Mariana Islands, Puerto Rico, or the U.S. Virgin Islands", + ), + ( + "tribal", + "Tribal: a tribal government recognized by the federal or a state government", + ), + ("county", "County: a county, parish, or borough"), + ("city", "City: a city, town, township, village, etc."), + ( + "special_district", + "Special district: an independent organization within a single state", + ), + ( + "school_district", + "School district: a school district that is not part of a local government", + ), + ], + help_text="Type of Organization", + max_length=255, + null=True, + ), + ), + ( + "federally_recognized_tribe", + models.BooleanField( + help_text="Is the tribe federally recognized", null=True + ), + ), + ( + "state_recognized_tribe", + models.BooleanField( + help_text="Is the tribe recognized by a state", null=True + ), + ), + ( + "tribe_name", + models.TextField(blank=True, help_text="Name of tribe", null=True), + ), + ( + "federal_agency", + models.TextField(blank=True, help_text="Federal agency", null=True), + ), + ( + "federal_type", + models.CharField( + blank=True, + choices=[ + ("executive", "Executive"), + ("judicial", "Judicial"), + ("legislative", "Legislative"), + ], + help_text="Federal government branch", + max_length=50, + null=True, + ), + ), + ( + "is_election_board", + models.BooleanField( + blank=True, + help_text="Is your organization an election office?", + null=True, + ), + ), + ( + "organization_name", + models.TextField( + blank=True, + db_index=True, + help_text="Organization name", + null=True, + ), + ), + ( + "address_line1", + models.TextField(blank=True, help_text="Street address", null=True), + ), + ( + "address_line2", + models.CharField( + blank=True, + help_text="Street address line 2", + max_length=15, + null=True, + ), + ), + ("city", models.TextField(blank=True, help_text="City", null=True)), + ( + "state_territory", + models.CharField( + blank=True, + help_text="State, territory, or military post", + max_length=2, + null=True, + ), + ), + ( + "zipcode", + models.CharField( + blank=True, + db_index=True, + help_text="Zip code", + max_length=10, + null=True, + ), + ), + ( + "urbanization", + models.TextField( + blank=True, + help_text="Urbanization (Puerto Rico only)", + null=True, + ), + ), + ( + "type_of_work", + models.TextField( + blank=True, + help_text="Type of work of the organization", + null=True, + ), + ), + ( + "more_organization_information", + models.TextField( + blank=True, + help_text="Further information about the government organization", + null=True, + ), + ), + ( + "purpose", + models.TextField( + blank=True, help_text="Purpose of your domain", null=True + ), + ), + ( + "no_other_contacts_rationale", + models.TextField( + blank=True, + help_text="Reason for listing no additional contacts", + null=True, + ), + ), + ( + "anything_else", + models.TextField( + blank=True, help_text="Anything else we should know?", null=True + ), + ), + ( + "is_policy_acknowledged", + models.BooleanField( + blank=True, + help_text="Acknowledged .gov acceptable use policy", + null=True, ), ), ( @@ -33,10 +207,72 @@ class Migration(migrations.Migration): null=True, ), ), + ( + "alternative_domains", + models.ManyToManyField( + blank=True, related_name="alternatives+", to="registrar.website" + ), + ), + ( + "authorizing_official", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="information_authorizing_official", + to="registrar.contact", + ), + ), + ( + "creator", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="information_created", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "domain", + models.ForeignKey( + blank=True, + help_text="Domain to which this information belongs", + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="domain_info", + to="registrar.domain", + ), + ), + ( + "investigator", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="information_investigating", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "other_contacts", + models.ManyToManyField( + blank=True, + related_name="contact_applications_information", + to="registrar.contact", + ), + ), + ( + "submitter", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="submitted_applications_information", + to="registrar.contact", + ), + ), ], options={ - "abstract": False, + "verbose_name_plural": "Domain Information", }, - bases=("registrar.domainapplication",), ), ] diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index 898615e27..5e50c88e9 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -9,11 +9,10 @@ from django_fsm import FSMField, transition # type: ignore from .utility.time_stamped_model import TimeStampedModel from ..utility.email import send_templated_email, EmailSendingError - +from itertools import chain logger = logging.getLogger(__name__) - class DomainApplication(TimeStampedModel): """A registrant's application for a new domain.""" @@ -520,6 +519,16 @@ class DomainApplication(TimeStampedModel): Domain = apps.get_model("registrar.Domain") created_domain, _ = Domain.objects.get_or_create(name=self.requested_domain) + + + # copy the information from domainapplication into domaininformation + DomainInformation = apps.get_model("registrar.DomainInformation") + domain_info = self.to_dict() + # remove PK from domainapplication as it use different PK + # for domain/domaininformation + + domain_info, _ = DomainInformation.create_from_da_dict(domain_info) + # create the permission for the user UserDomainRole = apps.get_model("registrar.UserDomainRole") UserDomainRole.objects.get_or_create( @@ -577,3 +586,24 @@ class DomainApplication(TimeStampedModel): if self.organization_type == DomainApplication.OrganizationChoices.FEDERAL: return True return False + + def to_dict(instance): + """This is to process to_dict for Domain Information, making it friendly to "copy" it """ + opts = instance._meta + data = {} + for field in chain(opts.concrete_fields, opts.private_fields): + # import pdb; pdb.set_trace() + if field.get_internal_type() in ("ForeignKey", "OneToOneField"): + # get the related instance of the FK value + print(f"{field.name}: ID: {field.value_from_object(instance)}") + fk_id = field.value_from_object(instance) + if fk_id: + data[field.name] = field.related_model.objects.get(id=fk_id) + else: + data[field.name] = None + else: + data[field.name] = field.value_from_object(instance) + for field in opts.many_to_many: + data[field.name] = field.value_from_object(instance) + return data + diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index 16a0f6236..b6adc6c52 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -1,6 +1,7 @@ from __future__ import annotations from typing import Union from .domain_application import DomainApplication +from .utility.time_stamped_model import TimeStampedModel import logging @@ -10,11 +11,458 @@ from django.db import models logger = logging.getLogger(__name__) -class DomainInformation(DomainApplication): +class DomainInformation(TimeStampedModel): + + """A registrant's application for a new domain.""" + + class StateTerritoryChoices(models.TextChoices): + ALABAMA = "AL", "Alabama (AL)" + ALASKA = "AK", "Alaska (AK)" + AMERICAN_SAMOA = "AS", "American Samoa (AS)" + ARIZONA = "AZ", "Arizona (AZ)" + ARKANSAS = "AR", "Arkansas (AR)" + CALIFORNIA = "CA", "California (CA)" + COLORADO = "CO", "Colorado (CO)" + CONNECTICUT = "CT", "Connecticut (CT)" + DELAWARE = "DE", "Delaware (DE)" + DISTRICT_OF_COLUMBIA = "DC", "District of Columbia (DC)" + FLORIDA = "FL", "Florida (FL)" + GEORGIA = "GA", "Georgia (GA)" + GUAM = "GU", "Guam (GU)" + HAWAII = "HI", "Hawaii (HI)" + IDAHO = "ID", "Idaho (ID)" + ILLINOIS = "IL", "Illinois (IL)" + INDIANA = "IN", "Indiana (IN)" + IOWA = "IA", "Iowa (IA)" + KANSAS = "KS", "Kansas (KS)" + KENTUCKY = "KY", "Kentucky (KY)" + LOUISIANA = "LA", "Louisiana (LA)" + MAINE = "ME", "Maine (ME)" + MARYLAND = "MD", "Maryland (MD)" + MASSACHUSETTS = "MA", "Massachusetts (MA)" + MICHIGAN = "MI", "Michigan (MI)" + MINNESOTA = "MN", "Minnesota (MN)" + MISSISSIPPI = "MS", "Mississippi (MS)" + MISSOURI = "MO", "Missouri (MO)" + MONTANA = "MT", "Montana (MT)" + NEBRASKA = "NE", "Nebraska (NE)" + NEVADA = "NV", "Nevada (NV)" + NEW_HAMPSHIRE = "NH", "New Hampshire (NH)" + NEW_JERSEY = "NJ", "New Jersey (NJ)" + NEW_MEXICO = "NM", "New Mexico (NM)" + NEW_YORK = "NY", "New York (NY)" + NORTH_CAROLINA = "NC", "North Carolina (NC)" + NORTH_DAKOTA = "ND", "North Dakota (ND)" + NORTHERN_MARIANA_ISLANDS = "MP", "Northern Mariana Islands (MP)" + OHIO = "OH", "Ohio (OH)" + OKLAHOMA = "OK", "Oklahoma (OK)" + OREGON = "OR", "Oregon (OR)" + PENNSYLVANIA = "PA", "Pennsylvania (PA)" + PUERTO_RICO = "PR", "Puerto Rico (PR)" + RHODE_ISLAND = "RI", "Rhode Island (RI)" + SOUTH_CAROLINA = "SC", "South Carolina (SC)" + SOUTH_DAKOTA = "SD", "South Dakota (SD)" + TENNESSEE = "TN", "Tennessee (TN)" + TEXAS = "TX", "Texas (TX)" + UNITED_STATES_MINOR_OUTLYING_ISLANDS = ( + "UM", + "United States Minor Outlying Islands (UM)", + ) + UTAH = "UT", "Utah (UT)" + VERMONT = "VT", "Vermont (VT)" + VIRGIN_ISLANDS = "VI", "Virgin Islands (VI)" + VIRGINIA = "VA", "Virginia (VA)" + WASHINGTON = "WA", "Washington (WA)" + WEST_VIRGINIA = "WV", "West Virginia (WV)" + WISCONSIN = "WI", "Wisconsin (WI)" + WYOMING = "WY", "Wyoming (WY)" + ARMED_FORCES_AA = "AA", "Armed Forces Americas (AA)" + ARMED_FORCES_AE = "AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)" + ARMED_FORCES_AP = "AP", "Armed Forces Pacific (AP)" + + class OrganizationChoices(models.TextChoices): + FEDERAL = ( + "federal", + "Federal: an agency of the U.S. government's executive, legislative, " + "or judicial branches", + ) + INTERSTATE = "interstate", "Interstate: an organization of two or more states" + STATE_OR_TERRITORY = "state_or_territory", ( + "State or territory: one of the 50 U.S. states, the District of " + "Columbia, American Samoa, Guam, Northern Mariana Islands, " + "Puerto Rico, or the U.S. Virgin Islands" + ) + TRIBAL = "tribal", ( + "Tribal: a tribal government recognized by the federal or " + "a state government" + ) + COUNTY = "county", "County: a county, parish, or borough" + CITY = "city", "City: a city, town, township, village, etc." + SPECIAL_DISTRICT = "special_district", ( + "Special district: an independent organization within a single state" + ) + SCHOOL_DISTRICT = "school_district", ( + "School district: a school district that is not part of a local government" + ) + + class BranchChoices(models.TextChoices): + EXECUTIVE = "executive", "Executive" + JUDICIAL = "judicial", "Judicial" + LEGISLATIVE = "legislative", "Legislative" + + AGENCIES = [ + "Administrative Conference of the United States", + "Advisory Council on Historic Preservation", + "American Battle Monuments Commission", + "Appalachian Regional Commission", + ( + "Appraisal Subcommittee of the Federal Financial " + "Institutions Examination Council" + ), + "Armed Forces Retirement Home", + "Barry Goldwater Scholarship and Excellence in Education Program", + "Central Intelligence Agency", + "Christopher Columbus Fellowship Foundation", + "Commission for the Preservation of America's Heritage Abroad", + "Commission of Fine Arts", + "Committee for Purchase From People Who Are Blind or Severely Disabled", + "Commodity Futures Trading Commission", + "Consumer Financial Protection Bureau", + "Consumer Product Safety Commission", + "Corporation for National and Community Service", + "Council of Inspectors General on Integrity and Efficiency", + "DC Court Services and Offender Supervision Agency", + "DC Pre-trial Services", + "Defense Nuclear Facilities Safety Board", + "Delta Regional Authority", + "Denali Commission", + "Department of Agriculture", + "Department of Commerce", + "Department of Defense", + "Department of Education", + "Department of Energy", + "Department of Health and Human Services", + "Department of Homeland Security", + "Department of Housing and Urban Development", + "Department of Justice", + "Department of Labor", + "Department of State", + "Department of the Interior", + "Department of the Treasury", + "Department of Transportation", + "Department of Veterans Affairs", + "Director of National Intelligence", + "Dwight D. Eisenhower Memorial Commission", + "Election Assistance Commission", + "Environmental Protection Agency", + "Equal Employment Opportunity Commission", + "Export-Import Bank of the United States", + "Farm Credit Administration", + "Farm Credit System Insurance Corporation", + "Federal Communications Commission", + "Federal Deposit Insurance Corporation", + "Federal Election Commission", + "Federal Financial Institutions Examination Council", + "Federal Housing Finance Agency", + "Federal Judiciary", + "Federal Labor Relations Authority", + "Federal Maritime Commission", + "Federal Mediation and Conciliation Service", + "Federal Mine Safety and Health Review Commission", + "Federal Reserve System", + "Federal Trade Commission", + "General Services Administration", + "Gulf Coast Ecosystem Restoration Council", + "Harry S Truman Scholarship Foundation", + "Institute of Peace", + "Inter-American Foundation", + "International Boundary and Water Commission: United States and Mexico", + "International Boundary Commission: United States and Canada", + "International Joint Commission: United States and Canada", + "James Madison Memorial Fellowship Foundation", + "Japan-United States Friendship Commission", + "John F. Kennedy Center for the Performing Arts", + "Legal Services Corporation", + "Legislative Branch", + "Marine Mammal Commission", + "Medicare Payment Advisory Commission", + "Merit Systems Protection Board", + "Millennium Challenge Corporation", + "National Aeronautics and Space Administration", + "National Archives and Records Administration", + "National Capital Planning Commission", + "National Council on Disability", + "National Credit Union Administration", + "National Foundation on the Arts and the Humanities", + "National Gallery of Art", + "National Labor Relations Board", + "National Mediation Board", + "National Science Foundation", + "National Transportation Safety Board", + "Northern Border Regional Commission", + "Nuclear Regulatory Commission", + "Nuclear Safety Oversight Committee", + "Nuclear Waste Technical Review Board", + "Occupational Safety and Health Review Commission", + "Office of Compliance", + "Office of Government Ethics", + "Office of Navajo and Hopi Indian Relocation", + "Office of Personnel Management", + "Overseas Private Investment Corporation", + "Peace Corps", + "Pension Benefit Guaranty Corporation", + "Postal Regulatory Commission", + "Privacy and Civil Liberties Oversight Board", + "Public Defender Service for the District of Columbia", + "Railroad Retirement Board", + "Securities and Exchange Commission", + "Selective Service System", + "Small Business Administration", + "Smithsonian Institution", + "Social Security Administration", + "State Justice Institute", + "State, Local, and Tribal Government", + "Stennis Center for Public Service", + "Surface Transportation Board", + "Tennessee Valley Authority", + "The Executive Office of the President", + "U.S. Access Board", + "U.S. Agency for Global Media", + "U.S. Agency for International Development", + "U.S. Chemical Safety Board", + "U.S. China Economic and Security Review Commission", + "U.S. Commission on Civil Rights", + "U.S. Commission on International Religious Freedom", + "U.S. Interagency Council on Homelessness", + "U.S. International Trade Commission", + "U.S. Office of Special Counsel", + "U.S. Postal Service", + "U.S. Trade and Development Agency", + "Udall Foundation", + "United States African Development Foundation", + "United States Arctic Research Commission", + "United States Holocaust Memorial Museum", + "Utah Reclamation Mitigation and Conservation Commission", + "Vietnam Education Foundation", + "Woodrow Wilson International Center for Scholars", + "World War I Centennial Commission", + ] + AGENCY_CHOICES = [(v, v) for v in AGENCIES] + + # This is the application user who created this application. The contact + # information that they gave is in the `submitter` field + creator = models.ForeignKey( + "registrar.User", + on_delete=models.PROTECT, + related_name="information_created", + ) + investigator = models.ForeignKey( + "registrar.User", + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="information_investigating", + ) + + # ##### data fields from the initial form ##### + organization_type = models.CharField( + max_length=255, + choices=OrganizationChoices.choices, + null=True, + blank=True, + help_text="Type of Organization", + ) + + federally_recognized_tribe = models.BooleanField( + null=True, + help_text="Is the tribe federally recognized", + ) + + state_recognized_tribe = models.BooleanField( + null=True, + help_text="Is the tribe recognized by a state", + ) + + tribe_name = models.TextField( + null=True, + blank=True, + help_text="Name of tribe", + ) + + federal_agency = models.TextField( + null=True, + blank=True, + help_text="Federal agency", + ) + + federal_type = models.CharField( + max_length=50, + choices=BranchChoices.choices, + null=True, + blank=True, + help_text="Federal government branch", + ) + + is_election_board = models.BooleanField( + null=True, + blank=True, + help_text="Is your organization an election office?", + ) + + organization_name = models.TextField( + null=True, + blank=True, + help_text="Organization name", + db_index=True, + ) + address_line1 = models.TextField( + null=True, + blank=True, + help_text="Street address", + ) + address_line2 = models.CharField( + max_length=15, + null=True, + blank=True, + help_text="Street address line 2", + ) + city = models.TextField( + null=True, + blank=True, + help_text="City", + ) + state_territory = models.CharField( + max_length=2, + null=True, + blank=True, + help_text="State, territory, or military post", + ) + zipcode = models.CharField( + max_length=10, + null=True, + blank=True, + help_text="Zip code", + db_index=True, + ) + urbanization = models.TextField( + null=True, + blank=True, + help_text="Urbanization (Puerto Rico only)", + ) + + type_of_work = 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", + ) + + authorizing_official = models.ForeignKey( + "registrar.Contact", + null=True, + blank=True, + related_name="information_authorizing_official", + on_delete=models.PROTECT, + ) + + domain = models.ForeignKey( + "registrar.Domain", + on_delete=models.PROTECT, + blank=True, + null=True, + related_name="domain_info", #Access this information via Domain as "domain.info" + help_text="Domain to which this information belongs" + ) + alternative_domains = models.ManyToManyField( + "registrar.Website", + blank=True, + related_name="alternatives+", + ) + + # This is the contact information provided by the applicant. The + # application user who created it is in the `creator` field. + submitter = models.ForeignKey( + "registrar.Contact", + null=True, + blank=True, + related_name="submitted_applications_information", + on_delete=models.PROTECT, + ) + + purpose = models.TextField( + null=True, + blank=True, + help_text="Purpose of your domain", + ) + + other_contacts = models.ManyToManyField( + "registrar.Contact", + blank=True, + related_name="contact_applications_information", + ) + + no_other_contacts_rationale = models.TextField( + null=True, + blank=True, + help_text="Reason for listing no additional contacts", + ) + + anything_else = models.TextField( + null=True, + blank=True, + help_text="Anything else we should know?", + ) + + is_policy_acknowledged = models.BooleanField( + null=True, + blank=True, + help_text="Acknowledged .gov acceptable use policy", + ) security_email = models.EmailField( max_length=320, null=True, blank=True, help_text="Security email for public use", ) + + def __str__(self): + try: + if self.domain and self.domain.name: + return self.domain.name + else: + return f"application created by {self.creator}" + except Exception: + return "" + + @classmethod + def create_from_da_dict(cls, da_dict): + """Takes in a DomainApplication dict and converts it into DomainInformation""" + # we don't want to pass the id to avoid conflicts + da_dict.pop("id") + # the following information below is not needed in the domain information: + da_dict.pop("status") + da_dict.pop("current_websites") + # use the requested_domain to create information for this domain + da_dict["domain"] = da_dict.pop("requested_domain") + other_contacts = da_dict.pop("other_contacts") + alternative_domains = da_dict.pop("alternative_domains") #just in case + domain_info = cls(**da_dict) + + #Save so the object now have PK (needed to process the manytomany below before first) + domain_info.save() + + #Process the remaining "many to many" stuff + domain_info.other_contacts.add(*other_contacts) + domain_info.alternative_domains.add(*alternative_domains) + domain_info.save() + return domain_info + + + class Meta: + verbose_name_plural = "Domain Information" \ No newline at end of file From 2ade162ec418575278a3c9a17bc775d86631d6f8 Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Mon, 8 May 2023 11:31:10 -0400 Subject: [PATCH 11/35] tweak to prevent duplication --- src/registrar/fixtures.py | 5 ++++ .../migrations/0018_domaininformation.py | 15 +++++++++-- src/registrar/models/domain_application.py | 4 +-- src/registrar/models/domain_information.py | 27 ++++++++++++++----- 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/registrar/fixtures.py b/src/registrar/fixtures.py index ee226b7cb..171fd3e22 100644 --- a/src/registrar/fixtures.py +++ b/src/registrar/fixtures.py @@ -43,6 +43,11 @@ class UserFixture: "username": "2ffe71b0-cea4-4097-8fb6-7a35b901dd70", "first_name": "Neil", "last_name": "Martinsen-Burrell", + }, + { + "username": "7185e6cd-d3c8-4adc-90a3-ceddba71d24f", + "first_name": "Jon", + "last_name": "Roberts", }, ] diff --git a/src/registrar/migrations/0018_domaininformation.py b/src/registrar/migrations/0018_domaininformation.py index c85943990..ef6a28226 100644 --- a/src/registrar/migrations/0018_domaininformation.py +++ b/src/registrar/migrations/0018_domaininformation.py @@ -1,4 +1,4 @@ -# Generated by Django 4.1.6 on 2023-05-08 12:48 +# Generated by Django 4.1.6 on 2023-05-08 15:30 from django.conf import settings from django.db import migrations, models @@ -233,7 +233,7 @@ class Migration(migrations.Migration): ), ( "domain", - models.ForeignKey( + models.OneToOneField( blank=True, help_text="Domain to which this information belongs", null=True, @@ -242,6 +242,17 @@ class Migration(migrations.Migration): to="registrar.domain", ), ), + ( + "domain_application", + models.OneToOneField( + blank=True, + help_text="Associated domain application", + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="domainapplication_info", + to="registrar.domainapplication", + ), + ), ( "investigator", models.ForeignKey( diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index 5e50c88e9..bdcde15cc 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -527,7 +527,7 @@ class DomainApplication(TimeStampedModel): # remove PK from domainapplication as it use different PK # for domain/domaininformation - domain_info, _ = DomainInformation.create_from_da_dict(domain_info) + domain_info = DomainInformation.create_from_da(self) # create the permission for the user UserDomainRole = apps.get_model("registrar.UserDomainRole") @@ -595,7 +595,7 @@ class DomainApplication(TimeStampedModel): # import pdb; pdb.set_trace() if field.get_internal_type() in ("ForeignKey", "OneToOneField"): # get the related instance of the FK value - print(f"{field.name}: ID: {field.value_from_object(instance)}") + # print(f"{field.name}: ID: {field.value_from_object(instance)}") fk_id = field.value_from_object(instance) if fk_id: data[field.name] = field.related_model.objects.get(id=fk_id) diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index b6adc6c52..ab7f75247 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -13,7 +13,7 @@ logger = logging.getLogger(__name__) class DomainInformation(TimeStampedModel): - """A registrant's application for a new domain.""" + """A registrant's domain information for that domain, exported from DomainApplication.""" class StateTerritoryChoices(models.TextChoices): ALABAMA = "AL", "Alabama (AL)" @@ -264,6 +264,16 @@ class DomainInformation(TimeStampedModel): related_name="information_investigating", ) + domain_application = models.OneToOneField( + "registrar.DomainApplication", + on_delete=models.PROTECT, + blank=True, + null=True, + related_name="domainapplication_info", + help_text="Associated domain application", + unique=True + ) + # ##### data fields from the initial form ##### organization_type = models.CharField( max_length=255, @@ -370,7 +380,7 @@ class DomainInformation(TimeStampedModel): on_delete=models.PROTECT, ) - domain = models.ForeignKey( + domain = models.OneToOneField( "registrar.Domain", on_delete=models.PROTECT, blank=True, @@ -440,10 +450,15 @@ class DomainInformation(TimeStampedModel): return "" @classmethod - def create_from_da_dict(cls, da_dict): + def create_from_da(cls, domain_application): """Takes in a DomainApplication dict and converts it into DomainInformation""" - # we don't want to pass the id to avoid conflicts - da_dict.pop("id") + da_dict = domain_application.to_dict() + # remove the id so one can be assinged on creation + da_id = da_dict.pop("id") + # check if we have a record that corresponds with the domain application, if so short circuit the create + domain_info = cls.objects.filter(domain_application__id=da_id).first() + if domain_info: + return domain_info # the following information below is not needed in the domain information: da_dict.pop("status") da_dict.pop("current_websites") @@ -452,7 +467,7 @@ class DomainInformation(TimeStampedModel): other_contacts = da_dict.pop("other_contacts") alternative_domains = da_dict.pop("alternative_domains") #just in case domain_info = cls(**da_dict) - + domain_info.domain_application = domain_application #Save so the object now have PK (needed to process the manytomany below before first) domain_info.save() From d1dea7fecdaf3279d47e13d984ad91af71a4b2ef Mon Sep 17 00:00:00 2001 From: igorkorenfeld Date: Mon, 8 May 2023 11:54:31 -0400 Subject: [PATCH 12/35] Add 404 page template --- src/registrar/templates/404.html | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/registrar/templates/404.html b/src/registrar/templates/404.html index f715e0fa9..c6bf6f5a8 100644 --- a/src/registrar/templates/404.html +++ b/src/registrar/templates/404.html @@ -1,15 +1,31 @@ - {% extends "base.html" %} -{% load i18n %} +{% load i18n static %} {% block title %}{% translate "Page not found" %}{% endblock %} {% block content %}
+
+
+

+ {% translate "We couldn’t find that page" %} +

+

+ {% translate "Status 404" %} +

-

{% translate "Page not found" %}

+

Try going to the homepage. If you can’t find what you’re looking for, contact us. +

+
+ +
+ +
+
-

{% translate "The requested page could not be found." %}

{% endblock %} From 85d0152c3cca0e42f123d7587d2fc7af2c83af3f Mon Sep 17 00:00:00 2001 From: igorkorenfeld Date: Mon, 8 May 2023 11:54:45 -0400 Subject: [PATCH 13/35] Add 500 page template --- src/registrar/templates/500.html | 45 +++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/registrar/templates/500.html b/src/registrar/templates/500.html index 5fbd30d2d..b3061dadd 100644 --- a/src/registrar/templates/500.html +++ b/src/registrar/templates/500.html @@ -1,24 +1,39 @@ {% extends "base.html" %} -{% load i18n %} +{% load i18n static %} {% block title %}{% translate "Server error" %}{% endblock %} {% block content %}
-

{% translate "Server Error" %}

+
+
+

+ {% translate "We're having some trouble" %} +

+

+ {% translate "Status 500 – server error" %} +

+ {% if friendly_message %} +

{{ friendly_message }}

+ {% else %} +

+ Sorry! Try waiting a few minutes and then reloading the page. + Contact us if you need help. +

+ {% endif %} - {% if friendly_message %} -

{{ friendly_message }}

- {% else %} -

{% translate "An internal server error occurred." %}

- {% endif %} - - {% if log_identifier %} -

Here's a unique identifier for this error.

-
{{ log_identifier }}
-

{% translate "Please include it if you contact us." %}

- {% endif %} - - TODO: Content team to create a "how to contact us" footer for the error pages + {% if log_identifier %} +

Here's a unique identifier for this error.

+

{{ log_identifier }}

+

{% translate "Please include it if you contact us." %}

+ {% endif %} +
+
+ +
+
{% endblock %} From 661fa195d46700b379451ada0af4a9402284d542 Mon Sep 17 00:00:00 2001 From: igorkorenfeld Date: Mon, 8 May 2023 11:55:03 -0400 Subject: [PATCH 14/35] Add 401 page template --- src/registrar/templates/401.html | 51 +++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/registrar/templates/401.html b/src/registrar/templates/401.html index 64bcec563..e5f7781c5 100644 --- a/src/registrar/templates/401.html +++ b/src/registrar/templates/401.html @@ -1,28 +1,45 @@ {% extends "base.html" %} -{% load i18n %} +{% load i18n static %} {% block title %}{% translate "Unauthorized" %}{% endblock %} {% block content %}
-

{% translate "Unauthorized" %}

+
+
+

+ {% translate "You are not authorized to view this page." %} +

+

+ {% translate "Status 401" %} +

- {% if friendly_message %} -

{{ friendly_message }}

- {% else %} -

{% translate "Authorization failed." %}

- {% endif %} -

- {% translate "Would you like to try logging in again?" %} -

+ {% if friendly_message %} +

{{ friendly_message }}

+ {% else %} +

{% translate "Authorization failed." %}

+ {% endif %} +

+ You must be an authorized user and need to be signed in to view this page. + Would you like to try loggining in again? +

+

+ If you would like help with this error contact us +

- {% if log_identifier %} -

Here's a unique identifier for this error.

-
{{ log_identifier }}
-

{% translate "Please include it if you contact us." %}

- {% endif %} - - TODO: Content team to create a "how to contact us" footer for the error pages + {% if log_identifier %} +

Here's a unique identifier for this error.

+
{{ log_identifier }}
+

{% translate "Please include it if you contact us." %}

+ {% endif %} +
+
+ +
+
{% endblock %} From 950ee0495831cd78f02b910452afd301eabf68f0 Mon Sep 17 00:00:00 2001 From: rachidatecs Date: Mon, 8 May 2023 12:15:29 -0400 Subject: [PATCH 15/35] Add Rachid to admins --- src/registrar/fixtures.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/registrar/fixtures.py b/src/registrar/fixtures.py index ee226b7cb..0ce0f4e24 100644 --- a/src/registrar/fixtures.py +++ b/src/registrar/fixtures.py @@ -44,6 +44,11 @@ class UserFixture: "first_name": "Neil", "last_name": "Martinsen-Burrell", }, + { + "username": "5f283494-31bd-49b5-b024-a7e7cae00848", + "first_name": "Rachid", + "last_name": "Mrad", + }, ] @classmethod From e91a4728d6030e8ecb6d7a9443e9cfd1125e4ea8 Mon Sep 17 00:00:00 2001 From: igorkorenfeld Date: Mon, 8 May 2023 12:28:02 -0400 Subject: [PATCH 16/35] Add illustration files --- .../public/img/registrar/dotgov_401_illo.svg | 20 +++++++ .../public/img/registrar/dotgov_404_illo.svg | 18 ++++++ .../public/img/registrar/dotgov_500_illo.svg | 59 +++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 src/registrar/public/img/registrar/dotgov_401_illo.svg create mode 100644 src/registrar/public/img/registrar/dotgov_404_illo.svg create mode 100644 src/registrar/public/img/registrar/dotgov_500_illo.svg diff --git a/src/registrar/public/img/registrar/dotgov_401_illo.svg b/src/registrar/public/img/registrar/dotgov_401_illo.svg new file mode 100644 index 000000000..71de33eaa --- /dev/null +++ b/src/registrar/public/img/registrar/dotgov_401_illo.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/registrar/public/img/registrar/dotgov_404_illo.svg b/src/registrar/public/img/registrar/dotgov_404_illo.svg new file mode 100644 index 000000000..3c9adab7e --- /dev/null +++ b/src/registrar/public/img/registrar/dotgov_404_illo.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/registrar/public/img/registrar/dotgov_500_illo.svg b/src/registrar/public/img/registrar/dotgov_500_illo.svg new file mode 100644 index 000000000..6dd538644 --- /dev/null +++ b/src/registrar/public/img/registrar/dotgov_500_illo.svg @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 3a2d9f5ada77e7575ea682c984bfd19cbbf0ec03 Mon Sep 17 00:00:00 2001 From: igorkorenfeld Date: Mon, 8 May 2023 12:52:39 -0400 Subject: [PATCH 17/35] Update test to match new content on 500 error --- src/djangooidc/tests/test_views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/djangooidc/tests/test_views.py b/src/djangooidc/tests/test_views.py index a3086db50..30caf3713 100644 --- a/src/djangooidc/tests/test_views.py +++ b/src/djangooidc/tests/test_views.py @@ -49,7 +49,7 @@ class ViewsTest(TestCase): # assert self.assertEqual(response.status_code, 500) self.assertTemplateUsed(response, "500.html") - self.assertIn("Server Error", response.content.decode("utf-8")) + self.assertIn("server error", response.content.decode("utf-8")) def test_login_callback_reads_next(self, mock_client): # setup From 43012e1af1ca0f0e25f1eb641394387049516d09 Mon Sep 17 00:00:00 2001 From: igorkorenfeld Date: Mon, 8 May 2023 13:04:07 -0400 Subject: [PATCH 18/35] Try deploy build with no-input option on collect static --- .github/workflows/deploy-sandbox.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-sandbox.yaml b/.github/workflows/deploy-sandbox.yaml index bc2b2d0b1..c0492c8ea 100644 --- a/.github/workflows/deploy-sandbox.yaml +++ b/.github/workflows/deploy-sandbox.yaml @@ -40,7 +40,7 @@ jobs: docker compose run node npx gulp compile - name: Collect static assets working-directory: ./src - run: docker compose run app python manage.py collectstatic + run: docker compose run app python manage.py collectstatic --no-input - name: Deploy to cloud.gov sandbox uses: 18f/cg-deploy-action@main env: @@ -69,4 +69,4 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, body: '🥳 Successfully deployed to developer sandbox **[${{ env.ENVIRONMENT }}](https://getgov-${{ env.ENVIRONMENT }}.app.cloud.gov/)**.' - }) \ No newline at end of file + }) From 62ebc07382f80fdf668ae470692b73c3699c8019 Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Mon, 8 May 2023 13:11:43 -0400 Subject: [PATCH 19/35] simplify the choices --- src/registrar/models/domain_information.py | 234 +-------------------- 1 file changed, 4 insertions(+), 230 deletions(-) diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index ab7f75247..0c666f594 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -15,239 +15,13 @@ class DomainInformation(TimeStampedModel): """A registrant's domain information for that domain, exported from DomainApplication.""" - class StateTerritoryChoices(models.TextChoices): - ALABAMA = "AL", "Alabama (AL)" - ALASKA = "AK", "Alaska (AK)" - AMERICAN_SAMOA = "AS", "American Samoa (AS)" - ARIZONA = "AZ", "Arizona (AZ)" - ARKANSAS = "AR", "Arkansas (AR)" - CALIFORNIA = "CA", "California (CA)" - COLORADO = "CO", "Colorado (CO)" - CONNECTICUT = "CT", "Connecticut (CT)" - DELAWARE = "DE", "Delaware (DE)" - DISTRICT_OF_COLUMBIA = "DC", "District of Columbia (DC)" - FLORIDA = "FL", "Florida (FL)" - GEORGIA = "GA", "Georgia (GA)" - GUAM = "GU", "Guam (GU)" - HAWAII = "HI", "Hawaii (HI)" - IDAHO = "ID", "Idaho (ID)" - ILLINOIS = "IL", "Illinois (IL)" - INDIANA = "IN", "Indiana (IN)" - IOWA = "IA", "Iowa (IA)" - KANSAS = "KS", "Kansas (KS)" - KENTUCKY = "KY", "Kentucky (KY)" - LOUISIANA = "LA", "Louisiana (LA)" - MAINE = "ME", "Maine (ME)" - MARYLAND = "MD", "Maryland (MD)" - MASSACHUSETTS = "MA", "Massachusetts (MA)" - MICHIGAN = "MI", "Michigan (MI)" - MINNESOTA = "MN", "Minnesota (MN)" - MISSISSIPPI = "MS", "Mississippi (MS)" - MISSOURI = "MO", "Missouri (MO)" - MONTANA = "MT", "Montana (MT)" - NEBRASKA = "NE", "Nebraska (NE)" - NEVADA = "NV", "Nevada (NV)" - NEW_HAMPSHIRE = "NH", "New Hampshire (NH)" - NEW_JERSEY = "NJ", "New Jersey (NJ)" - NEW_MEXICO = "NM", "New Mexico (NM)" - NEW_YORK = "NY", "New York (NY)" - NORTH_CAROLINA = "NC", "North Carolina (NC)" - NORTH_DAKOTA = "ND", "North Dakota (ND)" - NORTHERN_MARIANA_ISLANDS = "MP", "Northern Mariana Islands (MP)" - OHIO = "OH", "Ohio (OH)" - OKLAHOMA = "OK", "Oklahoma (OK)" - OREGON = "OR", "Oregon (OR)" - PENNSYLVANIA = "PA", "Pennsylvania (PA)" - PUERTO_RICO = "PR", "Puerto Rico (PR)" - RHODE_ISLAND = "RI", "Rhode Island (RI)" - SOUTH_CAROLINA = "SC", "South Carolina (SC)" - SOUTH_DAKOTA = "SD", "South Dakota (SD)" - TENNESSEE = "TN", "Tennessee (TN)" - TEXAS = "TX", "Texas (TX)" - UNITED_STATES_MINOR_OUTLYING_ISLANDS = ( - "UM", - "United States Minor Outlying Islands (UM)", - ) - UTAH = "UT", "Utah (UT)" - VERMONT = "VT", "Vermont (VT)" - VIRGIN_ISLANDS = "VI", "Virgin Islands (VI)" - VIRGINIA = "VA", "Virginia (VA)" - WASHINGTON = "WA", "Washington (WA)" - WEST_VIRGINIA = "WV", "West Virginia (WV)" - WISCONSIN = "WI", "Wisconsin (WI)" - WYOMING = "WY", "Wyoming (WY)" - ARMED_FORCES_AA = "AA", "Armed Forces Americas (AA)" - ARMED_FORCES_AE = "AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)" - ARMED_FORCES_AP = "AP", "Armed Forces Pacific (AP)" + StateTerritoryChoices=DomainApplication.StateTerritoryChoices - class OrganizationChoices(models.TextChoices): - FEDERAL = ( - "federal", - "Federal: an agency of the U.S. government's executive, legislative, " - "or judicial branches", - ) - INTERSTATE = "interstate", "Interstate: an organization of two or more states" - STATE_OR_TERRITORY = "state_or_territory", ( - "State or territory: one of the 50 U.S. states, the District of " - "Columbia, American Samoa, Guam, Northern Mariana Islands, " - "Puerto Rico, or the U.S. Virgin Islands" - ) - TRIBAL = "tribal", ( - "Tribal: a tribal government recognized by the federal or " - "a state government" - ) - COUNTY = "county", "County: a county, parish, or borough" - CITY = "city", "City: a city, town, township, village, etc." - SPECIAL_DISTRICT = "special_district", ( - "Special district: an independent organization within a single state" - ) - SCHOOL_DISTRICT = "school_district", ( - "School district: a school district that is not part of a local government" - ) + OrganizationChoices=DomainApplication.OrganizationChoices - class BranchChoices(models.TextChoices): - EXECUTIVE = "executive", "Executive" - JUDICIAL = "judicial", "Judicial" - LEGISLATIVE = "legislative", "Legislative" + BranchChoices=DomainApplication.BranchChoices - AGENCIES = [ - "Administrative Conference of the United States", - "Advisory Council on Historic Preservation", - "American Battle Monuments Commission", - "Appalachian Regional Commission", - ( - "Appraisal Subcommittee of the Federal Financial " - "Institutions Examination Council" - ), - "Armed Forces Retirement Home", - "Barry Goldwater Scholarship and Excellence in Education Program", - "Central Intelligence Agency", - "Christopher Columbus Fellowship Foundation", - "Commission for the Preservation of America's Heritage Abroad", - "Commission of Fine Arts", - "Committee for Purchase From People Who Are Blind or Severely Disabled", - "Commodity Futures Trading Commission", - "Consumer Financial Protection Bureau", - "Consumer Product Safety Commission", - "Corporation for National and Community Service", - "Council of Inspectors General on Integrity and Efficiency", - "DC Court Services and Offender Supervision Agency", - "DC Pre-trial Services", - "Defense Nuclear Facilities Safety Board", - "Delta Regional Authority", - "Denali Commission", - "Department of Agriculture", - "Department of Commerce", - "Department of Defense", - "Department of Education", - "Department of Energy", - "Department of Health and Human Services", - "Department of Homeland Security", - "Department of Housing and Urban Development", - "Department of Justice", - "Department of Labor", - "Department of State", - "Department of the Interior", - "Department of the Treasury", - "Department of Transportation", - "Department of Veterans Affairs", - "Director of National Intelligence", - "Dwight D. Eisenhower Memorial Commission", - "Election Assistance Commission", - "Environmental Protection Agency", - "Equal Employment Opportunity Commission", - "Export-Import Bank of the United States", - "Farm Credit Administration", - "Farm Credit System Insurance Corporation", - "Federal Communications Commission", - "Federal Deposit Insurance Corporation", - "Federal Election Commission", - "Federal Financial Institutions Examination Council", - "Federal Housing Finance Agency", - "Federal Judiciary", - "Federal Labor Relations Authority", - "Federal Maritime Commission", - "Federal Mediation and Conciliation Service", - "Federal Mine Safety and Health Review Commission", - "Federal Reserve System", - "Federal Trade Commission", - "General Services Administration", - "Gulf Coast Ecosystem Restoration Council", - "Harry S Truman Scholarship Foundation", - "Institute of Peace", - "Inter-American Foundation", - "International Boundary and Water Commission: United States and Mexico", - "International Boundary Commission: United States and Canada", - "International Joint Commission: United States and Canada", - "James Madison Memorial Fellowship Foundation", - "Japan-United States Friendship Commission", - "John F. Kennedy Center for the Performing Arts", - "Legal Services Corporation", - "Legislative Branch", - "Marine Mammal Commission", - "Medicare Payment Advisory Commission", - "Merit Systems Protection Board", - "Millennium Challenge Corporation", - "National Aeronautics and Space Administration", - "National Archives and Records Administration", - "National Capital Planning Commission", - "National Council on Disability", - "National Credit Union Administration", - "National Foundation on the Arts and the Humanities", - "National Gallery of Art", - "National Labor Relations Board", - "National Mediation Board", - "National Science Foundation", - "National Transportation Safety Board", - "Northern Border Regional Commission", - "Nuclear Regulatory Commission", - "Nuclear Safety Oversight Committee", - "Nuclear Waste Technical Review Board", - "Occupational Safety and Health Review Commission", - "Office of Compliance", - "Office of Government Ethics", - "Office of Navajo and Hopi Indian Relocation", - "Office of Personnel Management", - "Overseas Private Investment Corporation", - "Peace Corps", - "Pension Benefit Guaranty Corporation", - "Postal Regulatory Commission", - "Privacy and Civil Liberties Oversight Board", - "Public Defender Service for the District of Columbia", - "Railroad Retirement Board", - "Securities and Exchange Commission", - "Selective Service System", - "Small Business Administration", - "Smithsonian Institution", - "Social Security Administration", - "State Justice Institute", - "State, Local, and Tribal Government", - "Stennis Center for Public Service", - "Surface Transportation Board", - "Tennessee Valley Authority", - "The Executive Office of the President", - "U.S. Access Board", - "U.S. Agency for Global Media", - "U.S. Agency for International Development", - "U.S. Chemical Safety Board", - "U.S. China Economic and Security Review Commission", - "U.S. Commission on Civil Rights", - "U.S. Commission on International Religious Freedom", - "U.S. Interagency Council on Homelessness", - "U.S. International Trade Commission", - "U.S. Office of Special Counsel", - "U.S. Postal Service", - "U.S. Trade and Development Agency", - "Udall Foundation", - "United States African Development Foundation", - "United States Arctic Research Commission", - "United States Holocaust Memorial Museum", - "Utah Reclamation Mitigation and Conservation Commission", - "Vietnam Education Foundation", - "Woodrow Wilson International Center for Scholars", - "World War I Centennial Commission", - ] - AGENCY_CHOICES = [(v, v) for v in AGENCIES] + AGENCY_CHOICES=DomainApplication.AGENCY_CHOICES # This is the application user who created this application. The contact # information that they gave is in the `submitter` field From 1fee1078e045f283f396f65089d6acb55ac0a28d Mon Sep 17 00:00:00 2001 From: igorkorenfeld Date: Mon, 8 May 2023 15:06:15 -0400 Subject: [PATCH 20/35] Match styling of unique identifier to 500 page --- src/registrar/templates/401.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/templates/401.html b/src/registrar/templates/401.html index e5f7781c5..a11f42bae 100644 --- a/src/registrar/templates/401.html +++ b/src/registrar/templates/401.html @@ -30,7 +30,7 @@ {% if log_identifier %}

Here's a unique identifier for this error.

-
{{ log_identifier }}
+

{{ log_identifier }}

{% translate "Please include it if you contact us." %}

{% endif %} From e593199577fe540d63bfb35b219683e53e519331 Mon Sep 17 00:00:00 2001 From: igorkorenfeld Date: Mon, 8 May 2023 15:06:30 -0400 Subject: [PATCH 21/35] Add 403 template --- src/registrar/templates/403.html | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/registrar/templates/403.html diff --git a/src/registrar/templates/403.html b/src/registrar/templates/403.html new file mode 100644 index 000000000..fe17d646f --- /dev/null +++ b/src/registrar/templates/403.html @@ -0,0 +1,45 @@ +{% extends "base.html" %} +{% load i18n static %} + +{% block title %}{% translate "Unauthorized" %}{% endblock %} + +{% block content %} +
+
+
+

+ {% translate "You do not have the right permissions to view this page." %} +

+

+ {% translate "Status 403" %} +

+ + + {% if friendly_message %} +

{{ friendly_message }}

+ {% else %} +

{% translate "Forbidden." %}

+ {% endif %} +

+ You must be an authorized user and need to be signed in to view this page. + Would you like to try loggining in again? +

+

+ If you would like help with this error contact us +

+ + {% if log_identifier %} +

Here's a unique identifier for this error.

+

{{ log_identifier }}

+

{% translate "Please include it if you contact us." %}

+ {% endif %} +
+
+ +
+
+
+{% endblock %} From 8982d8d293a603d0b05d9b62f438a6f34f565189 Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Mon, 8 May 2023 15:30:49 -0400 Subject: [PATCH 22/35] Lint --- src/registrar/models/domain_application.py | 6 ++--- src/registrar/models/domain_information.py | 27 +++++++++++----------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index cc66eacf9..c73061216 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -13,6 +13,7 @@ from itertools import chain logger = logging.getLogger(__name__) + class DomainApplication(TimeStampedModel): """A registrant's application for a new domain.""" @@ -519,8 +520,6 @@ class DomainApplication(TimeStampedModel): Domain = apps.get_model("registrar.Domain") created_domain, _ = Domain.objects.get_or_create(name=self.requested_domain) - - # copy the information from domainapplication into domaininformation DomainInformation = apps.get_model("registrar.DomainInformation") domain_info = self.to_dict() @@ -588,7 +587,7 @@ class DomainApplication(TimeStampedModel): return False def to_dict(instance): - """This is to process to_dict for Domain Information, making it friendly to "copy" it """ + """This is to process to_dict for Domain Information, making it friendly to "copy" it""" opts = instance._meta data = {} for field in chain(opts.concrete_fields, opts.private_fields): @@ -606,4 +605,3 @@ class DomainApplication(TimeStampedModel): for field in opts.many_to_many: data[field.name] = field.value_from_object(instance) return data - diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index 0c666f594..fb814af93 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -11,17 +11,18 @@ from django.db import models logger = logging.getLogger(__name__) + class DomainInformation(TimeStampedModel): """A registrant's domain information for that domain, exported from DomainApplication.""" - StateTerritoryChoices=DomainApplication.StateTerritoryChoices + StateTerritoryChoices = DomainApplication.StateTerritoryChoices - OrganizationChoices=DomainApplication.OrganizationChoices + OrganizationChoices = DomainApplication.OrganizationChoices - BranchChoices=DomainApplication.BranchChoices + BranchChoices = DomainApplication.BranchChoices - AGENCY_CHOICES=DomainApplication.AGENCY_CHOICES + AGENCY_CHOICES = DomainApplication.AGENCY_CHOICES # This is the application user who created this application. The contact # information that they gave is in the `submitter` field @@ -41,11 +42,11 @@ class DomainInformation(TimeStampedModel): domain_application = models.OneToOneField( "registrar.DomainApplication", on_delete=models.PROTECT, - blank=True, + blank=True, null=True, related_name="domainapplication_info", help_text="Associated domain application", - unique=True + unique=True, ) # ##### data fields from the initial form ##### @@ -157,10 +158,10 @@ class DomainInformation(TimeStampedModel): domain = models.OneToOneField( "registrar.Domain", on_delete=models.PROTECT, - blank=True, + blank=True, null=True, - related_name="domain_info", #Access this information via Domain as "domain.info" - help_text="Domain to which this information belongs" + related_name="domain_info", # Access this information via Domain as "domain.info" + help_text="Domain to which this information belongs", ) alternative_domains = models.ManyToManyField( "registrar.Website", @@ -239,19 +240,17 @@ class DomainInformation(TimeStampedModel): # use the requested_domain to create information for this domain da_dict["domain"] = da_dict.pop("requested_domain") other_contacts = da_dict.pop("other_contacts") - alternative_domains = da_dict.pop("alternative_domains") #just in case + alternative_domains = da_dict.pop("alternative_domains") # just in case domain_info = cls(**da_dict) domain_info.domain_application = domain_application - #Save so the object now have PK (needed to process the manytomany below before first) + # Save so the object now have PK (needed to process the manytomany below before first) domain_info.save() - #Process the remaining "many to many" stuff + # Process the remaining "many to many" stuff domain_info.other_contacts.add(*other_contacts) domain_info.alternative_domains.add(*alternative_domains) domain_info.save() return domain_info - class Meta: verbose_name_plural = "Domain Information" - \ No newline at end of file From 4498ae7847a1054d630b753ccba0404d181a12e5 Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Mon, 8 May 2023 15:41:20 -0400 Subject: [PATCH 23/35] Lint --- src/registrar/models/domain_application.py | 15 ++------------- src/registrar/models/domain_information.py | 14 ++++++++------ 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index c73061216..67e0aeed7 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -516,18 +516,6 @@ class DomainApplication(TimeStampedModel): application into an admin on that domain. """ - # create the domain if it doesn't exist - Domain = apps.get_model("registrar.Domain") - created_domain, _ = Domain.objects.get_or_create(name=self.requested_domain) - - # copy the information from domainapplication into domaininformation - DomainInformation = apps.get_model("registrar.DomainInformation") - domain_info = self.to_dict() - # remove PK from domainapplication as it use different PK - # for domain/domaininformation - - domain_info = DomainInformation.create_from_da(self) - # create the permission for the user UserDomainRole = apps.get_model("registrar.UserDomainRole") UserDomainRole.objects.get_or_create( @@ -587,7 +575,8 @@ class DomainApplication(TimeStampedModel): return False def to_dict(instance): - """This is to process to_dict for Domain Information, making it friendly to "copy" it""" + """This is to process to_dict for Domain Information, making it friendly + to "copy" it""" opts = instance._meta data = {} for field in chain(opts.concrete_fields, opts.private_fields): diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index fb814af93..0d1864496 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -1,11 +1,9 @@ from __future__ import annotations -from typing import Union from .domain_application import DomainApplication from .utility.time_stamped_model import TimeStampedModel import logging -from django.apps import apps from django.db import models @@ -14,7 +12,8 @@ logger = logging.getLogger(__name__) class DomainInformation(TimeStampedModel): - """A registrant's domain information for that domain, exported from DomainApplication.""" + """A registrant's domain information for that domain, exported from + DomainApplication.""" StateTerritoryChoices = DomainApplication.StateTerritoryChoices @@ -160,7 +159,8 @@ class DomainInformation(TimeStampedModel): on_delete=models.PROTECT, blank=True, null=True, - related_name="domain_info", # Access this information via Domain as "domain.info" + # Access this information via Domain as "domain.info" + related_name="domain_info", help_text="Domain to which this information belongs", ) alternative_domains = models.ManyToManyField( @@ -230,7 +230,8 @@ class DomainInformation(TimeStampedModel): da_dict = domain_application.to_dict() # remove the id so one can be assinged on creation da_id = da_dict.pop("id") - # check if we have a record that corresponds with the domain application, if so short circuit the create + # check if we have a record that corresponds with the domain + # application, if so short circuit the create domain_info = cls.objects.filter(domain_application__id=da_id).first() if domain_info: return domain_info @@ -243,7 +244,8 @@ class DomainInformation(TimeStampedModel): alternative_domains = da_dict.pop("alternative_domains") # just in case domain_info = cls(**da_dict) domain_info.domain_application = domain_application - # Save so the object now have PK (needed to process the manytomany below before first) + # Save so the object now have PK + # (needed to process the manytomany below before, first) domain_info.save() # Process the remaining "many to many" stuff From 39598bbc217c5f3b22c0c0d7edfaf6d73cb55b10 Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Mon, 8 May 2023 15:50:12 -0400 Subject: [PATCH 24/35] accidently removed something, putting back in --- src/registrar/models/domain_application.py | 4 ++++ src/registrar/models/domain_information.py | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index 67e0aeed7..14f542490 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -516,6 +516,10 @@ class DomainApplication(TimeStampedModel): application into an admin on that domain. """ + # create the domain if it doesn't exist + Domain = apps.get_model("registrar.Domain") + created_domain, _ = Domain.objects.get_or_create(name=self.requested_domain) + # create the permission for the user UserDomainRole = apps.get_model("registrar.UserDomainRole") UserDomainRole.objects.get_or_create( diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index 0d1864496..270592753 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -12,7 +12,7 @@ logger = logging.getLogger(__name__) class DomainInformation(TimeStampedModel): - """A registrant's domain information for that domain, exported from + """A registrant's domain information for that domain, exported from DomainApplication.""" StateTerritoryChoices = DomainApplication.StateTerritoryChoices @@ -160,7 +160,7 @@ class DomainInformation(TimeStampedModel): blank=True, null=True, # Access this information via Domain as "domain.info" - related_name="domain_info", + related_name="domain_info", help_text="Domain to which this information belongs", ) alternative_domains = models.ManyToManyField( @@ -230,7 +230,7 @@ class DomainInformation(TimeStampedModel): da_dict = domain_application.to_dict() # remove the id so one can be assinged on creation da_id = da_dict.pop("id") - # check if we have a record that corresponds with the domain + # check if we have a record that corresponds with the domain # application, if so short circuit the create domain_info = cls.objects.filter(domain_application__id=da_id).first() if domain_info: @@ -244,7 +244,7 @@ class DomainInformation(TimeStampedModel): alternative_domains = da_dict.pop("alternative_domains") # just in case domain_info = cls(**da_dict) domain_info.domain_application = domain_application - # Save so the object now have PK + # Save so the object now have PK # (needed to process the manytomany below before, first) domain_info.save() From 3a1195b1a6c48ff61eb913405e08740b12b167fe Mon Sep 17 00:00:00 2001 From: igorkorenfeld Date: Mon, 8 May 2023 16:39:35 -0400 Subject: [PATCH 25/35] Fix spelling mistake, remove period from title --- src/registrar/templates/401.html | 4 ++-- src/registrar/templates/403.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/registrar/templates/401.html b/src/registrar/templates/401.html index a11f42bae..62cbba0b6 100644 --- a/src/registrar/templates/401.html +++ b/src/registrar/templates/401.html @@ -8,7 +8,7 @@

- {% translate "You are not authorized to view this page." %} + {% translate "You are not authorized to view this page" %}

{% translate "Status 401" %} @@ -22,7 +22,7 @@ {% endif %}

You must be an authorized user and need to be signed in to view this page. - Would you like to try loggining in again? + Would you like to try logging in again?

If you would like help with this error contact us diff --git a/src/registrar/templates/403.html b/src/registrar/templates/403.html index fe17d646f..2d1bc5d7f 100644 --- a/src/registrar/templates/403.html +++ b/src/registrar/templates/403.html @@ -22,7 +22,7 @@ {% endif %}

You must be an authorized user and need to be signed in to view this page. - Would you like to try loggining in again? + Would you like to try logging in again?

If you would like help with this error contact us From f1f3c525a29a3e88a73301818c15483b6e9d9439 Mon Sep 17 00:00:00 2001 From: Alysia Broddrick Date: Mon, 8 May 2023 13:52:28 -0700 Subject: [PATCH 26/35] added alysia to admins --- src/registrar/fixtures.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/registrar/fixtures.py b/src/registrar/fixtures.py index 0ce0f4e24..e58683c56 100644 --- a/src/registrar/fixtures.py +++ b/src/registrar/fixtures.py @@ -49,6 +49,11 @@ class UserFixture: "first_name": "Rachid", "last_name": "Mrad", }, + { + "username": "eb2214cd-fc0c-48c0-9dbd-bc4cd6820c74", + "first_name": "Alysia", + "last_name": "Broddrick", + }, ] @classmethod From 0274f6443976be3ff321f948ff7c98e2454f1bad Mon Sep 17 00:00:00 2001 From: igorkorenfeld Date: Tue, 9 May 2023 10:27:59 -0400 Subject: [PATCH 27/35] Add vertical bar to titles, change title for 403 --- src/registrar/templates/401.html | 2 +- src/registrar/templates/403.html | 2 +- src/registrar/templates/404.html | 2 +- src/registrar/templates/500.html | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/registrar/templates/401.html b/src/registrar/templates/401.html index 62cbba0b6..9fe0194ed 100644 --- a/src/registrar/templates/401.html +++ b/src/registrar/templates/401.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% load i18n static %} -{% block title %}{% translate "Unauthorized" %}{% endblock %} +{% block title %}{% translate "Unauthorized | " %}{% endblock %} {% block content %}

diff --git a/src/registrar/templates/403.html b/src/registrar/templates/403.html index 2d1bc5d7f..cc8c98656 100644 --- a/src/registrar/templates/403.html +++ b/src/registrar/templates/403.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% load i18n static %} -{% block title %}{% translate "Unauthorized" %}{% endblock %} +{% block title %}{% translate "Forbidden | " %}{% endblock %} {% block content %}
diff --git a/src/registrar/templates/404.html b/src/registrar/templates/404.html index c6bf6f5a8..76a301187 100644 --- a/src/registrar/templates/404.html +++ b/src/registrar/templates/404.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% load i18n static %} -{% block title %}{% translate "Page not found" %}{% endblock %} +{% block title %}{% translate "Page not found | " %}{% endblock %} {% block content %}
diff --git a/src/registrar/templates/500.html b/src/registrar/templates/500.html index b3061dadd..7b7e1dfed 100644 --- a/src/registrar/templates/500.html +++ b/src/registrar/templates/500.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% load i18n static %} -{% block title %}{% translate "Server error" %}{% endblock %} +{% block title %}{% translate "Server error | " %}{% endblock %} {% block content %}
From 54e6121f46075bb3caea8c3e91dbd8c0b42b2b2a Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Tue, 9 May 2023 15:26:23 -0400 Subject: [PATCH 28/35] added and cleaned up as per PR review --- src/registrar/models/domain_application.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index 14f542490..1c296b897 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -580,14 +580,15 @@ class DomainApplication(TimeStampedModel): def to_dict(instance): """This is to process to_dict for Domain Information, making it friendly - to "copy" it""" + to "copy" it + + More information can be found at this- (This dev used #5) + https://stackoverflow.com/questions/21925671/convert-django-model-object-to-dict-with-all-of-the-fields-intact/29088221#29088221""" opts = instance._meta data = {} for field in chain(opts.concrete_fields, opts.private_fields): - # import pdb; pdb.set_trace() if field.get_internal_type() in ("ForeignKey", "OneToOneField"): # get the related instance of the FK value - # print(f"{field.name}: ID: {field.value_from_object(instance)}") fk_id = field.value_from_object(instance) if fk_id: data[field.name] = field.related_model.objects.get(id=fk_id) From 08b2060f35a0367083ce944c32dfc7a2c16fcf32 Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Tue, 9 May 2023 16:27:09 -0400 Subject: [PATCH 29/35] PR feedback and new migration file based on PR #551 --- .../migrations/0018_domaininformation.py | 16 ------- ...ter_domainapplication_organization_type.py | 47 +++++++++++++++++++ src/registrar/models/domain_application.py | 2 +- src/registrar/models/domain_information.py | 16 +------ 4 files changed, 50 insertions(+), 31 deletions(-) create mode 100644 src/registrar/migrations/0019_alter_domainapplication_organization_type.py diff --git a/src/registrar/migrations/0018_domaininformation.py b/src/registrar/migrations/0018_domaininformation.py index ef6a28226..408fa048b 100644 --- a/src/registrar/migrations/0018_domaininformation.py +++ b/src/registrar/migrations/0018_domaininformation.py @@ -207,12 +207,6 @@ class Migration(migrations.Migration): null=True, ), ), - ( - "alternative_domains", - models.ManyToManyField( - blank=True, related_name="alternatives+", to="registrar.website" - ), - ), ( "authorizing_official", models.ForeignKey( @@ -253,16 +247,6 @@ class Migration(migrations.Migration): to="registrar.domainapplication", ), ), - ( - "investigator", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="information_investigating", - to=settings.AUTH_USER_MODEL, - ), - ), ( "other_contacts", models.ManyToManyField( diff --git a/src/registrar/migrations/0019_alter_domainapplication_organization_type.py b/src/registrar/migrations/0019_alter_domainapplication_organization_type.py new file mode 100644 index 000000000..1a7397255 --- /dev/null +++ b/src/registrar/migrations/0019_alter_domainapplication_organization_type.py @@ -0,0 +1,47 @@ +# Generated by Django 4.1.6 on 2023-05-09 19:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("registrar", "0018_domaininformation"), + ] + + operations = [ + migrations.AlterField( + model_name="domainapplication", + name="organization_type", + field=models.CharField( + blank=True, + choices=[ + ( + "federal", + "Federal: an agency of the U.S. government's executive, legislative, or judicial branches", + ), + ("interstate", "Interstate: an organization of two or more states"), + ( + "state_or_territory", + "State or territory: one of the 50 U.S. states, the District of Columbia, American Samoa, Guam, Northern Mariana Islands, Puerto Rico, or the U.S. Virgin Islands", + ), + ( + "tribal", + "Tribal: a tribal government recognized by the federal or a state government", + ), + ("county", "County: a county, parish, or borough"), + ("city", "City: a city, town, township, village, etc."), + ( + "special_district", + "Special district: an independent organization within a single state", + ), + ( + "school_district", + "School district: a school district that is not part of a local government", + ), + ], + help_text="Type of organization", + max_length=255, + null=True, + ), + ), + ] diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index 1c296b897..35d78a337 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -582,7 +582,7 @@ class DomainApplication(TimeStampedModel): """This is to process to_dict for Domain Information, making it friendly to "copy" it - More information can be found at this- (This dev used #5) + More information can be found at this- (This used #5) https://stackoverflow.com/questions/21925671/convert-django-model-object-to-dict-with-all-of-the-fields-intact/29088221#29088221""" opts = instance._meta data = {} diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index 270592753..b461c7555 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -30,13 +30,6 @@ class DomainInformation(TimeStampedModel): on_delete=models.PROTECT, related_name="information_created", ) - investigator = models.ForeignKey( - "registrar.User", - null=True, - blank=True, - on_delete=models.SET_NULL, - related_name="information_investigating", - ) domain_application = models.OneToOneField( "registrar.DomainApplication", @@ -163,11 +156,6 @@ class DomainInformation(TimeStampedModel): related_name="domain_info", help_text="Domain to which this information belongs", ) - alternative_domains = models.ManyToManyField( - "registrar.Website", - blank=True, - related_name="alternatives+", - ) # This is the contact information provided by the applicant. The # application user who created it is in the `creator` field. @@ -238,10 +226,11 @@ class DomainInformation(TimeStampedModel): # the following information below is not needed in the domain information: da_dict.pop("status") da_dict.pop("current_websites") + da_dict.pop("investigator") + da_dict.pop("alternative_domains") # use the requested_domain to create information for this domain da_dict["domain"] = da_dict.pop("requested_domain") other_contacts = da_dict.pop("other_contacts") - alternative_domains = da_dict.pop("alternative_domains") # just in case domain_info = cls(**da_dict) domain_info.domain_application = domain_application # Save so the object now have PK @@ -250,7 +239,6 @@ class DomainInformation(TimeStampedModel): # Process the remaining "many to many" stuff domain_info.other_contacts.add(*other_contacts) - domain_info.alternative_domains.add(*alternative_domains) domain_info.save() return domain_info From 7d38dce08c63fe4c5139b4360f33a2a650d9f23e Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Tue, 9 May 2023 17:10:00 -0400 Subject: [PATCH 30/35] updated more PR feedback --- src/registrar/models/domain_application.py | 10 +++++----- src/registrar/models/domain_information.py | 10 +++++++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index 35d78a337..6bc15de5d 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -578,24 +578,24 @@ class DomainApplication(TimeStampedModel): return True return False - def to_dict(instance): + def to_dict(self): """This is to process to_dict for Domain Information, making it friendly to "copy" it More information can be found at this- (This used #5) https://stackoverflow.com/questions/21925671/convert-django-model-object-to-dict-with-all-of-the-fields-intact/29088221#29088221""" - opts = instance._meta + opts = self._meta data = {} for field in chain(opts.concrete_fields, opts.private_fields): if field.get_internal_type() in ("ForeignKey", "OneToOneField"): # get the related instance of the FK value - fk_id = field.value_from_object(instance) + fk_id = field.value_from_object(self) if fk_id: data[field.name] = field.related_model.objects.get(id=fk_id) else: data[field.name] = None else: - data[field.name] = field.value_from_object(instance) + data[field.name] = field.value_from_object(self) for field in opts.many_to_many: - data[field.name] = field.value_from_object(instance) + data[field.name] = field.value_from_object(self) return data diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index b461c7555..6561a82b4 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -13,7 +13,11 @@ logger = logging.getLogger(__name__) class DomainInformation(TimeStampedModel): """A registrant's domain information for that domain, exported from - DomainApplication.""" + DomainApplication. We use these field from DomainApplication with few exceptation + which are 'removed' via pop at the bottom of this file. Most of design for domain + management's user information are based on application, but we cannot change + the application once approved, so copying them that way we can make changes + after its approved. Most fields here are copied from Application.""" StateTerritoryChoices = DomainApplication.StateTerritoryChoices @@ -152,7 +156,7 @@ class DomainInformation(TimeStampedModel): on_delete=models.PROTECT, blank=True, null=True, - # Access this information via Domain as "domain.info" + # Access this information via Domain as "domain.domain_info" related_name="domain_info", help_text="Domain to which this information belongs", ) @@ -208,7 +212,7 @@ class DomainInformation(TimeStampedModel): if self.domain and self.domain.name: return self.domain.name else: - return f"application created by {self.creator}" + return f"domain info set up and created by {self.creator}" except Exception: return "" From 4f2d660fe5f6ec29468080cd7bbae2edebdae7b7 Mon Sep 17 00:00:00 2001 From: igorkorenfeld Date: Tue, 9 May 2023 17:25:32 -0400 Subject: [PATCH 31/35] Resize CISA logo in identifier --- .../assets/sass/_theme/_uswds-theme-custom-styles.scss | 4 ++++ src/registrar/templates/includes/footer.html | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/registrar/assets/sass/_theme/_uswds-theme-custom-styles.scss b/src/registrar/assets/sass/_theme/_uswds-theme-custom-styles.scss index d43e82c9b..dbb4f4ef7 100644 --- a/src/registrar/assets/sass/_theme/_uswds-theme-custom-styles.scss +++ b/src/registrar/assets/sass/_theme/_uswds-theme-custom-styles.scss @@ -417,6 +417,10 @@ footer { color: color('primary'); } +.usa-identifier__logo { + height: units(7); +} + abbr[title] { // workaround for underlining abbr element border-bottom: none; diff --git a/src/registrar/templates/includes/footer.html b/src/registrar/templates/includes/footer.html index 929a49cce..ea7bd8420 100644 --- a/src/registrar/templates/includes/footer.html +++ b/src/registrar/templates/includes/footer.html @@ -52,7 +52,7 @@ src="{% static 'img/CISA_logo.png' %}" alt="CISA logo" role="img" - width="48px" + width="56px" />

Date: Wed, 10 May 2023 11:25:53 -0400 Subject: [PATCH 32/35] added model test and exception to one line from lint --- src/registrar/models/domain_application.py | 9 ++++++-- src/registrar/tests/test_models.py | 26 ++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index 6bc15de5d..44c93892c 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -520,6 +520,10 @@ class DomainApplication(TimeStampedModel): Domain = apps.get_model("registrar.Domain") created_domain, _ = Domain.objects.get_or_create(name=self.requested_domain) + # copy the information from domainapplication into domaininformation + DomainInformation = apps.get_model("registrar.DomainInformation") + DomainInformation.create_from_da(self) + # create the permission for the user UserDomainRole = apps.get_model("registrar.UserDomainRole") UserDomainRole.objects.get_or_create( @@ -581,9 +585,10 @@ class DomainApplication(TimeStampedModel): def to_dict(self): """This is to process to_dict for Domain Information, making it friendly to "copy" it - + More information can be found at this- (This used #5) - https://stackoverflow.com/questions/21925671/convert-django-model-object-to-dict-with-all-of-the-fields-intact/29088221#29088221""" + https://stackoverflow.com/questions/21925671/convert-django-model-object-to-dict-with-all-of-the-fields-intact/29088221#29088221 + """ # noqa 590 opts = self._meta data = {} for field in chain(opts.concrete_fields, opts.private_fields): diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py index 784920ec5..c8ae17058 100644 --- a/src/registrar/tests/test_models.py +++ b/src/registrar/tests/test_models.py @@ -4,6 +4,7 @@ from django.db.utils import IntegrityError from registrar.models import ( Contact, DomainApplication, + DomainInformation, User, Website, Domain, @@ -63,6 +64,31 @@ class TestDomainApplication(TestCase): application.other_contacts.add(contact) application.save() + def test_domain_info(self): + """Can create domain info with all fields.""" + user, _ = User.objects.get_or_create() + contact = Contact.objects.create() + domain, _ = Domain.objects.get_or_create(name="igorville.gov") + information = DomainInformation.objects.create( + creator=user, + organization_type=DomainInformation.OrganizationChoices.FEDERAL, + federal_type=DomainInformation.BranchChoices.EXECUTIVE, + is_election_board=False, + organization_name="Test", + address_line1="100 Main St.", + address_line2="APT 1A", + state_territory="CA", + zipcode="12345-6789", + authorizing_official=contact, + submitter=contact, + purpose="Igorville rules!", + anything_else="All of Igorville loves the dotgov program.", + is_policy_acknowledged=True, + domain=domain, + ) + information.other_contacts.add(contact) + information.save() + def test_status_fsm_submit_fail(self): user, _ = User.objects.get_or_create() application = DomainApplication.objects.create(creator=user) From e8d73d0dc70ca13ce848df8f785037ee909216ca Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Wed, 10 May 2023 11:34:53 -0400 Subject: [PATCH 33/35] added a test that verify approve will create domaininfo --- src/registrar/tests/test_models.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py index c8ae17058..26a591131 100644 --- a/src/registrar/tests/test_models.py +++ b/src/registrar/tests/test_models.py @@ -191,6 +191,22 @@ class TestPermissions(TestCase): # should be a role for this user self.assertTrue(UserDomainRole.objects.get(user=user, domain=domain)) +class TestDomainInfo(TestCase): + + """Test creation of Domain Information when approved.""" + + def test_approval_creates_info(self): + domain, _ = Domain.objects.get_or_create(name="igorville.gov") + user, _ = User.objects.get_or_create() + application = DomainApplication.objects.create( + creator=user, requested_domain=domain + ) + # skip using the submit method + application.status = DomainApplication.SUBMITTED + application.approve() + + # should be an information present for this domain + self.assertTrue(DomainInformation.objects.get(domain=domain)) class TestInvitations(TestCase): From 6e1c2f10fdbdb6992981abfdccfc871b83a3b3d6 Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Wed, 10 May 2023 11:46:12 -0400 Subject: [PATCH 34/35] lint --- src/registrar/tests/test_models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py index 26a591131..d0b0a67fc 100644 --- a/src/registrar/tests/test_models.py +++ b/src/registrar/tests/test_models.py @@ -191,6 +191,7 @@ class TestPermissions(TestCase): # should be a role for this user self.assertTrue(UserDomainRole.objects.get(user=user, domain=domain)) + class TestDomainInfo(TestCase): """Test creation of Domain Information when approved.""" @@ -208,6 +209,7 @@ class TestDomainInfo(TestCase): # should be an information present for this domain self.assertTrue(DomainInformation.objects.get(domain=domain)) + class TestInvitations(TestCase): """Test the retrieval of invitations.""" From 1df808c8d4525e24a61cd7156ef8bf71f383d066 Mon Sep 17 00:00:00 2001 From: Jon Roberts Date: Wed, 10 May 2023 14:37:49 -0400 Subject: [PATCH 35/35] added few test verification lines --- src/registrar/tests/test_models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py index d0b0a67fc..2ebca68d7 100644 --- a/src/registrar/tests/test_models.py +++ b/src/registrar/tests/test_models.py @@ -88,6 +88,8 @@ class TestDomainApplication(TestCase): ) information.other_contacts.add(contact) information.save() + self.assertEqual(information.domain.id, domain.id) + self.assertEqual(information.id, domain.domain_info.id) def test_status_fsm_submit_fail(self): user, _ = User.objects.get_or_create()