Merge pull request #2152 from cisagov/za/2118-add-first-ready-on-reports

Ticket #2118: Add first ready on field
This commit is contained in:
zandercymatics 2024-05-10 08:39:48 -06:00 committed by GitHub
commit bc2bcb7b21
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 75 additions and 54 deletions

View file

@ -97,6 +97,11 @@ def less_console_noise(output_stream=None):
output_stream.close() output_stream.close()
def get_time_aware_date(date=datetime(2023, 11, 1)):
"""Returns a time aware date"""
return timezone.make_aware(date)
class GenericTestHelper(TestCase): class GenericTestHelper(TestCase):
"""A helper class that contains various helper functions for TestCases""" """A helper class that contains various helper functions for TestCases"""
@ -532,11 +537,9 @@ class MockDb(TestCase):
username=username, first_name=first_name, last_name=last_name, email=email username=username, first_name=first_name, last_name=last_name, email=email
) )
# Create a time-aware current date current_date = get_time_aware_date(datetime(2024, 4, 2))
current_datetime = timezone.now()
# Extract the date part
current_date = current_datetime.date()
# Create start and end dates using timedelta # Create start and end dates using timedelta
self.end_date = current_date + timedelta(days=2) self.end_date = current_date + timedelta(days=2)
self.start_date = current_date - timedelta(days=2) self.start_date = current_date - timedelta(days=2)
@ -544,22 +547,22 @@ class MockDb(TestCase):
self.federal_agency_2, _ = FederalAgency.objects.get_or_create(agency="Armed Forces Retirement Home") self.federal_agency_2, _ = FederalAgency.objects.get_or_create(agency="Armed Forces Retirement Home")
self.domain_1, _ = Domain.objects.get_or_create( self.domain_1, _ = Domain.objects.get_or_create(
name="cdomain1.gov", state=Domain.State.READY, first_ready=timezone.now() name="cdomain1.gov", state=Domain.State.READY, first_ready=get_time_aware_date(datetime(2024, 4, 2))
) )
self.domain_2, _ = Domain.objects.get_or_create(name="adomain2.gov", state=Domain.State.DNS_NEEDED) self.domain_2, _ = Domain.objects.get_or_create(name="adomain2.gov", state=Domain.State.DNS_NEEDED)
self.domain_3, _ = Domain.objects.get_or_create(name="ddomain3.gov", state=Domain.State.ON_HOLD) self.domain_3, _ = Domain.objects.get_or_create(name="ddomain3.gov", state=Domain.State.ON_HOLD)
self.domain_4, _ = Domain.objects.get_or_create(name="bdomain4.gov", state=Domain.State.UNKNOWN) self.domain_4, _ = Domain.objects.get_or_create(name="bdomain4.gov", state=Domain.State.UNKNOWN)
self.domain_5, _ = Domain.objects.get_or_create( self.domain_5, _ = Domain.objects.get_or_create(
name="bdomain5.gov", state=Domain.State.DELETED, deleted=timezone.make_aware(datetime(2023, 11, 1)) name="bdomain5.gov", state=Domain.State.DELETED, deleted=get_time_aware_date(datetime(2023, 11, 1))
) )
self.domain_6, _ = Domain.objects.get_or_create( self.domain_6, _ = Domain.objects.get_or_create(
name="bdomain6.gov", state=Domain.State.DELETED, deleted=timezone.make_aware(datetime(1980, 10, 16)) name="bdomain6.gov", state=Domain.State.DELETED, deleted=get_time_aware_date(datetime(1980, 10, 16))
) )
self.domain_7, _ = Domain.objects.get_or_create( self.domain_7, _ = Domain.objects.get_or_create(
name="xdomain7.gov", state=Domain.State.DELETED, deleted=timezone.now() name="xdomain7.gov", state=Domain.State.DELETED, deleted=get_time_aware_date(datetime(2024, 4, 2))
) )
self.domain_8, _ = Domain.objects.get_or_create( self.domain_8, _ = Domain.objects.get_or_create(
name="sdomain8.gov", state=Domain.State.DELETED, deleted=timezone.now() name="sdomain8.gov", state=Domain.State.DELETED, deleted=get_time_aware_date(datetime(2024, 4, 2))
) )
# We use timezone.make_aware to sync to server time a datetime object with the current date (using date.today()) # We use timezone.make_aware to sync to server time a datetime object with the current date (using date.today())
# and a specific time (using datetime.min.time()). # and a specific time (using datetime.min.time()).
@ -567,19 +570,19 @@ class MockDb(TestCase):
self.domain_9, _ = Domain.objects.get_or_create( self.domain_9, _ = Domain.objects.get_or_create(
name="zdomain9.gov", name="zdomain9.gov",
state=Domain.State.DELETED, state=Domain.State.DELETED,
deleted=timezone.make_aware(datetime.combine(date.today() - timedelta(days=1), datetime.min.time())), deleted=get_time_aware_date(datetime(2024, 4, 1)),
) )
# ready tomorrow # ready tomorrow
self.domain_10, _ = Domain.objects.get_or_create( self.domain_10, _ = Domain.objects.get_or_create(
name="adomain10.gov", name="adomain10.gov",
state=Domain.State.READY, state=Domain.State.READY,
first_ready=timezone.make_aware(datetime.combine(date.today() + timedelta(days=1), datetime.min.time())), first_ready=get_time_aware_date(datetime(2024, 4, 3)),
) )
self.domain_11, _ = Domain.objects.get_or_create( self.domain_11, _ = Domain.objects.get_or_create(
name="cdomain11.gov", state=Domain.State.READY, first_ready=timezone.now() name="cdomain11.gov", state=Domain.State.READY, first_ready=get_time_aware_date(datetime(2024, 4, 2))
) )
self.domain_12, _ = Domain.objects.get_or_create( self.domain_12, _ = Domain.objects.get_or_create(
name="zdomain12.gov", state=Domain.State.READY, first_ready=timezone.now() name="zdomain12.gov", state=Domain.State.READY, first_ready=get_time_aware_date(datetime(2024, 4, 2))
) )
self.domain_information_1, _ = DomainInformation.objects.get_or_create( self.domain_information_1, _ = DomainInformation.objects.get_or_create(
@ -716,23 +719,31 @@ class MockDb(TestCase):
with less_console_noise(): with less_console_noise():
self.domain_request_1 = completed_domain_request( self.domain_request_1 = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED, name="city1.gov" status=DomainRequest.DomainRequestStatus.STARTED,
name="city1.gov",
) )
self.domain_request_2 = completed_domain_request( self.domain_request_2 = completed_domain_request(
status=DomainRequest.DomainRequestStatus.IN_REVIEW, name="city2.gov" status=DomainRequest.DomainRequestStatus.IN_REVIEW,
name="city2.gov",
) )
self.domain_request_3 = completed_domain_request( self.domain_request_3 = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED, name="city3.gov" status=DomainRequest.DomainRequestStatus.STARTED,
name="city3.gov",
) )
self.domain_request_4 = completed_domain_request( self.domain_request_4 = completed_domain_request(
status=DomainRequest.DomainRequestStatus.STARTED, name="city4.gov" status=DomainRequest.DomainRequestStatus.STARTED,
name="city4.gov",
) )
self.domain_request_5 = completed_domain_request( self.domain_request_5 = completed_domain_request(
status=DomainRequest.DomainRequestStatus.APPROVED, name="city5.gov" status=DomainRequest.DomainRequestStatus.APPROVED,
name="city5.gov",
) )
self.domain_request_3.submit() self.domain_request_3.submit()
self.domain_request_3.save()
self.domain_request_4.submit() self.domain_request_4.submit()
self.domain_request_3.submission_date = get_time_aware_date(datetime(2024, 4, 2))
self.domain_request_4.submission_date = get_time_aware_date(datetime(2024, 4, 2))
self.domain_request_3.save()
self.domain_request_4.save() self.domain_request_4.save()
def tearDown(self): def tearDown(self):
@ -873,6 +884,7 @@ def completed_domain_request(
if organization_type: if organization_type:
domain_request_kwargs["organization_type"] = organization_type domain_request_kwargs["organization_type"] = organization_type
domain_request, _ = DomainRequest.objects.get_or_create(**domain_request_kwargs) domain_request, _ = DomainRequest.objects.get_or_create(**domain_request_kwargs)
if has_other_contacts: if has_other_contacts:

View file

@ -2164,7 +2164,6 @@ class TestDomainRequestAdmin(MockEppLib):
self.assertContains(response, "Yes, select ineligible status") self.assertContains(response, "Yes, select ineligible status")
def test_readonly_when_restricted_creator(self): def test_readonly_when_restricted_creator(self):
self.maxDiff = None
with less_console_noise(): with less_console_noise():
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW) domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW)
with boto3_mocking.clients.handler_for("sesv2", self.mock_client): with boto3_mocking.clients.handler_for("sesv2", self.mock_client):

View file

@ -22,9 +22,8 @@ from django.conf import settings
from botocore.exceptions import ClientError from botocore.exceptions import ClientError
import boto3_mocking import boto3_mocking
from registrar.utility.s3_bucket import S3ClientError, S3ClientErrorCodes # type: ignore from registrar.utility.s3_bucket import S3ClientError, S3ClientErrorCodes # type: ignore
from datetime import datetime
from django.utils import timezone from django.utils import timezone
from .common import MockDb, MockEppLib, less_console_noise from .common import MockDb, MockEppLib, less_console_noise, get_time_aware_date
class CsvReportsTest(MockDb): class CsvReportsTest(MockDb):
@ -201,9 +200,9 @@ class ExportDataTest(MockDb, MockEppLib):
def tearDown(self): def tearDown(self):
super().tearDown() super().tearDown()
def test_export_domains_to_writer_security_emails(self): def test_export_domains_to_writer_security_emails_and_first_ready(self):
"""Test that export_domains_to_writer returns the """Test that export_domains_to_writer returns the
expected security email""" expected security email and first_ready value"""
with less_console_noise(): with less_console_noise():
# Add security email information # Add security email information
@ -215,6 +214,11 @@ class ExportDataTest(MockDb, MockEppLib):
self.domain_2.security_contact self.domain_2.security_contact
# Invoke setter # Invoke setter
self.domain_3.security_contact self.domain_3.security_contact
# Add a first ready date on the first domain. Leaving the others blank.
self.domain_1.first_ready = get_default_start_date()
self.domain_1.save()
# Create a CSV file in memory # Create a CSV file in memory
csv_file = StringIO() csv_file = StringIO()
writer = csv.writer(csv_file) writer = csv.writer(csv_file)
@ -231,6 +235,7 @@ class ExportDataTest(MockDb, MockEppLib):
"Security contact email", "Security contact email",
"Status", "Status",
"Expiration date", "Expiration date",
"First ready on",
] ]
sort_fields = ["domain__name"] sort_fields = ["domain__name"]
filter_condition = { filter_condition = {
@ -240,7 +245,7 @@ class ExportDataTest(MockDb, MockEppLib):
Domain.State.ON_HOLD, Domain.State.ON_HOLD,
], ],
} }
self.maxDiff = None
# Call the export functions # Call the export functions
write_csv_for_domains( write_csv_for_domains(
writer, writer,
@ -259,13 +264,14 @@ class ExportDataTest(MockDb, MockEppLib):
# sorted alphabetially by domain name # sorted alphabetially by domain name
expected_content = ( expected_content = (
"Domain name,Domain type,Agency,Organization name,City,State,AO," "Domain name,Domain type,Agency,Organization name,City,State,AO,"
"AO email,Security contact email,Status,Expiration date\n" "AO email,Security contact email,Status,Expiration date, First ready on\n"
"adomain10.gov,Federal,Armed Forces Retirement Home,Ready\n" "adomain10.gov,Federal,Armed Forces Retirement Home,Ready,(blank),2024-04-03\n"
"adomain2.gov,Interstate,(blank),Dns needed\n" "adomain2.gov,Interstate,(blank),Dns needed,(blank),(blank)\n"
"cdomain11.govFederal-ExecutiveWorldWarICentennialCommissionReady\n" "cdomain11.gov,Federal-Executive,WorldWarICentennialCommission,Ready,(blank),2024-04-02\n"
"ddomain3.gov,Federal,Armed Forces Retirement Home,security@mail.gov,On hold,2023-11-15\n" "ddomain3.gov,Federal,Armed Forces Retirement Home,security@mail.gov,On hold,2023-11-15,(blank)\n"
"defaultsecurity.gov,Federal - Executive,World War I Centennial Commission,(blank),Ready\n" "defaultsecurity.gov,Federal - Executive,World War I Centennial Commission,"
"zdomain12.govInterstateReady\n" "(blank),Ready,(blank),2023-11-01\n"
"zdomain12.govInterstateReady,(blank),2024-04-02\n"
) )
# Normalize line endings and remove commas, # Normalize line endings and remove commas,
# spaces and leading/trailing whitespace # spaces and leading/trailing whitespace
@ -470,18 +476,18 @@ class ExportDataTest(MockDb, MockEppLib):
# Read the content into a variable # Read the content into a variable
csv_content = csv_file.read() csv_content = csv_file.read()
# We expect READY domains first, created between today-2 and today+2, sorted by created_at then name # We expect READY domains first, created between day-2 and day+2, sorted by created_at then name
# and DELETED domains deleted between today-2 and today+2, sorted by deleted then name # and DELETED domains deleted between day-2 and day+2, sorted by deleted then name
expected_content = ( expected_content = (
"Domain name,Domain type,Agency,Organization name,City," "Domain name,Domain type,Agency,Organization name,City,"
"State,Status,Expiration date\n" "State,Status,Expiration date\n"
"cdomain1.gov,Federal-Executive,World War I Centennial Commission,,,,Ready,\n" "cdomain1.gov,Federal-Executive,World War I Centennial Commission,,,,Ready,(blank)\n"
"adomain10.gov,Federal,Armed Forces Retirement Home,,,,Ready,\n" "adomain10.gov,Federal,Armed Forces Retirement Home,,,,Ready,(blank)\n"
"cdomain11.govFederal-ExecutiveWorldWarICentennialCommissionReady\n" "cdomain11.govFederal-ExecutiveWorldWarICentennialCommissionReady(blank)\n"
"zdomain12.govInterstateReady\n" "zdomain12.govInterstateReady(blank)\n"
"zdomain9.gov,Federal,Armed Forces Retirement Home,,,,Deleted,\n" "zdomain9.gov,Federal,ArmedForcesRetirementHome,Deleted,(blank)\n"
"sdomain8.gov,Federal,Armed Forces Retirement Home,,,,Deleted,\n" "sdomain8.gov,Federal,Armed Forces Retirement Home,,,,Deleted,(blank)\n"
"xdomain7.gov,Federal,Armed Forces Retirement Home,,,,Deleted,\n" "xdomain7.gov,FederalArmedForcesRetirementHome,Deleted,(blank)\n"
) )
# Normalize line endings and remove commas, # Normalize line endings and remove commas,
@ -526,7 +532,7 @@ class ExportDataTest(MockDb, MockEppLib):
Domain.State.ON_HOLD, Domain.State.ON_HOLD,
], ],
} }
self.maxDiff = None
# Call the export functions # Call the export functions
write_csv_for_domains( write_csv_for_domains(
writer, writer,
@ -548,14 +554,14 @@ class ExportDataTest(MockDb, MockEppLib):
"Organization name,City,State,AO,AO email," "Organization name,City,State,AO,AO email,"
"Security contact email,Domain manager 1,DM1 status,Domain manager 2,DM2 status," "Security contact email,Domain manager 1,DM1 status,Domain manager 2,DM2 status,"
"Domain manager 3,DM3 status,Domain manager 4,DM4 status\n" "Domain manager 3,DM3 status,Domain manager 4,DM4 status\n"
"adomain10.gov,Ready,,Federal,Armed Forces Retirement Home,,,, , ,squeaker@rocks.com, I\n" "adomain10.gov,Ready,(blank),Federal,Armed Forces Retirement Home,,,, , ,squeaker@rocks.com, I\n"
"adomain2.gov,Dns needed,,Interstate,,,,, , , ,meoward@rocks.com, R,squeaker@rocks.com, I\n" "adomain2.gov,Dns needed,(blank),Interstate,,,,, , , ,meoward@rocks.com, R,squeaker@rocks.com, I\n"
"cdomain11.govReadyFederal-ExecutiveWorldWarICentennialCommissionmeoward@rocks.comR\n" "cdomain11.govReady,(blank),Federal-ExecutiveWorldWarICentennialCommissionmeoward@rocks.comR\n"
"cdomain1.gov,Ready,,Federal - Executive,World War I Centennial Commission,,," "cdomain1.gov,Ready,(blank),Federal - Executive,World War I Centennial Commission,,,"
", , , ,meoward@rocks.com,R,info@example.com,R,big_lebowski@dude.co,R," ", , , ,meoward@rocks.com,R,info@example.com,R,big_lebowski@dude.co,R,"
"woofwardthethird@rocks.com,I\n" "woofwardthethird@rocks.com,I\n"
"ddomain3.gov,On hold,,Federal,Armed Forces Retirement Home,,,, , , ,,\n" "ddomain3.gov,On hold,(blank),Federal,Armed Forces Retirement Home,,,, , , ,,\n"
"zdomain12.govReadyInterstatemeoward@rocks.comR\n" "zdomain12.gov,Ready,(blank),Interstate,meoward@rocks.com,R\n"
) )
# Normalize line endings and remove commas, # Normalize line endings and remove commas,
# spaces and leading/trailing whitespace # spaces and leading/trailing whitespace
@ -580,7 +586,7 @@ class ExportDataTest(MockDb, MockEppLib):
csv_file.seek(0) csv_file.seek(0)
# Read the content into a variable # Read the content into a variable
csv_content = csv_file.read() csv_content = csv_file.read()
self.maxDiff = None
# We expect the READY domain names with the domain managers: Their counts, and listing at end_date. # We expect the READY domain names with the domain managers: Their counts, and listing at end_date.
expected_content = ( expected_content = (
"MANAGED DOMAINS COUNTS AT START DATE\n" "MANAGED DOMAINS COUNTS AT START DATE\n"
@ -623,7 +629,7 @@ class ExportDataTest(MockDb, MockEppLib):
csv_file.seek(0) csv_file.seek(0)
# Read the content into a variable # Read the content into a variable
csv_content = csv_file.read() csv_content = csv_file.read()
self.maxDiff = None
# We expect the READY domain names with the domain managers: Their counts, and listing at end_date. # We expect the READY domain names with the domain managers: Their counts, and listing at end_date.
expected_content = ( expected_content = (
"UNMANAGED DOMAINS AT START DATE\n" "UNMANAGED DOMAINS AT START DATE\n"
@ -698,7 +704,7 @@ class HelperFunctions(MockDb):
"""This asserts that 1=1. Its limited usefulness lies in making sure the helper methods stay healthy.""" """This asserts that 1=1. Its limited usefulness lies in making sure the helper methods stay healthy."""
def test_get_default_start_date(self): def test_get_default_start_date(self):
expected_date = timezone.make_aware(datetime(2023, 11, 1)) expected_date = get_time_aware_date()
actual_date = get_default_start_date() actual_date = get_default_start_date()
self.assertEqual(actual_date, expected_date) self.assertEqual(actual_date, expected_date)

View file

@ -96,7 +96,8 @@ def parse_row_for_domain(
FIELDS = { FIELDS = {
"Domain name": domain.name, "Domain name": domain.name,
"Status": domain.get_state_display(), "Status": domain.get_state_display(),
"Expiration date": domain.expiration_date, "First ready on": domain.first_ready or "(blank)",
"Expiration date": domain.expiration_date or "(blank)",
"Domain type": domain_type, "Domain type": domain_type,
"Agency": domain_info.federal_agency, "Agency": domain_info.federal_agency,
"Organization name": domain_info.organization_name, "Organization name": domain_info.organization_name,
@ -106,7 +107,6 @@ def parse_row_for_domain(
"AO email": domain_info.authorizing_official.email if domain_info.authorizing_official else " ", "AO email": domain_info.authorizing_official.email if domain_info.authorizing_official else " ",
"Security contact email": security_email, "Security contact email": security_email,
"Created at": domain.created_at, "Created at": domain.created_at,
"First ready": domain.first_ready,
"Deleted": domain.deleted, "Deleted": domain.deleted,
} }
@ -378,13 +378,17 @@ def write_csv_for_requests(
def export_data_type_to_csv(csv_file): def export_data_type_to_csv(csv_file):
"""All domains report with extra columns""" """
All domains report with extra columns.
This maps to the "All domain metadata" button.
"""
writer = csv.writer(csv_file) writer = csv.writer(csv_file)
# define columns to include in export # define columns to include in export
columns = [ columns = [
"Domain name", "Domain name",
"Status", "Status",
"First ready on",
"Expiration date", "Expiration date",
"Domain type", "Domain type",
"Agency", "Agency",