Unit tests

This commit is contained in:
Rachid Mrad 2024-06-28 16:34:39 -04:00
parent 59e912b69a
commit b600a26eb8
No known key found for this signature in database
5 changed files with 534 additions and 704 deletions

View file

@ -50,7 +50,7 @@ class Command(BaseCommand):
# Generate a file locally for upload # Generate a file locally for upload
with open(file_path, "w") as file: with open(file_path, "w") as file:
csv_export.export_data_federal_to_csv(file) csv_export.DomainDataFederal.export_data_to_csv(file)
if check_path and not os.path.exists(file_path): if check_path and not os.path.exists(file_path):
raise FileNotFoundError(f"Could not find newly created file at '{file_path}'") raise FileNotFoundError(f"Could not find newly created file at '{file_path}'")

View file

@ -49,7 +49,7 @@ class Command(BaseCommand):
# Generate a file locally for upload # Generate a file locally for upload
with open(file_path, "w") as file: with open(file_path, "w") as file:
csv_export.export_data_full_to_csv(file) csv_export.DomainDataFull.export_data_to_csv(file)
if check_path and not os.path.exists(file_path): if check_path and not os.path.exists(file_path):
raise FileNotFoundError(f"Could not find newly created file at '{file_path}'") raise FileNotFoundError(f"Could not find newly created file at '{file_path}'")

View file

@ -1,21 +1,23 @@
import csv
import io import io
from django.test import Client, RequestFactory from django.test import Client, RequestFactory
from io import StringIO from io import StringIO
from registrar.models.domain_request import DomainRequest from registrar.models.domain_request import DomainRequest
from registrar.models.domain import Domain from registrar.models.domain import Domain
from registrar.models.utility.generic_helper import convert_queryset_to_dict
from registrar.utility.csv_export import ( from registrar.utility.csv_export import (
export_data_managed_domains_to_csv, DomainDataFull,
export_data_unmanaged_domains_to_csv, DomainDataType,
get_sliced_domains, DomainDataFederal,
get_sliced_requests, DomainGrowth,
write_csv_for_domains, DomainManaged,
DomainUnmanaged,
DomainExport,
DomainRequestExport,
DomainRequestGrowth,
DomainRequestDataFull,
get_default_start_date, get_default_start_date,
get_default_end_date, get_default_end_date,
DomainRequestExport,
) )
from django.db.models import Case, When
from django.core.management import call_command from django.core.management import call_command
from unittest.mock import MagicMock, call, mock_open, patch from unittest.mock import MagicMock, call, mock_open, patch
from api.views import get_current_federal, get_current_full from api.views import get_current_federal, get_current_full
@ -45,10 +47,10 @@ class CsvReportsTest(MockDb):
fake_open = mock_open() fake_open = mock_open()
expected_file_content = [ expected_file_content = [
call("Domain name,Domain type,Agency,Organization name,City,State,Security contact email\r\n"), call("Domain name,Domain type,Agency,Organization name,City,State,Security contact email\r\n"),
call("cdomain11.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n"), call("cdomain11.gov,Federal - Executive,World War I Centennial Commission,,,,\r\n"),
call("cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n"), call("cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\r\n"),
call("adomain10.gov,Federal,Armed Forces Retirement Home,,,, \r\n"), call("adomain10.gov,Federal,Armed Forces Retirement Home,,,,\r\n"),
call("ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n"), call("ddomain3.gov,Federal,Armed Forces Retirement Home,,,,\r\n"),
] ]
# We don't actually want to write anything for a test case, # We don't actually want to write anything for a test case,
# we just want to verify what is being written. # we just want to verify what is being written.
@ -67,11 +69,12 @@ class CsvReportsTest(MockDb):
fake_open = mock_open() fake_open = mock_open()
expected_file_content = [ expected_file_content = [
call("Domain name,Domain type,Agency,Organization name,City,State,Security contact email\r\n"), call("Domain name,Domain type,Agency,Organization name,City,State,Security contact email\r\n"),
call("cdomain11.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n"), call("cdomain11.gov,Federal - Executive,World War I Centennial Commission,,,,\r\n"),
call("cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n"), call("cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\r\n"),
call("adomain10.gov,Federal,Armed Forces Retirement Home,,,, \r\n"), call("adomain10.gov,Federal,Armed Forces Retirement Home,,,,\r\n"),
call("ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n"), call("ddomain3.gov,Federal,Armed Forces Retirement Home,,,,\r\n"),
call("adomain2.gov,Interstate,,,,, \r\n"), call("adomain2.gov,Interstate,,,,,\r\n"),
call("zdomain12.gov,Interstate,,,,,\r\n"),
] ]
# We don't actually want to write anything for a test case, # We don't actually want to write anything for a test case,
# we just want to verify what is being written. # we just want to verify what is being written.
@ -202,494 +205,299 @@ class ExportDataTest(MockDb, MockEppLib):
def tearDown(self): def tearDown(self):
super().tearDown() super().tearDown()
def test_export_domains_to_writer_security_emails_and_first_ready(self): @less_console_noise_decorator
"""Test that export_domains_to_writer returns the def test_domain_data_type(self):
expected security email and first_ready value""" """Shows security contacts, domain managers, ao"""
# Add security email information
self.domain_1.name = "defaultsecurity.gov"
self.domain_1.save()
# Invoke setter
self.domain_1.security_contact
# Invoke setter
self.domain_2.security_contact
# Invoke setter
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
csv_file = StringIO()
# Call the export functions
DomainDataType.export_data_to_csv(csv_file)
# Reset the CSV file's position to the beginning
csv_file.seek(0)
# Read the content into a variable
csv_content = csv_file.read()
# We expect READY domains,
# sorted alphabetially by domain name
expected_content = (
"Domain name,Status,First ready on,Expiration date,Domain type,Agency,Organization name,City,State,AO,"
"AO email,Security contact email,Domain managers,Invited domain managers\n"
"cdomain11.gov,Ready,2024-04-02,(blank),Federal - Executive,World War I Centennial Commission,,,, ,,,"
"meoward@rocks.com,\n"
"defaultsecurity.gov,Ready,2023-11-01,(blank),Federal - Executive,World War I Centennial Commission,,,"
', ,,dotgov@cisa.dhs.gov,"meoward@rocks.com, info@example.com, big_lebowski@dude.co",'
"woofwardthethird@rocks.com\n"
"adomain10.gov,Ready,2024-04-03,(blank),Federal,Armed Forces Retirement Home,,,, ,,,,"
"squeaker@rocks.com\n"
"bdomain4.gov,Unknown,(blank),(blank),Federal,Armed Forces Retirement Home,,,, ,,,,\n"
"bdomain5.gov,Deleted,(blank),(blank),Federal,Armed Forces Retirement Home,,,, ,,,,\n"
"bdomain6.gov,Deleted,(blank),(blank),Federal,Armed Forces Retirement Home,,,, ,,,,\n"
"ddomain3.gov,On hold,(blank),2023-11-15,Federal,Armed Forces Retirement Home,,,, ,,"
"security@mail.gov,,\n"
"sdomain8.gov,Deleted,(blank),(blank),Federal,Armed Forces Retirement Home,,,, ,,,,\n"
"xdomain7.gov,Deleted,(blank),(blank),Federal,Armed Forces Retirement Home,,,, ,,,,\n"
"zdomain9.gov,Deleted,(blank),(blank),Federal,Armed Forces Retirement Home,,,, ,,,,\n"
"adomain2.gov,Dns needed,(blank),(blank),Interstate,,,,, ,,registrar@dotgov.gov,"
"meoward@rocks.com,squeaker@rocks.com\n"
"zdomain12.gov,Ready,2024-04-02,(blank),Interstate,,,,, ,,,meoward@rocks.com,\n"
)
# Normalize line endings and remove commas,
# spaces and leading/trailing whitespace
csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip()
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content)
with less_console_noise(): @less_console_noise_decorator
# Add security email information def test_domain_data_full(self):
self.domain_1.name = "defaultsecurity.gov" """Shows security contacts, filtered by state"""
self.domain_1.save() # Add security email information
# Invoke setter self.domain_1.name = "defaultsecurity.gov"
self.domain_1.security_contact self.domain_1.save()
# Invoke setter # Invoke setter
self.domain_2.security_contact self.domain_1.security_contact
# Invoke setter # Invoke setter
self.domain_3.security_contact self.domain_2.security_contact
# Invoke setter
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
csv_file = StringIO()
# Call the export functions
DomainDataFull.export_data_to_csv(csv_file)
# Reset the CSV file's position to the beginning
csv_file.seek(0)
# Read the content into a variable
csv_content = csv_file.read()
# We expect READY domains,
# sorted alphabetially by domain name
expected_content = (
"Domain name,Domain type,Agency,Organization name,City,State,Security contact email\n"
"cdomain11.gov,Federal - Executive,World War I Centennial Commission,,,,\n"
"defaultsecurity.gov,Federal - Executive,World War I Centennial Commission,,,,dotgov@cisa.dhs.gov\n"
"adomain10.gov,Federal,Armed Forces Retirement Home,,,,\n"
"ddomain3.gov,Federal,Armed Forces Retirement Home,,,,security@mail.gov\n"
"adomain2.gov,Interstate,,,,,registrar@dotgov.gov\n"
"zdomain12.gov,Interstate,,,,,\n"
)
# Normalize line endings and remove commas,
# spaces and leading/trailing whitespace
csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip()
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content)
# Add a first ready date on the first domain. Leaving the others blank. @less_console_noise_decorator
self.domain_1.first_ready = get_default_start_date() def test_domain_data_federal(self):
self.domain_1.save() """Shows security contacts, filtered by state and org type"""
# Add security email information
self.domain_1.name = "defaultsecurity.gov"
self.domain_1.save()
# Invoke setter
self.domain_1.security_contact
# Invoke setter
self.domain_2.security_contact
# Invoke setter
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
csv_file = StringIO()
# Call the export functions
DomainDataFederal.export_data_to_csv(csv_file)
# Reset the CSV file's position to the beginning
csv_file.seek(0)
# Read the content into a variable
csv_content = csv_file.read()
# We expect READY domains,
# sorted alphabetially by domain name
expected_content = (
"Domain name,Domain type,Agency,Organization name,City,State,Security contact email\n"
"cdomain11.gov,Federal - Executive,World War I Centennial Commission,,,,\n"
"defaultsecurity.gov,Federal - Executive,World War I Centennial Commission,,,,dotgov@cisa.dhs.gov\n"
"adomain10.gov,Federal,Armed Forces Retirement Home,,,,\n"
"ddomain3.gov,Federal,Armed Forces Retirement Home,,,,security@mail.gov\n"
)
# Normalize line endings and remove commas,
# spaces and leading/trailing whitespace
csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip()
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content)
# Create a CSV file in memory @less_console_noise_decorator
csv_file = StringIO() def test_domain_growth(self):
writer = csv.writer(csv_file) """Shows ready and deleted domains within a date range, sorted"""
# Define columns, sort fields, and filter condition # Remove "Created at" and "First ready" because we can't guess this immutable, dynamically generated test data
columns = [ columns = [
"Domain name", "Domain name",
"Domain type", "Domain type",
"Agency", "Agency",
"Organization name", "Organization name",
"City", "City",
"State", "State",
"AO", "Status",
"AO email", "Expiration date",
"Security contact email", # "Created at",
"Status", # "First ready",
"Expiration date", "Deleted",
"First ready on", ]
] sort = {
sort_fields = ["domain__name"] "custom_sort": Case(
filter_condition = { When(domain__state=Domain.State.READY, then="domain__created_at"),
"domain__state__in": [ When(domain__state=Domain.State.DELETED, then="domain__deleted"),
Domain.State.READY,
Domain.State.DNS_NEEDED,
Domain.State.ON_HOLD,
],
}
# Call the export functions
write_csv_for_domains(
writer,
columns,
sort_fields,
filter_condition,
should_get_domain_managers=False,
should_write_header=True,
) )
}
with patch("registrar.utility.csv_export.DomainGrowth.get_columns", return_value=columns):
with patch("registrar.utility.csv_export.DomainGrowth.get_annotations_for_sort", return_value=sort):
# Create a CSV file in memory
csv_file = StringIO()
# Call the export functions
DomainGrowth.export_data_to_csv(
csv_file,
self.start_date.strftime("%Y-%m-%d"),
self.end_date.strftime("%Y-%m-%d"),
)
# Reset the CSV file's position to the beginning
csv_file.seek(0)
# Read the content into a variable
csv_content = csv_file.read()
# We expect READY domains first, created between day-2 and day+2, sorted by created_at then name
# and DELETED domains deleted between day-2 and day+2, sorted by deleted then name
expected_content = (
"Domain name,Domain type,Agency,Organization name,City,"
"State,Status,Expiration date, Deleted\n"
"cdomain1.gov,Federal-Executive,World War I Centennial Commission,,,,Ready,(blank)\n"
"adomain10.gov,Federal,Armed Forces Retirement Home,,,,Ready,(blank)\n"
"cdomain11.govFederal-ExecutiveWorldWarICentennialCommissionReady(blank)\n"
"zdomain12.govInterstateReady(blank)\n"
"zdomain9.gov,Federal,ArmedForcesRetirementHome,Deleted,(blank),2024-04-01\n"
"sdomain8.gov,Federal,Armed Forces Retirement Home,,,,Deleted,(blank),2024-04-02\n"
"xdomain7.gov,FederalArmedForcesRetirementHome,Deleted,(blank),2024-04-02\n"
)
# Normalize line endings and remove commas,
# spaces and leading/trailing whitespace
csv_content = (
csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip()
)
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content)
# Reset the CSV file's position to the beginning @less_console_noise_decorator
csv_file.seek(0) def test_domain_managed(self):
# Read the content into a variable """Shows ready and deleted domains by an end date, sorted
csv_content = csv_file.read()
# We expect READY domains,
# sorted alphabetially by domain name
expected_content = (
"Domain name,Domain type,Agency,Organization name,City,State,AO,"
"AO email,Security contact email,Status,Expiration date, First ready on\n"
"adomain10.gov,Federal,Armed Forces Retirement Home,Ready,(blank),2024-04-03\n"
"adomain2.gov,Interstate,(blank),Dns needed,(blank),(blank)\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,(blank)\n"
"defaultsecurity.gov,Federal - Executive,World War I Centennial Commission,"
"(blank),Ready,(blank),2023-11-01\n"
"zdomain12.govInterstateReady,(blank),2024-04-02\n"
)
# Normalize line endings and remove commas,
# spaces and leading/trailing whitespace
csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip()
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content)
def test_write_csv_for_domains(self):
"""Test that write_body returns the
existing domain, test that sort by domain name works,
test that filter works"""
with less_console_noise():
# Create a CSV file in memory
csv_file = StringIO()
writer = csv.writer(csv_file)
# Define columns, sort fields, and filter condition
columns = [
"Domain name",
"Domain type",
"Agency",
"Organization name",
"City",
"State",
"AO",
"AO email",
"Submitter",
"Submitter title",
"Submitter email",
"Submitter phone",
"Security contact email",
"Status",
]
sort_fields = ["domain__name"]
filter_condition = {
"domain__state__in": [
Domain.State.READY,
Domain.State.DNS_NEEDED,
Domain.State.ON_HOLD,
],
}
# Call the export functions
write_csv_for_domains(
writer,
columns,
sort_fields,
filter_condition,
should_get_domain_managers=False,
should_write_header=True,
)
# Reset the CSV file's position to the beginning
csv_file.seek(0)
# Read the content into a variable
csv_content = csv_file.read()
# We expect READY domains,
# sorted alphabetially by domain name
expected_content = (
"Domain name,Domain type,Agency,Organization name,City,State,AO,"
"AO email,Submitter,Submitter title,Submitter email,Submitter phone,"
"Security contact email,Status\n"
"adomain10.gov,Federal,Armed Forces Retirement Home,Ready\n"
"adomain2.gov,Interstate,Dns needed\n"
"cdomain11.govFederal-ExecutiveWorldWarICentennialCommissionReady\n"
"cdomain1.gov,Federal - Executive,World War I Centennial Commission,Ready\n"
"ddomain3.gov,Federal,Armed Forces Retirement Home,On hold\n"
"zdomain12.govInterstateReady\n"
)
# Normalize line endings and remove commas,
# spaces and leading/trailing whitespace
csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip()
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content)
def test_write_domains_body_additional(self):
"""An additional test for filters and multi-column sort"""
with less_console_noise():
# Create a CSV file in memory
csv_file = StringIO()
writer = csv.writer(csv_file)
# Define columns, sort fields, and filter condition
columns = [
"Domain name",
"Domain type",
"Agency",
"Organization name",
"City",
"State",
"Security contact email",
]
sort_fields = ["domain__name", "federal_agency", "generic_org_type"]
filter_condition = {
"generic_org_type__icontains": "federal",
"domain__state__in": [
Domain.State.READY,
Domain.State.DNS_NEEDED,
Domain.State.ON_HOLD,
],
}
# Call the export functions
write_csv_for_domains(
writer,
columns,
sort_fields,
filter_condition,
should_get_domain_managers=False,
should_write_header=True,
)
# Reset the CSV file's position to the beginning
csv_file.seek(0)
# Read the content into a variable
csv_content = csv_file.read()
# We expect READY domains,
# federal only
# sorted alphabetially by domain name
expected_content = (
"Domain name,Domain type,Agency,Organization name,City,"
"State,Security contact email\n"
"adomain10.gov,Federal,Armed Forces Retirement Home\n"
"cdomain11.govFederal-ExecutiveWorldWarICentennialCommission\n"
"cdomain1.gov,Federal - Executive,World War I Centennial Commission\n"
"ddomain3.gov,Federal,Armed Forces Retirement Home\n"
)
# Normalize line endings and remove commas,
# spaces and leading/trailing whitespace
csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip()
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content)
def test_write_domains_body_with_date_filter_pulls_domains_in_range(self):
"""Test that domains that are
1. READY and their first_ready dates are in range
2. DELETED and their deleted dates are in range
are pulled when the growth report conditions are applied to export_domains_to_writed.
Test that ready domains are sorted by first_ready/deleted dates first, names second.
We considered testing export_data_domain_growth_to_csv which calls write_body
and would have been easy to set up, but expected_content would contain created_at dates
which are hard to mock.
TODO: Simplify if created_at is not needed for the report."""
with less_console_noise():
# Create a CSV file in memory
csv_file = StringIO()
writer = csv.writer(csv_file)
# Define columns, sort fields, and filter condition
columns = [
"Domain name",
"Domain type",
"Agency",
"Organization name",
"City",
"State",
"Status",
"Expiration date",
]
sort_fields = [
"created_at",
"domain__name",
]
sort_fields_for_deleted_domains = [
"domain__deleted",
"domain__name",
]
filter_condition = {
"domain__state__in": [
Domain.State.READY,
],
"domain__first_ready__lte": self.end_date,
"domain__first_ready__gte": self.start_date,
}
filter_conditions_for_deleted_domains = {
"domain__state__in": [
Domain.State.DELETED,
],
"domain__deleted__lte": self.end_date,
"domain__deleted__gte": self.start_date,
}
# Call the export functions
write_csv_for_domains(
writer,
columns,
sort_fields,
filter_condition,
should_get_domain_managers=False,
should_write_header=True,
)
write_csv_for_domains(
writer,
columns,
sort_fields_for_deleted_domains,
filter_conditions_for_deleted_domains,
should_get_domain_managers=False,
should_write_header=False,
)
# Reset the CSV file's position to the beginning
csv_file.seek(0)
# Read the content into a variable
csv_content = csv_file.read()
# We expect READY domains first, created between day-2 and day+2, sorted by created_at then name
# and DELETED domains deleted between day-2 and day+2, sorted by deleted then name
expected_content = (
"Domain name,Domain type,Agency,Organization name,City,"
"State,Status,Expiration date\n"
"cdomain1.gov,Federal-Executive,World War I Centennial Commission,,,,Ready,(blank)\n"
"adomain10.gov,Federal,Armed Forces Retirement Home,,,,Ready,(blank)\n"
"cdomain11.govFederal-ExecutiveWorldWarICentennialCommissionReady(blank)\n"
"zdomain12.govInterstateReady(blank)\n"
"zdomain9.gov,Federal,ArmedForcesRetirementHome,Deleted,(blank)\n"
"sdomain8.gov,Federal,Armed Forces Retirement Home,,,,Deleted,(blank)\n"
"xdomain7.gov,FederalArmedForcesRetirementHome,Deleted,(blank)\n"
)
# Normalize line endings and remove commas,
# spaces and leading/trailing whitespace
csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip()
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content)
def test_export_domains_to_writer_domain_managers(self):
"""Test that export_domains_to_writer returns the
expected domain managers.
An invited user, woofwardthethird, should also be pulled into this report. An invited user, woofwardthethird, should also be pulled into this report.
squeaker@rocks.com is invited to domain2 (DNS_NEEDED) and domain10 (No managers). squeaker@rocks.com is invited to domain2 (DNS_NEEDED) and domain10 (No managers).
She should show twice in this report but not in test_export_data_managed_domains_to_csv.""" She should show twice in this report but not in test_DomainManaged."""
# Create a CSV file in memory
csv_file = StringIO()
# Call the export functions
DomainManaged.export_data_to_csv(
csv_file,
self.start_date.strftime("%Y-%m-%d"),
self.end_date.strftime("%Y-%m-%d"),
)
# Reset the CSV file's position to the beginning
csv_file.seek(0)
# Read the content into a variable
csv_content = csv_file.read()
# We expect the READY domain names with the domain managers: Their counts, and listing at end_date.
expected_content = (
"MANAGED DOMAINS COUNTS AT START DATE\n"
"Total,Federal,Interstate,State or territory,Tribal,County,City,Special district,"
"School district,Election office\n"
"0,0,0,0,0,0,0,0,0,0\n"
"\n"
"MANAGED DOMAINS COUNTS AT END DATE\n"
"Total,Federal,Interstate,State or territory,Tribal,County,City,"
"Special district,School district,Election office\n"
"3,2,1,0,0,0,0,0,0,0\n"
"\n"
"Domain name,Domain type,Domain managers,Invited domain managers\n"
"cdomain11.gov,Federal - Executive,meoward@rocks.com,\n"
'cdomain1.gov,Federal - Executive,"meoward@rocks.com, info@example.com, big_lebowski@dude.co",'
"woofwardthethird@rocks.com\n"
"zdomain12.gov,Interstate,meoward@rocks.com,\n"
)
# Normalize line endings and remove commas,
# spaces and leading/trailing whitespace
csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip()
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content)
with less_console_noise(): @less_console_noise_decorator
def test_domain_unmanaged(self):
"""Shows unmanaged domains by an end date, sorted"""
# Create a CSV file in memory
csv_file = StringIO()
DomainUnmanaged.export_data_to_csv(
csv_file, self.start_date.strftime("%Y-%m-%d"), self.end_date.strftime("%Y-%m-%d")
)
# Reset the CSV file's position to the beginning
csv_file.seek(0)
# Read the content into a variable
csv_content = csv_file.read()
# We expect the READY domain names with the domain managers: Their counts, and listing at end_date.
expected_content = (
"UNMANAGED DOMAINS AT START DATE\n"
"Total,Federal,Interstate,State or territory,Tribal,County,City,Special district,"
"School district,Election office\n"
"0,0,0,0,0,0,0,0,0,0\n"
"\n"
"UNMANAGED DOMAINS AT END DATE\n"
"Total,Federal,Interstate,State or territory,Tribal,County,City,Special district,"
"School district,Election office\n"
"1,1,0,0,0,0,0,0,0,0\n"
"\n"
"Domain name,Domain type\n"
"adomain10.gov,Federal\n"
)
# Normalize line endings and remove commas,
# spaces and leading/trailing whitespace
csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip()
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content)
@less_console_noise_decorator
def test_domain_request_growth(self):
"""Shows submitted requests within a date range, sorted"""
# Remove "Submitted at" because we can't guess this immutable, dynamically generated test data
columns = [
"Domain request",
"Domain type",
"Federal type",
# "Submitted at",
]
with patch("registrar.utility.csv_export.DomainRequestGrowth.get_columns", return_value=columns):
# Create a CSV file in memory # Create a CSV file in memory
csv_file = StringIO() csv_file = StringIO()
writer = csv.writer(csv_file)
# Define columns, sort fields, and filter condition
columns = [
"Domain name",
"Status",
"Expiration date",
"Domain type",
"Agency",
"Organization name",
"City",
"State",
"AO",
"AO email",
"Security contact email",
]
sort_fields = ["domain__name"]
filter_condition = {
"domain__state__in": [
Domain.State.READY,
Domain.State.DNS_NEEDED,
Domain.State.ON_HOLD,
],
}
# Call the export functions # Call the export functions
write_csv_for_domains( DomainRequestGrowth.export_data_to_csv(
writer, csv_file,
columns, self.start_date.strftime("%Y-%m-%d"),
sort_fields, self.end_date.strftime("%Y-%m-%d"),
filter_condition,
should_get_domain_managers=True,
should_write_header=True,
) )
# Reset the CSV file's position to the beginning # Reset the CSV file's position to the beginning
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()
# We expect READY domains,
# sorted alphabetially by domain name
expected_content = (
"Domain name,Status,Expiration date,Domain type,Agency,"
"Organization name,City,State,AO,AO email,"
"Security contact email,Domain manager 1,DM1 status,Domain manager 2,DM2 status,"
"Domain manager 3,DM3 status,Domain manager 4,DM4 status\n"
"adomain10.gov,Ready,(blank),Federal,Armed Forces Retirement Home,,,, , ,squeaker@rocks.com, I\n"
"adomain2.gov,Dns needed,(blank),Interstate,,,,, , , ,meoward@rocks.com, R,squeaker@rocks.com, I\n"
"cdomain11.govReady,(blank),Federal-ExecutiveWorldWarICentennialCommissionmeoward@rocks.comR\n"
"cdomain1.gov,Ready,(blank),Federal - Executive,World War I Centennial Commission,,,"
", , , ,meoward@rocks.com,R,info@example.com,R,big_lebowski@dude.co,R,"
"woofwardthethird@rocks.com,I\n"
"ddomain3.gov,On hold,(blank),Federal,Armed Forces Retirement Home,,,, , , ,,\n"
"zdomain12.gov,Ready,(blank),Interstate,meoward@rocks.com,R\n"
)
# Normalize line endings and remove commas,
# spaces and leading/trailing whitespace
csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip()
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content)
def test_export_data_managed_domains_to_csv(self):
"""Test get counts for domains that have domain managers for two different dates,
get list of managed domains at end_date.
An invited user, woofwardthethird, should also be pulled into this report."""
with less_console_noise():
# Create a CSV file in memory
csv_file = StringIO()
export_data_managed_domains_to_csv(
csv_file, self.start_date.strftime("%Y-%m-%d"), self.end_date.strftime("%Y-%m-%d")
)
# Reset the CSV file's position to the beginning
csv_file.seek(0)
# Read the content into a variable
csv_content = csv_file.read()
# We expect the READY domain names with the domain managers: Their counts, and listing at end_date.
expected_content = (
"MANAGED DOMAINS COUNTS AT START DATE\n"
"Total,Federal,Interstate,State or territory,Tribal,County,City,Special district,"
"School district,Election office\n"
"0,0,0,0,0,0,0,0,0,0\n"
"\n"
"MANAGED DOMAINS COUNTS AT END DATE\n"
"Total,Federal,Interstate,State or territory,Tribal,County,City,"
"Special district,School district,Election office\n"
"3,2,1,0,0,0,0,0,0,0\n"
"\n"
"Domain name,Domain type,Domain manager 1,DM1 status,Domain manager 2,DM2 status,"
"Domain manager 3,DM3 status,Domain manager 4,DM4 status\n"
"cdomain11.govFederal-Executivemeoward@rocks.com, R\n"
"cdomain1.gov,Federal - Executive,meoward@rocks.com,R,info@example.com,R,"
"big_lebowski@dude.co,R,woofwardthethird@rocks.com,I\n"
"zdomain12.govInterstatemeoward@rocks.com,R\n"
)
# Normalize line endings and remove commas,
# spaces and leading/trailing whitespace
csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip()
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content)
def test_export_data_unmanaged_domains_to_csv(self):
"""Test get counts for domains that do not have domain managers for two different dates,
get list of unmanaged domains at end_date."""
with less_console_noise():
# Create a CSV file in memory
csv_file = StringIO()
export_data_unmanaged_domains_to_csv(
csv_file, self.start_date.strftime("%Y-%m-%d"), self.end_date.strftime("%Y-%m-%d")
)
# Reset the CSV file's position to the beginning
csv_file.seek(0)
# Read the content into a variable
csv_content = csv_file.read()
# We expect the READY domain names with the domain managers: Their counts, and listing at end_date.
expected_content = (
"UNMANAGED DOMAINS AT START DATE\n"
"Total,Federal,Interstate,State or territory,Tribal,County,City,Special district,"
"School district,Election office\n"
"0,0,0,0,0,0,0,0,0,0\n"
"\n"
"UNMANAGED DOMAINS AT END DATE\n"
"Total,Federal,Interstate,State or territory,Tribal,County,City,Special district,"
"School district,Election office\n"
"1,1,0,0,0,0,0,0,0,0\n"
"\n"
"Domain name,Domain type\n"
"adomain10.gov,Federal\n"
)
# Normalize line endings and remove commas,
# spaces and leading/trailing whitespace
csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip()
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content)
def test_write_requests_body_with_date_filter_pulls_requests_in_range(self):
"""Test that requests that are
1. SUBMITTED and their submission_date are in range
are pulled when the growth report conditions are applied to export_requests_to_writed.
Test that requests are sorted by requested domain name.
"""
with less_console_noise():
# Create a CSV file in memory
csv_file = StringIO()
writer = csv.writer(csv_file)
# Define columns, sort fields, and filter condition
# We'll skip submission date because it's dynamic and therefore
# impossible to set in expected_content
columns = ["Domain request", "Domain type", "Federal type"]
sort_fields = [
"requested_domain__name",
]
filter_condition = {
"status": DomainRequest.DomainRequestStatus.SUBMITTED,
"submission_date__lte": self.end_date,
"submission_date__gte": self.start_date,
}
additional_values = ["requested_domain__name"]
all_requests = DomainRequest.objects.filter(**filter_condition).order_by(*sort_fields).distinct()
annotated_requests = DomainRequestExport.annotate_and_retrieve_fields(all_requests, {}, additional_values)
requests_dict = convert_queryset_to_dict(annotated_requests, is_model=False)
DomainRequestExport.write_csv_for_requests(writer, columns, requests_dict)
# Reset the CSV file's position to the beginning
csv_file.seek(0)
# Read the content into a variable
csv_content = csv_file.read()
# We expect READY domains first, created between today-2 and today+2, sorted by created_at then name
# and DELETED domains deleted between today-2 and today+2, sorted by deleted then name
expected_content = ( expected_content = (
"Domain request,Domain type,Federal type\n" "Domain request,Domain type,Federal type\n"
"city3.gov,Federal,Executive\n" "city3.gov,Federal,Executive\n"
@ -705,70 +513,82 @@ class ExportDataTest(MockDb, MockEppLib):
self.assertEqual(csv_content, expected_content) self.assertEqual(csv_content, expected_content)
@less_console_noise_decorator @less_console_noise_decorator
def test_full_domain_request_report(self): def test_domain_request_data_full(self):
"""Tests the full domain request report.""" """Tests the full domain request report."""
# Remove "Submitted at" because we can't guess this immutable, dynamically generated test data
# Create a CSV file in memory columns = [
csv_file = StringIO() "Domain request",
writer = csv.writer(csv_file) # "Submitted at",
"Status",
# Call the report. Get existing fields from the report itself. "Domain type",
annotations = DomainRequestExport._full_domain_request_annotations() "Federal type",
additional_values = [ "Federal agency",
"requested_domain__name", "Organization name",
"federal_agency__agency", "Election office",
"authorizing_official__first_name", "City",
"authorizing_official__last_name", "State/territory",
"authorizing_official__email", "Region",
"authorizing_official__title", "Creator first name",
"creator__first_name", "Creator last name",
"creator__last_name", "Creator email",
"creator__email", "Creator approved domains count",
"investigator__email", "Creator active requests count",
"Alternative domains",
"AO first name",
"AO last name",
"AO email",
"AO title/role",
"Request purpose",
"Request additional details",
"Other contacts",
"CISA regional representative",
"Current websites",
"Investigator",
] ]
requests = DomainRequest.objects.exclude(status=DomainRequest.DomainRequestStatus.STARTED) with patch("registrar.utility.csv_export.DomainRequestDataFull.get_columns", return_value=columns):
annotated_requests = DomainRequestExport.annotate_and_retrieve_fields(requests, annotations, additional_values) # Create a CSV file in memory
requests_dict = convert_queryset_to_dict(annotated_requests, is_model=False) csv_file = StringIO()
DomainRequestExport.write_csv_for_requests(writer, DomainRequestExport.all_columns, requests_dict) # Call the export functions
DomainRequestDataFull.export_data_to_csv(csv_file)
# Reset the CSV file's position to the beginning # Reset the CSV file's position to the beginning
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()
print(csv_content) print(csv_content)
self.maxDiff = None expected_content = (
expected_content = ( # Header
# Header "Domain request,Status,Domain type,Federal type,"
"Domain request,Submitted at,Status,Domain type,Federal type," "Federal agency,Organization name,Election office,City,State/territory,"
"Federal agency,Organization name,Election office,City,State/territory," "Region,Creator first name,Creator last name,Creator email,Creator approved domains count,"
"Region,Creator first name,Creator last name,Creator email,Creator approved domains count," "Creator active requests count,Alternative domains,AO first name,AO last name,AO email,"
"Creator active requests count,Alternative domains,AO first name,AO last name,AO email," "AO title/role,Request purpose,Request additional details,Other contacts,"
"AO title/role,Request purpose,Request additional details,Other contacts," "CISA regional representative,Current websites,Investigator\n"
"CISA regional representative,Current websites,Investigator\n" # Content
# Content "city5.gov,,Approved,Federal,Executive,,Testorg,N/A,,NY,2,,,,1,0,city1.gov,Testy,Tester,testy@town.com,"
"city2.gov,,In review,Federal,Executive,,Testorg,N/A,,NY,2,,,,0,1,city1.gov,Testy,Tester,testy@town.com," "Chief Tester,Purpose of the site,There is more,Testy Tester testy2@town.com,,city.com,\n"
"Chief Tester,Purpose of the site,There is more,Testy Tester testy2@town.com,,city.com,\n" "city2.gov,,In review,Federal,Executive,,Testorg,N/A,,NY,2,,,,0,1,city1.gov,Testy,Tester,"
"city3.gov,2024-04-02,Submitted,Federal,Executive,,Testorg,N/A,,NY,2,,,,0,1," "testy@town.com,"
"cheeseville.gov | city1.gov | igorville.gov,Testy,Tester,testy@town.com,Chief Tester," "Chief Tester,Purpose of the site,There is more,Testy Tester testy2@town.com,,city.com,\n"
"Purpose of the site,CISA-first-name CISA-last-name | There is more,Meow Tester24 te2@town.com | " 'city3.gov,Submitted,Federal,Executive,,Testorg,N/A,,NY,2,,,,0,1,"cheeseville.gov, city1.gov,'
"Testy1232 Tester24 te2@town.com | Testy Tester testy2@town.com,test@igorville.com," 'igorville.gov",Testy,Tester,testy@town.com,Chief Tester,Purpose of the site,CISA-first-name '
"city.com | https://www.example2.com | https://www.example.com,\n" "CISA-last-name "
"city4.gov,2024-04-02,Submitted,City,Executive,,Testorg,Yes,,NY,2,,,,0,1,city1.gov,Testy,Tester," '| There is more,"Meow Tester24 te2@town.com, Testy1232 Tester24 te2@town.com, Testy Tester '
"testy@town.com,Chief Tester,Purpose of the site,CISA-first-name CISA-last-name | There is more," 'testy2@town.com"'
"Testy Tester testy2@town.com,cisaRep@igorville.gov,city.com,\n" ',test@igorville.com,"city.com, https://www.example2.com, https://www.example.com",\n'
"city5.gov,,Approved,Federal,Executive,,Testorg,N/A,,NY,2,,,,1,0,city1.gov,Testy,Tester,testy@town.com," "city4.gov,Submitted,City,Executive,,Testorg,Yes,,NY,2,,,,0,1,city1.gov,Testy,Tester,testy@town.com,"
"Chief Tester,Purpose of the site,There is more,Testy Tester testy2@town.com,,city.com,\n" "Chief Tester,Purpose of the site,CISA-first-name CISA-last-name | There is more,Testy Tester "
"city6.gov,2024-04-02,Submitted,Federal,Executive,,Testorg,N/A,,NY,2,,,,0,1,city1.gov,Testy,Tester," "testy2@town.com"
"testy@town.com,Chief Tester,Purpose of the site,CISA-first-name CISA-last-name | There is more," ",cisaRep@igorville.gov,city.com,\n"
"Testy Tester testy2@town.com,cisaRep@igorville.gov,city.com," "city6.gov,Submitted,Federal,Executive,,Testorg,N/A,,NY,2,,,,0,1,city1.gov,Testy,Tester,testy@town.com,"
) "Chief Tester,Purpose of the site,CISA-first-name CISA-last-name | There is more,Testy Tester "
"testy2@town.com,"
# Normalize line endings and remove commas, "cisaRep@igorville.gov,city.com,\n"
# spaces and leading/trailing whitespace )
csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip() # Normalize line endings and remove commas,
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip() # spaces and leading/trailing whitespace
csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip()
self.assertEqual(csv_content, expected_content) expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content)
class HelperFunctions(MockDb): class HelperFunctions(MockDb):
@ -794,12 +614,12 @@ class HelperFunctions(MockDb):
"domain__first_ready__lte": self.end_date, "domain__first_ready__lte": self.end_date,
} }
# Test with distinct # Test with distinct
managed_domains_sliced_at_end_date = get_sliced_domains(filter_condition) managed_domains_sliced_at_end_date = DomainExport.get_sliced_domains(filter_condition)
expected_content = [3, 2, 1, 0, 0, 0, 0, 0, 0, 0] expected_content = [3, 2, 1, 0, 0, 0, 0, 0, 0, 0]
self.assertEqual(managed_domains_sliced_at_end_date, expected_content) self.assertEqual(managed_domains_sliced_at_end_date, expected_content)
# Test without distinct # Test without distinct
managed_domains_sliced_at_end_date = get_sliced_domains(filter_condition) managed_domains_sliced_at_end_date = DomainExport.get_sliced_domains(filter_condition)
expected_content = [3, 2, 1, 0, 0, 0, 0, 0, 0, 0] expected_content = [3, 2, 1, 0, 0, 0, 0, 0, 0, 0]
self.assertEqual(managed_domains_sliced_at_end_date, expected_content) self.assertEqual(managed_domains_sliced_at_end_date, expected_content)
@ -811,6 +631,6 @@ class HelperFunctions(MockDb):
"status": DomainRequest.DomainRequestStatus.SUBMITTED, "status": DomainRequest.DomainRequestStatus.SUBMITTED,
"submission_date__lte": self.end_date, "submission_date__lte": self.end_date,
} }
submitted_requests_sliced_at_end_date = get_sliced_requests(filter_condition) submitted_requests_sliced_at_end_date = DomainRequestExport.get_sliced_requests(filter_condition)
expected_content = [3, 2, 0, 0, 0, 0, 1, 0, 0, 1] expected_content = [3, 2, 0, 0, 0, 0, 1, 0, 0, 1]
self.assertEqual(submitted_requests_sliced_at_end_date, expected_content) self.assertEqual(submitted_requests_sliced_at_end_date, expected_content)

View file

@ -15,12 +15,10 @@ from django.db.models import QuerySet, Value, CharField, Count, Q, F
from django.db.models import Case, When, DateField from django.db.models import Case, When, DateField
from django.db.models import ManyToManyField from django.db.models import ManyToManyField
from django.utils import timezone from django.utils import timezone
from django.core.paginator import Paginator
from django.db.models.functions import Concat, Coalesce from django.db.models.functions import Concat, Coalesce
from django.contrib.postgres.aggregates import StringAgg from django.contrib.postgres.aggregates import StringAgg
from registrar.models.utility.generic_helper import convert_queryset_to_dict from registrar.models.utility.generic_helper import convert_queryset_to_dict
from registrar.templatetags.custom_filters import get_region from registrar.templatetags.custom_filters import get_region
from registrar.utility.enums import DefaultEmail
from registrar.utility.constants import BranchChoices from registrar.utility.constants import BranchChoices
@ -34,14 +32,17 @@ def write_header(writer, columns):
""" """
writer.writerow(columns) writer.writerow(columns)
def get_default_start_date(): def get_default_start_date():
# Default to a date that's prior to our first deployment """Default to a date that's prior to our first deployment"""
return timezone.make_aware(datetime(2023, 11, 1)) return timezone.make_aware(datetime(2023, 11, 1))
def get_default_end_date(): def get_default_end_date():
# Default to now() """Default to now()"""
return timezone.now() return timezone.now()
def format_start_date(start_date): def format_start_date(start_date):
return timezone.make_aware(datetime.strptime(start_date, "%Y-%m-%d")) if start_date else get_default_start_date() return timezone.make_aware(datetime.strptime(start_date, "%Y-%m-%d")) if start_date else get_default_start_date()
@ -49,9 +50,11 @@ def format_start_date(start_date):
def format_end_date(end_date): def format_end_date(end_date):
return timezone.make_aware(datetime.strptime(end_date, "%Y-%m-%d")) if end_date else get_default_end_date() return timezone.make_aware(datetime.strptime(end_date, "%Y-%m-%d")) if end_date else get_default_end_date()
class BaseExport(ABC): class BaseExport(ABC):
""" """
A generic class for exporting data which returns a csv file for the given model. A generic class for exporting data which returns a csv file for the given model.
Base class in an inheritance tree of 3.
""" """
@classmethod @classmethod
@ -69,14 +72,14 @@ class BaseExport(ABC):
Returns the columns for CSV export. Override in subclasses as needed. Returns the columns for CSV export. Override in subclasses as needed.
""" """
return [] return []
@classmethod @classmethod
def get_sort_fields(cls): def get_sort_fields(cls):
""" """
Returns the sort fields for the CSV export. Override in subclasses as needed. Returns the sort fields for the CSV export. Override in subclasses as needed.
""" """
return [] return []
@classmethod @classmethod
def get_additional_args(cls): def get_additional_args(cls):
""" """
@ -84,63 +87,63 @@ class BaseExport(ABC):
Override in subclasses to provide specific arguments. Override in subclasses to provide specific arguments.
""" """
return {} return {}
@classmethod @classmethod
def get_select_related(cls): def get_select_related(cls):
""" """
Get a list of tables to pass to select_related when building queryset. Get a list of tables to pass to select_related when building queryset.
""" """
return [] return []
@classmethod @classmethod
def get_prefetch_related(cls): def get_prefetch_related(cls):
""" """
Get a list of tables to pass to prefetch_related when building queryset. Get a list of tables to pass to prefetch_related when building queryset.
""" """
return [] return []
@classmethod @classmethod
def get_exclusions(cls): def get_exclusions(cls):
""" """
Get a Q object of exclusion conditions to use when building queryset. Get a Q object of exclusion conditions to use when building queryset.
""" """
return Q() return Q()
@classmethod @classmethod
def get_filter_conditions(cls, start_date=None, end_date=None): def get_filter_conditions(cls, start_date=None, end_date=None):
""" """
Get a Q object of filter conditions to filter when building queryset. Get a Q object of filter conditions to filter when building queryset.
""" """
return Q() return Q()
@classmethod @classmethod
def get_computed_fields(cls): def get_computed_fields(cls):
""" """
Get a dict of computed fields. Get a dict of computed fields.
""" """
return {} return {}
@classmethod @classmethod
def get_annotations_for_sort(cls): def get_annotations_for_sort(cls):
""" """
Get a dict of annotations to make available for order_by clause. Get a dict of annotations to make available for order_by clause.
""" """
return {} return {}
@classmethod @classmethod
def get_related_table_fields(cls): def get_related_table_fields(cls):
""" """
Get a list of fields from related tables. Get a list of fields from related tables.
""" """
return [] return []
@classmethod @classmethod
def update_queryset(cls, queryset, **kwargs): def update_queryset(cls, queryset, **kwargs):
""" """
Returns an updated queryset. Override in subclass to update queryset. Returns an updated queryset. Override in subclass to update queryset.
""" """
return queryset return queryset
@classmethod @classmethod
def write_csv_before(cls, csv_writer, start_date=None, end_date=None): def write_csv_before(cls, csv_writer, start_date=None, end_date=None):
""" """
@ -187,7 +190,7 @@ class BaseExport(ABC):
queryset = initial_queryset.annotate(**computed_fields).values(*model_fields, *related_table_fields) queryset = initial_queryset.annotate(**computed_fields).values(*model_fields, *related_table_fields)
return cls.update_queryset(queryset, **kwargs) return cls.update_queryset(queryset, **kwargs)
@classmethod @classmethod
def export_data_to_csv(cls, csv_file, start_date=None, end_date=None): def export_data_to_csv(cls, csv_file, start_date=None, end_date=None):
""" """
@ -207,7 +210,8 @@ class BaseExport(ABC):
related_table_fields = cls.get_related_table_fields() related_table_fields = cls.get_related_table_fields()
model_queryset = ( model_queryset = (
cls.model().objects.select_related(*select_related) cls.model()
.objects.select_related(*select_related)
.prefetch_related(*prefetch_related) .prefetch_related(*prefetch_related)
.filter(filter_conditions) .filter(filter_conditions)
.exclude(exclusions) .exclude(exclusions)
@ -217,7 +221,9 @@ class BaseExport(ABC):
) )
# Convert the queryset to a dictionary (including annotated fields) # Convert the queryset to a dictionary (including annotated fields)
annotated_queryset = cls.annotate_and_retrieve_fields(model_queryset, computed_fields, related_table_fields, **kwargs) annotated_queryset = cls.annotate_and_retrieve_fields(
model_queryset, computed_fields, related_table_fields, **kwargs
)
models_dict = convert_queryset_to_dict(annotated_queryset, is_model=False) models_dict = convert_queryset_to_dict(annotated_queryset, is_model=False)
# Write to csv file before the write_csv # Write to csv file before the write_csv
@ -259,10 +265,11 @@ class BaseExport(ABC):
""" """
pass pass
class DomainExport(BaseExport): class DomainExport(BaseExport):
""" """
A collection of functions which return csv files regarding the Domain model. A collection of functions which return csv files regarding the Domain model.
Second class in an inheritance tree of 3.
""" """
@classmethod @classmethod
@ -279,9 +286,9 @@ class DomainExport(BaseExport):
based on public_contacts, domain_invitations and user_domain_roles based on public_contacts, domain_invitations and user_domain_roles
passed through kwargs. passed through kwargs.
""" """
public_contacts = kwargs.get('public_contacts', {}) public_contacts = kwargs.get("public_contacts", {})
domain_invitations = kwargs.get('domain_invitations', {}) domain_invitations = kwargs.get("domain_invitations", {})
user_domain_roles = kwargs.get('user_domain_roles', {}) user_domain_roles = kwargs.get("user_domain_roles", {})
annotated_domain_infos = [] annotated_domain_infos = []
@ -296,16 +303,18 @@ class DomainExport(BaseExport):
# Annotate with security_contact from public_contacts # Annotate with security_contact from public_contacts
for domain_info in queryset: for domain_info in queryset:
domain_info['security_contact_email'] = public_contacts.get(domain_info.get('domain__security_contact_registry_id')) domain_info["security_contact_email"] = public_contacts.get(
domain_info['invited_users'] = ', '.join(invited_users_dict.get(domain_info.get('domain__name'), [])) domain_info.get("domain__security_contact_registry_id")
domain_info['managers'] = ', '.join(managers_dict.get(domain_info.get('domain__name'), [])) )
domain_info["invited_users"] = ", ".join(invited_users_dict.get(domain_info.get("domain__name"), []))
domain_info["managers"] = ", ".join(managers_dict.get(domain_info.get("domain__name"), []))
annotated_domain_infos.append(domain_info) annotated_domain_infos.append(domain_info)
if annotated_domain_infos: if annotated_domain_infos:
return annotated_domain_infos return annotated_domain_infos
return queryset return queryset
# ============================================================= # # ============================================================= #
# Helper functions for django ORM queries. # # Helper functions for django ORM queries. #
# We are using these rather than pure python for speed reasons. # # We are using these rather than pure python for speed reasons. #
@ -316,15 +325,15 @@ class DomainExport(BaseExport):
""" """
Fetch all PublicContact entries and return a mapping of registry_id to email. Fetch all PublicContact entries and return a mapping of registry_id to email.
""" """
public_contacts = PublicContact.objects.values_list('registry_id', 'email') public_contacts = PublicContact.objects.values_list("registry_id", "email")
return {registry_id: email for registry_id, email in public_contacts} return {registry_id: email for registry_id, email in public_contacts}
@classmethod @classmethod
def get_all_domain_invitations(cls): def get_all_domain_invitations(cls):
""" """
Fetch all DomainInvitation entries and return a mapping of domain to email. Fetch all DomainInvitation entries and return a mapping of domain to email.
""" """
domain_invitations = DomainInvitation.objects.filter(status="invited").values_list('domain__name', 'email') domain_invitations = DomainInvitation.objects.filter(status="invited").values_list("domain__name", "email")
return list(domain_invitations) return list(domain_invitations)
@classmethod @classmethod
@ -332,7 +341,7 @@ class DomainExport(BaseExport):
""" """
Fetch all UserDomainRole entries and return a mapping of domain to user__email. Fetch all UserDomainRole entries and return a mapping of domain to user__email.
""" """
user_domain_roles = UserDomainRole.objects.select_related('user').values_list('domain__name', 'user__email') user_domain_roles = UserDomainRole.objects.select_related("user").values_list("domain__name", "user__email")
return list(user_domain_roles) return list(user_domain_roles)
@classmethod @classmethod
@ -360,19 +369,9 @@ class DomainExport(BaseExport):
if domain_federal_type and domain_org_type == DomainRequest.OrgChoicesElectionOffice.FEDERAL: if domain_federal_type and domain_org_type == DomainRequest.OrgChoicesElectionOffice.FEDERAL:
domain_type = f"{human_readable_domain_org_type} - {human_readable_domain_federal_type}" domain_type = f"{human_readable_domain_org_type} - {human_readable_domain_federal_type}"
if model.get("domain__name") == "18f.gov":
print(f'domain_type {domain_type}')
print(f'federal_agency {model.get("federal_agency")}')
print(f'city {model.get("city")}')
print(f'agency {model.get("agency")}')
print(f'federal_agency__agency {model.get("federal_agency__agency")}')
# create a dictionary of fields which can be included in output. # create a dictionary of fields which can be included in output.
# "extra_fields" are precomputed fields (generated in the DB or parsed). # "extra_fields" are precomputed fields (generated in the DB or parsed).
FIELDS = { FIELDS = {
"Domain name": model.get("domain__name"), "Domain name": model.get("domain__name"),
"Status": human_readable_status, "Status": human_readable_status,
"First ready on": first_ready_on, "First ready on": first_ready_on,
@ -434,6 +433,10 @@ class DomainExport(BaseExport):
class DomainDataType(DomainExport): class DomainDataType(DomainExport):
"""
Shows security contacts, domain managers, ao
Inherits from BaseExport -> DomainExport
"""
@classmethod @classmethod
def get_columns(cls): def get_columns(cls):
@ -456,7 +459,7 @@ class DomainDataType(DomainExport):
"Domain managers", "Domain managers",
"Invited domain managers", "Invited domain managers",
] ]
@classmethod @classmethod
def get_sort_fields(cls): def get_sort_fields(cls):
""" """
@ -488,29 +491,24 @@ class DomainDataType(DomainExport):
user_domain_roles = cls.get_all_user_domain_roles() user_domain_roles = cls.get_all_user_domain_roles()
return { return {
'public_contacts': public_contacts, "public_contacts": public_contacts,
'domain_invitations': domain_invitations, "domain_invitations": domain_invitations,
'user_domain_roles': user_domain_roles, "user_domain_roles": user_domain_roles,
} }
@classmethod @classmethod
def get_select_related(cls): def get_select_related(cls):
""" """
Get a list of tables to pass to select_related when building queryset. Get a list of tables to pass to select_related when building queryset.
""" """
return [ return ["domain", "authorizing_official"]
"domain",
"authorizing_official"
]
@classmethod @classmethod
def get_prefetch_related(cls): def get_prefetch_related(cls):
""" """
Get a list of tables to pass to prefetch_related when building queryset. Get a list of tables to pass to prefetch_related when building queryset.
""" """
return [ return ["permissions"]
"permissions"
]
@classmethod @classmethod
def get_computed_fields(cls, delimiter=", "): def get_computed_fields(cls, delimiter=", "):
@ -525,7 +523,7 @@ class DomainDataType(DomainExport):
output_field=CharField(), output_field=CharField(),
), ),
} }
@classmethod @classmethod
def get_related_table_fields(cls): def get_related_table_fields(cls):
""" """
@ -542,9 +540,13 @@ class DomainDataType(DomainExport):
"authorizing_official__email", "authorizing_official__email",
"federal_agency__agency", "federal_agency__agency",
] ]
class DomainDataFull(DomainExport): class DomainDataFull(DomainExport):
"""
Shows security contacts, filtered by state
Inherits from BaseExport -> DomainExport
"""
@classmethod @classmethod
def get_columns(cls): def get_columns(cls):
@ -560,7 +562,7 @@ class DomainDataFull(DomainExport):
"State", "State",
"Security contact email", "Security contact email",
] ]
@classmethod @classmethod
def get_sort_fields(cls): def get_sort_fields(cls):
""" """
@ -586,17 +588,15 @@ class DomainDataFull(DomainExport):
public_contacts = cls.get_all_security_emails() public_contacts = cls.get_all_security_emails()
return { return {
'public_contacts': public_contacts, "public_contacts": public_contacts,
} }
@classmethod @classmethod
def get_select_related(cls): def get_select_related(cls):
""" """
Get a list of tables to pass to select_related when building queryset. Get a list of tables to pass to select_related when building queryset.
""" """
return [ return ["domain"]
"domain"
]
@classmethod @classmethod
def get_filter_conditions(cls, start_date=None, end_date=None): def get_filter_conditions(cls, start_date=None, end_date=None):
@ -604,13 +604,13 @@ class DomainDataFull(DomainExport):
Get a Q object of filter conditions to filter when building queryset. Get a Q object of filter conditions to filter when building queryset.
""" """
return Q( return Q(
domain__state__in = [ domain__state__in=[
Domain.State.READY, Domain.State.READY,
Domain.State.DNS_NEEDED, Domain.State.DNS_NEEDED,
Domain.State.ON_HOLD, Domain.State.ON_HOLD,
], ],
) )
@classmethod @classmethod
def get_computed_fields(cls, delimiter=", "): def get_computed_fields(cls, delimiter=", "):
""" """
@ -624,7 +624,7 @@ class DomainDataFull(DomainExport):
output_field=CharField(), output_field=CharField(),
), ),
} }
@classmethod @classmethod
def get_related_table_fields(cls): def get_related_table_fields(cls):
""" """
@ -638,6 +638,10 @@ class DomainDataFull(DomainExport):
class DomainDataFederal(DomainExport): class DomainDataFederal(DomainExport):
"""
Shows security contacts, filtered by state and org type
Inherits from BaseExport -> DomainExport
"""
@classmethod @classmethod
def get_columns(cls): def get_columns(cls):
@ -653,7 +657,7 @@ class DomainDataFederal(DomainExport):
"State", "State",
"Security contact email", "Security contact email",
] ]
@classmethod @classmethod
def get_sort_fields(cls): def get_sort_fields(cls):
""" """
@ -679,17 +683,15 @@ class DomainDataFederal(DomainExport):
public_contacts = cls.get_all_security_emails() public_contacts = cls.get_all_security_emails()
return { return {
'public_contacts': public_contacts, "public_contacts": public_contacts,
} }
@classmethod @classmethod
def get_select_related(cls): def get_select_related(cls):
""" """
Get a list of tables to pass to select_related when building queryset. Get a list of tables to pass to select_related when building queryset.
""" """
return [ return ["domain"]
"domain"
]
@classmethod @classmethod
def get_filter_conditions(cls, start_date=None, end_date=None): def get_filter_conditions(cls, start_date=None, end_date=None):
@ -702,9 +704,9 @@ class DomainDataFederal(DomainExport):
Domain.State.READY, Domain.State.READY,
Domain.State.DNS_NEEDED, Domain.State.DNS_NEEDED,
Domain.State.ON_HOLD, Domain.State.ON_HOLD,
] ],
) )
@classmethod @classmethod
def get_computed_fields(cls, delimiter=", "): def get_computed_fields(cls, delimiter=", "):
""" """
@ -718,7 +720,7 @@ class DomainDataFederal(DomainExport):
output_field=CharField(), output_field=CharField(),
), ),
} }
@classmethod @classmethod
def get_related_table_fields(cls): def get_related_table_fields(cls):
""" """
@ -732,6 +734,10 @@ class DomainDataFederal(DomainExport):
class DomainGrowth(DomainExport): class DomainGrowth(DomainExport):
"""
Shows ready and deleted domains within a date range, sorted
Inherits from BaseExport -> DomainExport
"""
@classmethod @classmethod
def get_columns(cls): def get_columns(cls):
@ -751,7 +757,7 @@ class DomainGrowth(DomainExport):
"First ready", "First ready",
"Deleted", "Deleted",
] ]
@classmethod @classmethod
def get_annotations_for_sort(cls, delimiter=", "): def get_annotations_for_sort(cls, delimiter=", "):
""" """
@ -760,10 +766,10 @@ class DomainGrowth(DomainExport):
today = timezone.now().date() today = timezone.now().date()
return { return {
"custom_sort": Case( "custom_sort": Case(
When(domain__state=Domain.State.READY, then='domain__first_ready'), When(domain__state=Domain.State.READY, then="domain__first_ready"),
When(domain__state=Domain.State.DELETED, then='domain__deleted'), When(domain__state=Domain.State.DELETED, then="domain__deleted"),
default=Value(today), # Default value if no conditions match default=Value(today), # Default value if no conditions match
output_field=DateField() output_field=DateField(),
) )
} }
@ -773,19 +779,17 @@ class DomainGrowth(DomainExport):
Returns the sort fields. Returns the sort fields.
""" """
return [ return [
'-domain__state', "-domain__state",
'custom_sort', "custom_sort",
'domain__name', "domain__name",
] ]
@classmethod @classmethod
def get_select_related(cls): def get_select_related(cls):
""" """
Get a list of tables to pass to select_related when building queryset. Get a list of tables to pass to select_related when building queryset.
""" """
return [ return ["domain"]
"domain"
]
@classmethod @classmethod
def get_filter_conditions(cls, start_date=None, end_date=None): def get_filter_conditions(cls, start_date=None, end_date=None):
@ -795,15 +799,13 @@ class DomainGrowth(DomainExport):
filter_ready = Q( filter_ready = Q(
domain__state__in=[Domain.State.READY], domain__state__in=[Domain.State.READY],
domain__first_ready__gte=start_date, domain__first_ready__gte=start_date,
domain__first_ready__lte=end_date domain__first_ready__lte=end_date,
) )
filter_deleted = Q( filter_deleted = Q(
domain__state__in=[Domain.State.DELETED], domain__state__in=[Domain.State.DELETED], domain__deleted__gte=start_date, domain__deleted__lte=end_date
domain__deleted__gte=start_date,
domain__deleted__lte=end_date
) )
return filter_ready | filter_deleted return filter_ready | filter_deleted
@classmethod @classmethod
def get_related_table_fields(cls): def get_related_table_fields(cls):
""" """
@ -821,6 +823,10 @@ class DomainGrowth(DomainExport):
class DomainManaged(DomainExport): class DomainManaged(DomainExport):
"""
Shows managed domains by an end date, sorted
Inherits from BaseExport -> DomainExport
"""
@classmethod @classmethod
def get_columns(cls): def get_columns(cls):
@ -833,34 +839,30 @@ class DomainManaged(DomainExport):
"Domain managers", "Domain managers",
"Invited domain managers", "Invited domain managers",
] ]
@classmethod @classmethod
def get_sort_fields(cls): def get_sort_fields(cls):
""" """
Returns the sort fields. Returns the sort fields.
""" """
return [ return [
'domain__name', "domain__name",
] ]
@classmethod @classmethod
def get_select_related(cls): def get_select_related(cls):
""" """
Get a list of tables to pass to select_related when building queryset. Get a list of tables to pass to select_related when building queryset.
""" """
return [ return ["domain"]
"domain"
]
@classmethod @classmethod
def get_prefetch_related(cls): def get_prefetch_related(cls):
""" """
Get a list of tables to pass to prefetch_related when building queryset. Get a list of tables to pass to prefetch_related when building queryset.
""" """
return [ return ["permissions"]
"permissions"
]
@classmethod @classmethod
def get_filter_conditions(cls, start_date=None, end_date=None): def get_filter_conditions(cls, start_date=None, end_date=None):
""" """
@ -871,7 +873,6 @@ class DomainManaged(DomainExport):
domain__permissions__isnull=False, domain__permissions__isnull=False,
domain__first_ready__lte=end_date_formatted, domain__first_ready__lte=end_date_formatted,
) )
@classmethod @classmethod
def get_additional_args(cls): def get_additional_args(cls):
@ -889,10 +890,10 @@ class DomainManaged(DomainExport):
user_domain_roles = cls.get_all_user_domain_roles() user_domain_roles = cls.get_all_user_domain_roles()
return { return {
'domain_invitations': domain_invitations, "domain_invitations": domain_invitations,
'user_domain_roles': user_domain_roles, "user_domain_roles": user_domain_roles,
} }
@classmethod @classmethod
def get_related_table_fields(cls): def get_related_table_fields(cls):
""" """
@ -901,7 +902,7 @@ class DomainManaged(DomainExport):
return [ return [
"domain__name", "domain__name",
] ]
@classmethod @classmethod
def write_csv_before(cls, csv_writer, start_date=None, end_date=None): def write_csv_before(cls, csv_writer, start_date=None, end_date=None):
""" """
@ -959,6 +960,10 @@ class DomainManaged(DomainExport):
class DomainUnmanaged(DomainExport): class DomainUnmanaged(DomainExport):
"""
Shows unmanaged domains by an end date, sorted
Inherits from BaseExport -> DomainExport
"""
@classmethod @classmethod
def get_columns(cls): def get_columns(cls):
@ -969,34 +974,30 @@ class DomainUnmanaged(DomainExport):
"Domain name", "Domain name",
"Domain type", "Domain type",
] ]
@classmethod @classmethod
def get_sort_fields(cls): def get_sort_fields(cls):
""" """
Returns the sort fields. Returns the sort fields.
""" """
return [ return [
'domain__name', "domain__name",
] ]
@classmethod @classmethod
def get_select_related(cls): def get_select_related(cls):
""" """
Get a list of tables to pass to select_related when building queryset. Get a list of tables to pass to select_related when building queryset.
""" """
return [ return ["domain"]
"domain"
]
@classmethod @classmethod
def get_prefetch_related(cls): def get_prefetch_related(cls):
""" """
Get a list of tables to pass to prefetch_related when building queryset. Get a list of tables to pass to prefetch_related when building queryset.
""" """
return [ return ["permissions"]
"permissions"
]
@classmethod @classmethod
def get_filter_conditions(cls, start_date=None, end_date=None): def get_filter_conditions(cls, start_date=None, end_date=None):
""" """
@ -1007,7 +1008,7 @@ class DomainUnmanaged(DomainExport):
domain__permissions__isnull=True, domain__permissions__isnull=True,
domain__first_ready__lte=end_date_formatted, domain__first_ready__lte=end_date_formatted,
) )
@classmethod @classmethod
def get_related_table_fields(cls): def get_related_table_fields(cls):
""" """
@ -1016,12 +1017,12 @@ class DomainUnmanaged(DomainExport):
return [ return [
"domain__name", "domain__name",
] ]
@classmethod @classmethod
def write_csv_before(cls, csv_writer, start_date=None, end_date=None): def write_csv_before(cls, csv_writer, start_date=None, end_date=None):
""" """
Write to csv file before the write_csv method. Write to csv file before the write_csv method.
""" """
start_date_formatted = format_start_date(start_date) start_date_formatted = format_start_date(start_date)
end_date_formatted = format_end_date(end_date) end_date_formatted = format_end_date(end_date)
@ -1075,6 +1076,10 @@ class DomainUnmanaged(DomainExport):
class DomainRequestExport(BaseExport): class DomainRequestExport(BaseExport):
"""
A collection of functions which return csv files regarding the DomainRequest model.
Second class in an inheritance tree of 3.
"""
@classmethod @classmethod
def model(cls): def model(cls):
@ -1197,6 +1202,10 @@ class DomainRequestExport(BaseExport):
class DomainRequestGrowth(DomainRequestExport): class DomainRequestGrowth(DomainRequestExport):
"""
Shows submitted requests within a date range, sorted
Inherits from BaseExport -> DomainRequestExport
"""
@classmethod @classmethod
def get_columns(cls): def get_columns(cls):
@ -1218,7 +1227,7 @@ class DomainRequestGrowth(DomainRequestExport):
return [ return [
"requested_domain__name", "requested_domain__name",
] ]
@classmethod @classmethod
def get_filter_conditions(cls, start_date=None, end_date=None): def get_filter_conditions(cls, start_date=None, end_date=None):
""" """
@ -1238,12 +1247,14 @@ class DomainRequestGrowth(DomainRequestExport):
""" """
Get a list of fields from related tables. Get a list of fields from related tables.
""" """
return [ return ["requested_domain__name"]
"requested_domain__name"
]
class DomainRequestDataFull(DomainRequestExport): class DomainRequestDataFull(DomainRequestExport):
"""
Shows all but STARTED requests
Inherits from BaseExport -> DomainRequestExport
"""
@classmethod @classmethod
def get_columns(cls): def get_columns(cls):
@ -1285,34 +1296,22 @@ class DomainRequestDataFull(DomainRequestExport):
""" """
Get a list of tables to pass to select_related when building queryset. Get a list of tables to pass to select_related when building queryset.
""" """
return [ return ["creator", "authorizing_official", "federal_agency", "investigator", "requested_domain"]
"creator",
"authorizing_official",
"federal_agency",
"investigator",
"requested_domain"
]
@classmethod @classmethod
def get_prefetch_related(cls): def get_prefetch_related(cls):
""" """
Get a list of tables to pass to prefetch_related when building queryset. Get a list of tables to pass to prefetch_related when building queryset.
""" """
return [ return ["current_websites", "other_contacts", "alternative_domains"]
"current_websites",
"other_contacts",
"alternative_domains"
]
@classmethod @classmethod
def get_exclusions(cls): def get_exclusions(cls):
""" """
Get a Q object of exclusion conditions to use when building queryset. Get a Q object of exclusion conditions to use when building queryset.
""" """
return Q( return Q(status__in=[DomainRequest.DomainRequestStatus.STARTED])
status__in=[DomainRequest.DomainRequestStatus.STARTED]
)
@classmethod @classmethod
def get_sort_fields(cls): def get_sort_fields(cls):
""" """
@ -1322,7 +1321,7 @@ class DomainRequestDataFull(DomainRequestExport):
"status", "status",
"requested_domain__name", "requested_domain__name",
] ]
@classmethod @classmethod
def get_computed_fields(cls, delimiter=", "): def get_computed_fields(cls, delimiter=", "):
""" """
@ -1346,7 +1345,7 @@ class DomainRequestDataFull(DomainRequestExport):
distinct=True, distinct=True,
), ),
} }
@classmethod @classmethod
def get_related_table_fields(cls): def get_related_table_fields(cls):
""" """
@ -1364,7 +1363,6 @@ class DomainRequestDataFull(DomainRequestExport):
"creator__email", "creator__email",
"investigator__email", "investigator__email",
] ]
# ============================================================= # # ============================================================= #
# Helper functions for django ORM queries. # # Helper functions for django ORM queries. #

View file

@ -49,7 +49,9 @@ class AnalyticsView(View):
"domain__permissions__isnull": False, "domain__permissions__isnull": False,
"domain__first_ready__lte": end_date_formatted, "domain__first_ready__lte": end_date_formatted,
} }
managed_domains_sliced_at_start_date = csv_export.DomainExport.get_sliced_domains(filter_managed_domains_start_date) managed_domains_sliced_at_start_date = csv_export.DomainExport.get_sliced_domains(
filter_managed_domains_start_date
)
managed_domains_sliced_at_end_date = csv_export.DomainExport.get_sliced_domains(filter_managed_domains_end_date) managed_domains_sliced_at_end_date = csv_export.DomainExport.get_sliced_domains(filter_managed_domains_end_date)
filter_unmanaged_domains_start_date = { filter_unmanaged_domains_start_date = {
@ -60,8 +62,12 @@ class AnalyticsView(View):
"domain__permissions__isnull": True, "domain__permissions__isnull": True,
"domain__first_ready__lte": end_date_formatted, "domain__first_ready__lte": end_date_formatted,
} }
unmanaged_domains_sliced_at_start_date = csv_export.DomainExport.get_sliced_domains(filter_unmanaged_domains_start_date) unmanaged_domains_sliced_at_start_date = csv_export.DomainExport.get_sliced_domains(
unmanaged_domains_sliced_at_end_date = csv_export.DomainExport.get_sliced_domains(filter_unmanaged_domains_end_date) filter_unmanaged_domains_start_date
)
unmanaged_domains_sliced_at_end_date = csv_export.DomainExport.get_sliced_domains(
filter_unmanaged_domains_end_date
)
filter_ready_domains_start_date = { filter_ready_domains_start_date = {
"domain__state__in": [models.Domain.State.READY], "domain__state__in": [models.Domain.State.READY],
@ -82,7 +88,9 @@ class AnalyticsView(View):
"domain__state__in": [models.Domain.State.DELETED], "domain__state__in": [models.Domain.State.DELETED],
"domain__deleted__lte": end_date_formatted, "domain__deleted__lte": end_date_formatted,
} }
deleted_domains_sliced_at_start_date = csv_export.DomainExport.get_sliced_domains(filter_deleted_domains_start_date) deleted_domains_sliced_at_start_date = csv_export.DomainExport.get_sliced_domains(
filter_deleted_domains_start_date
)
deleted_domains_sliced_at_end_date = csv_export.DomainExport.get_sliced_domains(filter_deleted_domains_end_date) deleted_domains_sliced_at_end_date = csv_export.DomainExport.get_sliced_domains(filter_deleted_domains_end_date)
filter_requests_start_date = { filter_requests_start_date = {
@ -102,8 +110,12 @@ class AnalyticsView(View):
"status": models.DomainRequest.DomainRequestStatus.SUBMITTED, "status": models.DomainRequest.DomainRequestStatus.SUBMITTED,
"submission_date__lte": end_date_formatted, "submission_date__lte": end_date_formatted,
} }
submitted_requests_sliced_at_start_date = csv_export.DomainRequestExport.get_sliced_requests(filter_submitted_requests_start_date) submitted_requests_sliced_at_start_date = csv_export.DomainRequestExport.get_sliced_requests(
submitted_requests_sliced_at_end_date = csv_export.DomainRequestExport.get_sliced_requests(filter_submitted_requests_end_date) filter_submitted_requests_start_date
)
submitted_requests_sliced_at_end_date = csv_export.DomainRequestExport.get_sliced_requests(
filter_submitted_requests_end_date
)
context = dict( context = dict(
# Generate a dictionary of context variables that are common across all admin templates # Generate a dictionary of context variables that are common across all admin templates