mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-15 14:04:10 +02:00
Wrap up PR
This commit is contained in:
parent
028d033ff2
commit
db75d31a0b
6 changed files with 252 additions and 37 deletions
|
@ -918,3 +918,38 @@ Example (only requests): `./manage.py create_federal_portfolio --branch "executi
|
||||||
- Parameters #1-#2: Either `--agency_name` or `--branch` must be specified. Not both.
|
- Parameters #1-#2: Either `--agency_name` or `--branch` must be specified. Not both.
|
||||||
- Parameters #2-#3, you cannot use `--both` while using these. You must specify either `--parse_requests` or `--parse_domains` seperately. While all of these parameters are optional in that you do not need to specify all of them,
|
- Parameters #2-#3, you cannot use `--both` while using these. You must specify either `--parse_requests` or `--parse_domains` seperately. While all of these parameters are optional in that you do not need to specify all of them,
|
||||||
you must specify at least one to run this script.
|
you must specify at least one to run this script.
|
||||||
|
|
||||||
|
|
||||||
|
## Patch suborganizations
|
||||||
|
This script deletes some duplicate suborganization data that exists in our database (one-time use).
|
||||||
|
It works in two ways:
|
||||||
|
1. If the only name difference between two suborg records is extra spaces or a capitalization difference,
|
||||||
|
then we delete all duplicate records of this type.
|
||||||
|
2. If the suborg name is one we manually specify to delete via the script.
|
||||||
|
|
||||||
|
Before it deletes records, it goes through each DomainInformation and DomainRequest object and updates the reference to "sub_organization" to match the non-duplicative record.
|
||||||
|
|
||||||
|
### Running on sandboxes
|
||||||
|
|
||||||
|
#### Step 1: Login to CloudFoundry
|
||||||
|
```cf login -a api.fr.cloud.gov --sso```
|
||||||
|
|
||||||
|
#### Step 2: SSH into your environment
|
||||||
|
```cf ssh getgov-{space}```
|
||||||
|
|
||||||
|
Example: `cf ssh getgov-za`
|
||||||
|
|
||||||
|
#### Step 3: Create a shell instance
|
||||||
|
```/tmp/lifecycle/shell```
|
||||||
|
|
||||||
|
#### Step 4: Upload your csv to the desired sandbox
|
||||||
|
[Follow these steps](#use-scp-to-transfer-data-to-sandboxes) to upload the federal_cio csv to a sandbox of your choice.
|
||||||
|
|
||||||
|
#### Step 5: Running the script
|
||||||
|
To create a specific portfolio:
|
||||||
|
```./manage.py patch_suborganizations```
|
||||||
|
|
||||||
|
### Running locally
|
||||||
|
|
||||||
|
#### Step 1: Running the script
|
||||||
|
```docker-compose exec app ./manage.py patch_suborganizations```
|
||||||
|
|
|
@ -80,18 +80,33 @@ class Command(BaseCommand):
|
||||||
else:
|
else:
|
||||||
raise CommandError(f"Cannot find '{branch}' federal agencies in our database.")
|
raise CommandError(f"Cannot find '{branch}' federal agencies in our database.")
|
||||||
|
|
||||||
|
all_suborganizations = []
|
||||||
|
all_domains = []
|
||||||
|
all_domain_requests = []
|
||||||
for federal_agency in agencies:
|
for federal_agency in agencies:
|
||||||
message = f"Processing federal agency '{federal_agency.agency}'..."
|
message = f"Processing federal agency '{federal_agency.agency}'..."
|
||||||
TerminalHelper.colorful_logger(logger.info, TerminalColors.MAGENTA, message)
|
TerminalHelper.colorful_logger(logger.info, TerminalColors.MAGENTA, message)
|
||||||
try:
|
try:
|
||||||
# C901 'Command.handle' is too complex (12)
|
# C901 'Command.handle' is too complex (12)
|
||||||
self.handle_populate_portfolio(federal_agency, parse_domains, parse_requests, both)
|
suborgs, domains, requests = self.handle_populate_portfolio(
|
||||||
|
federal_agency, parse_domains, parse_requests, both
|
||||||
|
)
|
||||||
|
all_suborganizations.extend(suborgs)
|
||||||
|
all_domains.extend(domains)
|
||||||
|
all_domain_requests.extend(requests)
|
||||||
except Exception as exec:
|
except Exception as exec:
|
||||||
self.failed_portfolios.add(federal_agency)
|
self.failed_portfolios.add(federal_agency)
|
||||||
logger.error(exec)
|
logger.error(exec)
|
||||||
message = f"Failed to create portfolio '{federal_agency.agency}'"
|
message = f"Failed to create portfolio '{federal_agency.agency}'"
|
||||||
TerminalHelper.colorful_logger(logger.info, TerminalColors.FAIL, message)
|
TerminalHelper.colorful_logger(logger.info, TerminalColors.FAIL, message)
|
||||||
|
|
||||||
|
# Post process steps
|
||||||
|
# Add suborg info to created or existing suborgs.
|
||||||
|
if all_suborganizations:
|
||||||
|
self.post_process_suborganization_fields(all_suborganizations, all_domains, all_domain_requests)
|
||||||
|
message = f"Added city and state_territory information to {len(all_suborganizations)} suborgs."
|
||||||
|
TerminalHelper.colorful_logger(logger.info, TerminalColors.MAGENTA, message)
|
||||||
|
|
||||||
TerminalHelper.log_script_run_summary(
|
TerminalHelper.log_script_run_summary(
|
||||||
self.updated_portfolios,
|
self.updated_portfolios,
|
||||||
self.failed_portfolios,
|
self.failed_portfolios,
|
||||||
|
@ -105,47 +120,46 @@ class Command(BaseCommand):
|
||||||
"""Attempts to create a portfolio. If successful, this function will
|
"""Attempts to create a portfolio. If successful, this function will
|
||||||
also create new suborganizations"""
|
also create new suborganizations"""
|
||||||
portfolio, created = self.create_portfolio(federal_agency)
|
portfolio, created = self.create_portfolio(federal_agency)
|
||||||
suborganizations = Suborganization.objects.none()
|
|
||||||
domains = DomainInformation.objects.filter(federal_agency=federal_agency, portfolio__isnull=True)
|
|
||||||
domain_requests = DomainRequest.objects.filter(federal_agency=federal_agency, portfolio__isnull=True).exclude(
|
|
||||||
status__in=[
|
|
||||||
DomainRequest.DomainRequestStatus.STARTED,
|
|
||||||
DomainRequest.DomainRequestStatus.INELIGIBLE,
|
|
||||||
DomainRequest.DomainRequestStatus.REJECTED,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
if created:
|
|
||||||
suborganizations = self.create_suborganizations(portfolio, federal_agency)
|
suborganizations = self.create_suborganizations(portfolio, federal_agency)
|
||||||
|
domains = []
|
||||||
|
domain_requests = []
|
||||||
|
if created:
|
||||||
if parse_domains or both:
|
if parse_domains or both:
|
||||||
domains = self.handle_portfolio_domains(portfolio, federal_agency, domains)
|
domains = self.handle_portfolio_domains(portfolio, federal_agency)
|
||||||
|
|
||||||
if parse_requests or both:
|
if parse_requests or both:
|
||||||
domain_requests = self.handle_portfolio_requests(portfolio, federal_agency, domain_requests)
|
domain_requests = self.handle_portfolio_requests(portfolio, federal_agency)
|
||||||
|
|
||||||
# Post process steps
|
return suborganizations, domains, domain_requests
|
||||||
# Add suborg info to created or existing suborgs. Get the refreshed queryset for each.
|
|
||||||
self.post_process_suborganization_fields(suborganizations.all(), domains.all(), domain_requests.all())
|
|
||||||
|
|
||||||
def post_process_suborganization_fields(self, suborganizations, domains, requests):
|
def post_process_suborganization_fields(self, suborganizations, domains, requests):
|
||||||
|
"""Post-process suborganization fields by pulling data from related domains and requests.
|
||||||
|
|
||||||
|
This function updates suborganization city and state_territory fields based on
|
||||||
|
related domain information and domain request information.
|
||||||
|
"""
|
||||||
|
|
||||||
# Exclude domains and requests where the org name is the same,
|
# Exclude domains and requests where the org name is the same,
|
||||||
# and where we are missing some crucial information.
|
# and where we are missing some crucial information.
|
||||||
domains = domains.exclude(
|
domains = DomainInformation.objects.filter(id__in=[domain.id for domain in domains]).exclude(
|
||||||
portfolio__isnull=True,
|
portfolio__isnull=True,
|
||||||
organization_name__isnull=True,
|
organization_name__isnull=True,
|
||||||
sub_organization__isnull=True,
|
sub_organization__isnull=True,
|
||||||
organization_name__iexact=F("portfolio__organization_name"),
|
organization_name__iexact=F("portfolio__organization_name"),
|
||||||
).in_bulk(field_name="organization_name")
|
)
|
||||||
|
domains_dict = {domain.organization_name: domain for domain in domains}
|
||||||
|
|
||||||
requests = requests.exclude(
|
requests = DomainRequest.objects.filter(id__in=[request.id for request in requests]).exclude(
|
||||||
portfolio__isnull=True,
|
portfolio__isnull=True,
|
||||||
organization_name__isnull=True,
|
organization_name__isnull=True,
|
||||||
sub_organization__isnull=True,
|
sub_organization__isnull=True,
|
||||||
organization_name__iexact=F("portfolio__organization_name"),
|
organization_name__iexact=F("portfolio__organization_name"),
|
||||||
).in_bulk(field_name="organization_name")
|
)
|
||||||
|
requests_dict = {request.organization_name: request for request in requests}
|
||||||
|
|
||||||
for suborg in suborganizations:
|
for suborg in suborganizations:
|
||||||
domain = domains.get(suborg.name, None)
|
domain = domains_dict.get(suborg.name, None)
|
||||||
request = requests.get(suborg.name, None)
|
request = requests_dict.get(suborg.name, None)
|
||||||
|
|
||||||
# PRIORITY:
|
# PRIORITY:
|
||||||
# 1. Domain info
|
# 1. Domain info
|
||||||
|
@ -266,11 +280,19 @@ class Command(BaseCommand):
|
||||||
TerminalHelper.colorful_logger(logger.warning, TerminalColors.YELLOW, "No suborganizations added")
|
TerminalHelper.colorful_logger(logger.warning, TerminalColors.YELLOW, "No suborganizations added")
|
||||||
return new_suborgs
|
return new_suborgs
|
||||||
|
|
||||||
def handle_portfolio_requests(self, portfolio: Portfolio, federal_agency: FederalAgency, domain_requests):
|
def handle_portfolio_requests(self, portfolio: Portfolio, federal_agency: FederalAgency):
|
||||||
"""
|
"""
|
||||||
Associate portfolio with domain requests for a federal agency.
|
Associate portfolio with domain requests for a federal agency.
|
||||||
Updates all relevant domain request records.
|
Updates all relevant domain request records.
|
||||||
"""
|
"""
|
||||||
|
domain_requests = DomainRequest.objects.filter(federal_agency=federal_agency, portfolio__isnull=True).exclude(
|
||||||
|
status__in=[
|
||||||
|
DomainRequest.DomainRequestStatus.STARTED,
|
||||||
|
DomainRequest.DomainRequestStatus.INELIGIBLE,
|
||||||
|
DomainRequest.DomainRequestStatus.REJECTED,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
if not domain_requests.exists():
|
if not domain_requests.exists():
|
||||||
message = f"""
|
message = f"""
|
||||||
Portfolio '{portfolio}' not added to domain requests: no valid records found.
|
Portfolio '{portfolio}' not added to domain requests: no valid records found.
|
||||||
|
@ -295,14 +317,14 @@ class Command(BaseCommand):
|
||||||
message = f"Added portfolio '{portfolio}' to {len(domain_requests)} domain requests."
|
message = f"Added portfolio '{portfolio}' to {len(domain_requests)} domain requests."
|
||||||
TerminalHelper.colorful_logger(logger.info, TerminalColors.OKGREEN, message)
|
TerminalHelper.colorful_logger(logger.info, TerminalColors.OKGREEN, message)
|
||||||
|
|
||||||
# Return a fresh copy of the queryset
|
return domain_requests
|
||||||
return domain_requests.all()
|
|
||||||
|
|
||||||
def handle_portfolio_domains(self, portfolio: Portfolio, federal_agency: FederalAgency, domain_infos):
|
def handle_portfolio_domains(self, portfolio: Portfolio, federal_agency: FederalAgency):
|
||||||
"""
|
"""
|
||||||
Associate portfolio with domains for a federal agency.
|
Associate portfolio with domains for a federal agency.
|
||||||
Updates all relevant domain information records.
|
Updates all relevant domain information records.
|
||||||
"""
|
"""
|
||||||
|
domain_infos = DomainInformation.objects.filter(federal_agency=federal_agency, portfolio__isnull=True)
|
||||||
if not domain_infos.exists():
|
if not domain_infos.exists():
|
||||||
message = f"""
|
message = f"""
|
||||||
Portfolio '{portfolio}' not added to domains: no valid records found.
|
Portfolio '{portfolio}' not added to domains: no valid records found.
|
||||||
|
@ -323,5 +345,4 @@ class Command(BaseCommand):
|
||||||
message = f"Added portfolio '{portfolio}' to {len(domain_infos)} domains."
|
message = f"Added portfolio '{portfolio}' to {len(domain_infos)} domains."
|
||||||
TerminalHelper.colorful_logger(logger.info, TerminalColors.OKGREEN, message)
|
TerminalHelper.colorful_logger(logger.info, TerminalColors.OKGREEN, message)
|
||||||
|
|
||||||
# Return a fresh copy of the queryset
|
return domain_infos
|
||||||
return domain_infos.all()
|
|
||||||
|
|
|
@ -19,12 +19,12 @@ class Command(BaseCommand):
|
||||||
# For extra_records_to_prune: the key gets deleted, the value gets kept.
|
# For extra_records_to_prune: the key gets deleted, the value gets kept.
|
||||||
extra_records_to_prune = {
|
extra_records_to_prune = {
|
||||||
normalize_string("Assistant Secretary for Preparedness and Response Office of the Secretary"): {
|
normalize_string("Assistant Secretary for Preparedness and Response Office of the Secretary"): {
|
||||||
"keep": "Assistant Secretary for Preparedness and Response, Office of the Secretary"
|
"replace_with": "Assistant Secretary for Preparedness and Response, Office of the Secretary"
|
||||||
},
|
},
|
||||||
normalize_string("US Geological Survey"): {"keep": "U.S. Geological Survey"},
|
normalize_string("US Geological Survey"): {"replace_with": "U.S. Geological Survey"},
|
||||||
normalize_string("USDA/OC"): {"keep": "USDA, Office of Communications"},
|
normalize_string("USDA/OC"): {"replace_with": "USDA, Office of Communications"},
|
||||||
normalize_string("GSA, IC, OGP WebPortfolio"): {"keep": "GSA, IC, OGP Web Portfolio"},
|
normalize_string("GSA, IC, OGP WebPortfolio"): {"replace_with": "GSA, IC, OGP Web Portfolio"},
|
||||||
normalize_string("USDA/ARS/NAL"): {"keep": "USDA, ARS, NAL"},
|
normalize_string("USDA/ARS/NAL"): {"replace_with": "USDA, ARS, NAL"},
|
||||||
}
|
}
|
||||||
|
|
||||||
# First: Group all suborganization names by their "normalized" names (finding duplicates).
|
# First: Group all suborganization names by their "normalized" names (finding duplicates).
|
||||||
|
@ -61,8 +61,8 @@ class Command(BaseCommand):
|
||||||
if normalized_name in extra_records_to_prune:
|
if normalized_name in extra_records_to_prune:
|
||||||
# The 'keep' field expects a Suborganization but we just pass in a string, so this is just a workaround.
|
# The 'keep' field expects a Suborganization but we just pass in a string, so this is just a workaround.
|
||||||
# This assumes that there is only one item in the name_group array (see usda/oc example). Should be fine, given our data.
|
# This assumes that there is only one item in the name_group array (see usda/oc example). Should be fine, given our data.
|
||||||
hardcoded_record_name = extra_records_to_prune[normalized_name]["keep"]
|
hardcoded_record_name = extra_records_to_prune[normalized_name]["replace_with"]
|
||||||
name_group = name_groups.get(normalized_name(hardcoded_record_name), [])
|
name_group = name_groups.get(normalize_string(hardcoded_record_name))
|
||||||
keep = name_group[0] if name_group else None
|
keep = name_group[0] if name_group else None
|
||||||
records_to_prune[normalized_name] = {"keep": keep, "delete": duplicate_suborgs}
|
records_to_prune[normalized_name] = {"keep": keep, "delete": duplicate_suborgs}
|
||||||
# Delete duplicates (extra spaces or casing differences)
|
# Delete duplicates (extra spaces or casing differences)
|
||||||
|
|
|
@ -39,6 +39,7 @@ from epplibwrapper import (
|
||||||
ErrorCode,
|
ErrorCode,
|
||||||
responses,
|
responses,
|
||||||
)
|
)
|
||||||
|
from registrar.models.suborganization import Suborganization
|
||||||
from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices
|
from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices
|
||||||
from registrar.models.user_domain_role import UserDomainRole
|
from registrar.models.user_domain_role import UserDomainRole
|
||||||
|
|
||||||
|
@ -905,6 +906,7 @@ class MockDb(TestCase):
|
||||||
DomainInformation.objects.all().delete()
|
DomainInformation.objects.all().delete()
|
||||||
DomainRequest.objects.all().delete()
|
DomainRequest.objects.all().delete()
|
||||||
UserDomainRole.objects.all().delete()
|
UserDomainRole.objects.all().delete()
|
||||||
|
Suborganization.objects.all().delete()
|
||||||
Portfolio.objects.all().delete()
|
Portfolio.objects.all().delete()
|
||||||
UserPortfolioPermission.objects.all().delete()
|
UserPortfolioPermission.objects.all().delete()
|
||||||
User.objects.all().delete()
|
User.objects.all().delete()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import copy
|
import copy
|
||||||
|
from io import StringIO
|
||||||
import boto3_mocking # type: ignore
|
import boto3_mocking # type: ignore
|
||||||
from datetime import date, datetime, time
|
from datetime import date, datetime, time
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
|
@ -32,7 +33,7 @@ import tablib
|
||||||
from unittest.mock import patch, call, MagicMock, mock_open
|
from unittest.mock import patch, call, MagicMock, mock_open
|
||||||
from epplibwrapper import commands, common
|
from epplibwrapper import commands, common
|
||||||
|
|
||||||
from .common import MockEppLib, less_console_noise, completed_domain_request, MockSESClient
|
from .common import MockEppLib, less_console_noise, completed_domain_request, MockSESClient, MockDbForIndividualTests
|
||||||
from api.tests.common import less_console_noise_decorator
|
from api.tests.common import less_console_noise_decorator
|
||||||
|
|
||||||
|
|
||||||
|
@ -1731,3 +1732,159 @@ class TestCreateFederalPortfolio(TestCase):
|
||||||
self.assertEqual(existing_portfolio.organization_name, self.federal_agency.agency)
|
self.assertEqual(existing_portfolio.organization_name, self.federal_agency.agency)
|
||||||
self.assertEqual(existing_portfolio.notes, "Old notes")
|
self.assertEqual(existing_portfolio.notes, "Old notes")
|
||||||
self.assertEqual(existing_portfolio.creator, self.user)
|
self.assertEqual(existing_portfolio.creator, self.user)
|
||||||
|
|
||||||
|
def test_post_process_suborganization_fields(self):
|
||||||
|
"""Test suborganization field updates from domain and request data.
|
||||||
|
Also tests the priority order for updating city and state_territory:
|
||||||
|
1. Domain information fields
|
||||||
|
2. Domain request suborganization fields
|
||||||
|
3. Domain request standard fields
|
||||||
|
"""
|
||||||
|
# Create test data with different field combinations
|
||||||
|
self.domain_info.organization_name = "super"
|
||||||
|
self.domain_info.city = "Domain City "
|
||||||
|
self.domain_info.state_territory = "NY"
|
||||||
|
self.domain_info.save()
|
||||||
|
|
||||||
|
self.domain_request_2.organization_name = "super"
|
||||||
|
self.domain_request.suborganization_city = "Request Suborg City"
|
||||||
|
self.domain_request.suborganization_state_territory = "CA"
|
||||||
|
self.domain_request.city = "Request City"
|
||||||
|
self.domain_request.state_territory = "TX"
|
||||||
|
self.domain_request.save()
|
||||||
|
|
||||||
|
# Create another request/info pair without domain info data
|
||||||
|
self.domain_info_2.organization_name = "creative"
|
||||||
|
self.domain_info_2.city = None
|
||||||
|
self.domain_info_2.state_territory = None
|
||||||
|
self.domain_info_2.save()
|
||||||
|
|
||||||
|
self.domain_request_2.organization_name = "creative"
|
||||||
|
self.domain_request_2.suborganization_city = "Second Suborg City"
|
||||||
|
self.domain_request_2.suborganization_state_territory = "WA"
|
||||||
|
self.domain_request_2.city = "Second City"
|
||||||
|
self.domain_request_2.state_territory = "OR"
|
||||||
|
self.domain_request_2.save()
|
||||||
|
|
||||||
|
# Create a third request/info pair without suborg data
|
||||||
|
self.domain_info_3.organization_name = "names"
|
||||||
|
self.domain_info_3.city = None
|
||||||
|
self.domain_info_3.state_territory = None
|
||||||
|
self.domain_info_3.save()
|
||||||
|
|
||||||
|
self.domain_request_3.organization_name = "names"
|
||||||
|
self.domain_request_3.suborganization_city = None
|
||||||
|
self.domain_request_3.suborganization_state_territory = None
|
||||||
|
self.domain_request_3.city = "Third City"
|
||||||
|
self.domain_request_3.state_territory = "FL"
|
||||||
|
self.domain_request_3.save()
|
||||||
|
|
||||||
|
# Test running the script with both, and just with parse_requests
|
||||||
|
self.run_create_federal_portfolio(agency_name="Test Federal Agency", parse_requests=True, parse_domains=True)
|
||||||
|
self.run_create_federal_portfolio(
|
||||||
|
agency_name="Executive Agency 1",
|
||||||
|
parse_requests=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.domain_info.refresh_from_db()
|
||||||
|
self.domain_request.refresh_from_db()
|
||||||
|
self.domain_info_2.refresh_from_db()
|
||||||
|
self.domain_request_2.refresh_from_db()
|
||||||
|
self.domain_info_3.refresh_from_db()
|
||||||
|
self.domain_request_3.refresh_from_db()
|
||||||
|
|
||||||
|
# Verify suborganizations were created with correct field values
|
||||||
|
# Should use domain info values
|
||||||
|
suborg_1 = Suborganization.objects.get(name=self.domain_info.organization_name)
|
||||||
|
self.assertEqual(suborg_1.city, "Domain City")
|
||||||
|
self.assertEqual(suborg_1.state_territory, "NY")
|
||||||
|
|
||||||
|
# Should use domain request suborg values
|
||||||
|
suborg_2 = Suborganization.objects.get(name=self.domain_info_2.organization_name)
|
||||||
|
self.assertEqual(suborg_2.city, "Second Suborg City")
|
||||||
|
self.assertEqual(suborg_2.state_territory, "WA")
|
||||||
|
|
||||||
|
# Should use domain request standard values
|
||||||
|
suborg_3 = Suborganization.objects.get(name=self.domain_info_3.organization_name)
|
||||||
|
self.assertEqual(suborg_3.city, "Third City")
|
||||||
|
self.assertEqual(suborg_3.state_territory, "FL")
|
||||||
|
|
||||||
|
|
||||||
|
class TestPatchSuborganizations(MockDbForIndividualTests):
|
||||||
|
"""Tests for the patch_suborganizations management command."""
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def run_patch_suborganizations(self):
|
||||||
|
"""Helper method to run the patch_suborganizations command."""
|
||||||
|
with patch(
|
||||||
|
"registrar.management.commands.utility.terminal_helper.TerminalHelper.prompt_for_execution",
|
||||||
|
return_value=True,
|
||||||
|
):
|
||||||
|
call_command("patch_suborganizations")
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_space_and_case_duplicates(self):
|
||||||
|
"""Test cleaning up duplicates that differ by spaces and case.
|
||||||
|
|
||||||
|
Should keep the version with:
|
||||||
|
1. Fewest spaces
|
||||||
|
2. Most leading capitals
|
||||||
|
"""
|
||||||
|
Suborganization.objects.create(name="Test Organization ", portfolio=self.portfolio_1)
|
||||||
|
Suborganization.objects.create(name="test organization", portfolio=self.portfolio_1)
|
||||||
|
Suborganization.objects.create(name="Test Organization", portfolio=self.portfolio_1)
|
||||||
|
|
||||||
|
# Create an unrelated record to test that it doesn't get deleted, too
|
||||||
|
Suborganization.objects.create(name="unrelated org", portfolio=self.portfolio_1)
|
||||||
|
self.run_patch_suborganizations()
|
||||||
|
self.assertEqual(Suborganization.objects.count(), 2)
|
||||||
|
self.assertEqual(Suborganization.objects.filter(name__in=["unrelated org", "Test Organization"]).count(), 2)
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_hardcoded_record(self):
|
||||||
|
"""Tests that our hardcoded records update as we expect them to"""
|
||||||
|
# Create orgs with old and new name formats
|
||||||
|
old_name = "USDA/OC"
|
||||||
|
new_name = "USDA, Office of Communications"
|
||||||
|
|
||||||
|
Suborganization.objects.create(name=old_name, portfolio=self.portfolio_1)
|
||||||
|
Suborganization.objects.create(name=new_name, portfolio=self.portfolio_1)
|
||||||
|
|
||||||
|
self.run_patch_suborganizations()
|
||||||
|
|
||||||
|
# Verify only the new one remains
|
||||||
|
self.assertEqual(Suborganization.objects.count(), 1)
|
||||||
|
remaining = Suborganization.objects.first()
|
||||||
|
self.assertEqual(remaining.name, new_name)
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_reference_updates(self):
|
||||||
|
"""Test that references are updated on domain info and domain request before deletion."""
|
||||||
|
# Create suborganizations
|
||||||
|
keep_org = Suborganization.objects.create(name="Test Organization", portfolio=self.portfolio_1)
|
||||||
|
delete_org = Suborganization.objects.create(name="test organization ", portfolio=self.portfolio_1)
|
||||||
|
unrelated_org = Suborganization.objects.create(name="awesome", portfolio=self.portfolio_1)
|
||||||
|
|
||||||
|
# We expect these references to update
|
||||||
|
self.domain_request_1.sub_organization = delete_org
|
||||||
|
self.domain_information_1.sub_organization = delete_org
|
||||||
|
self.domain_request_1.save()
|
||||||
|
self.domain_information_1.save()
|
||||||
|
|
||||||
|
# But not these ones
|
||||||
|
self.domain_request_2.sub_organization = unrelated_org
|
||||||
|
self.domain_information_2.sub_organization = unrelated_org
|
||||||
|
self.domain_request_2.save()
|
||||||
|
self.domain_information_2.save()
|
||||||
|
|
||||||
|
self.run_patch_suborganizations()
|
||||||
|
|
||||||
|
self.domain_request_1.refresh_from_db()
|
||||||
|
self.domain_information_1.refresh_from_db()
|
||||||
|
self.domain_request_2.refresh_from_db()
|
||||||
|
self.domain_information_2.refresh_from_db()
|
||||||
|
|
||||||
|
self.assertEqual(self.domain_request_1.sub_organization, keep_org)
|
||||||
|
self.assertEqual(self.domain_information_1.sub_organization, keep_org)
|
||||||
|
self.assertEqual(self.domain_request_2.sub_organization, unrelated_org)
|
||||||
|
self.assertEqual(self.domain_information_2.sub_organization, unrelated_org)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue