This commit is contained in:
zandercymatics 2024-06-07 14:46:13 -06:00
parent 34a9497372
commit 4a14296e6d
No known key found for this signature in database
GPG key ID: FF4636ABEC9682B7

View file

@ -711,12 +711,12 @@ def export_data_unmanaged_domains_to_csv(csv_file, start_date, end_date):
class DomainRequestExport: class DomainRequestExport:
""" """
A collection of functions which return csv files regarding the DomainRequest model. A collection of functions which return csv files regarding the DomainRequest model.
Purely organizational -- all functions are independent.
""" """
# Get all the field names of the DomainRequest object # Get all the field names of the DomainRequest object
domain_request_fields = set(field.name for field in DomainRequest._meta.get_fields()) domain_request_fields = set(field.name for field in DomainRequest._meta.get_fields())
# Get all columns on the full metadata report
all_columns = [ all_columns = [
"Domain request", "Domain request",
"Submitted at", "Submitted at",
@ -763,6 +763,7 @@ class DomainRequestExport:
columns = [ columns = [
"Domain request", "Domain request",
"Domain type", "Domain type",
"Federal type",
"Submitted at", "Submitted at",
] ]
@ -775,15 +776,31 @@ class DomainRequestExport:
"submission_date__gte": start_date_formatted, "submission_date__gte": start_date_formatted,
} }
# We don't want to annotate anything, but we do want to access the requested domain name
annotations = {}
additional_values = ["requested_domain__name"]
all_requests = DomainRequest.objects.filter(**filter_condition).order_by(*sort_fields).distinct() all_requests = DomainRequest.objects.filter(**filter_condition).order_by(*sort_fields).distinct()
annotated_requests = cls.annotate_and_retrieve_fields(all_requests, {"requested_domain__name"}) annotated_requests = cls.annotate_and_retrieve_fields(all_requests, annotations, additional_values)
requests_dict = convert_queryset_to_dict(annotated_requests, is_model=False) requests_dict = convert_queryset_to_dict(annotated_requests, is_model=False)
cls.write_csv_for_requests(writer, columns, requests_dict) cls.write_csv_for_requests(writer, columns, requests_dict)
@classmethod @classmethod
def export_full_domain_request_report(cls, csv_file): def export_full_domain_request_report(cls, csv_file):
"""
Generates a detailed domain request report to a CSV file.
Retrieves and annotates DomainRequest objects, excluding 'STARTED' status,
with related data optimizations via select/prefetch and annotation.
Annotated with counts and aggregates of related entities.
Converts to dict and writes to CSV using predefined columns.
Parameters:
csv_file (file-like object): Target CSV file.
"""
writer = csv.writer(csv_file) writer = csv.writer(csv_file)
requests = ( requests = (
@ -792,17 +809,32 @@ class DomainRequestExport:
) )
.prefetch_related("current_websites", "other_contacts", "alternative_domains") .prefetch_related("current_websites", "other_contacts", "alternative_domains")
.exclude(status__in=[DomainRequest.DomainRequestStatus.STARTED]) .exclude(status__in=[DomainRequest.DomainRequestStatus.STARTED])
.order_by("status","requested_domain__name",) .order_by(
"status",
"requested_domain__name",
)
.distinct() .distinct()
) )
# Annotations are custom columns returned to the queryset (AKA: computed in the DB) # Annotations are custom columns returned to the queryset (AKA: computed in the DB)
delimiter = " | "
annotations = { annotations = {
"creator_approved_domains_count": DomainRequestExport.get_creator_approved_domains_count_query(), "creator_approved_domains_count": DomainRequestExport.get_creator_approved_domains_count_query(),
"creator_active_requests_count": DomainRequestExport.get_creator_active_requests_count_query(), "creator_active_requests_count": DomainRequestExport.get_creator_active_requests_count_query(),
"all_other_contacts": DomainRequestExport.get_all_other_contacts_query(), "all_current_websites": StringAgg("current_websites__website", delimiter=delimiter, distinct=True),
"all_current_websites": StringAgg("current_websites__website", delimiter=" | ", distinct=True), "all_alternative_domains": StringAgg("alternative_domains__website", delimiter=delimiter, distinct=True),
"all_alternative_domains": StringAgg("alternative_domains__website", delimiter=" | ", distinct=True), # Coerce the other contacts object to "{first_name} {last_name} {email}"
"all_other_contacts": StringAgg(
Concat(
"other_contacts__first_name",
Value(" "),
"other_contacts__last_name",
Value(" "),
"other_contacts__email",
),
delimiter=delimiter,
distinct=True,
),
} }
# .values can't go two levels deep (just returns the id) - so we have to include this. # .values can't go two levels deep (just returns the id) - so we have to include this.
@ -816,7 +848,7 @@ class DomainRequestExport:
"creator__first_name", "creator__first_name",
"creator__last_name", "creator__last_name",
"creator__email", "creator__email",
"investigator__email" "investigator__email",
] ]
# Convert the domain request queryset to a dictionary (including annotated fields) # Convert the domain request queryset to a dictionary (including annotated fields)
@ -851,27 +883,20 @@ class DomainRequestExport:
writer.writerows(rows) writer.writerows(rows)
@staticmethod @staticmethod
def parse_row_for_requests(columns, request, show_expanded_org_type=False): def parse_row_for_requests(columns, request):
""" """
Given a set of columns and a request dictionary, Given a set of columns and a request dictionary, generate a new row from cleaned column data.
generate a new row from cleaned column data
""" """
# FK fields
federal_agency = "federal_agency"
ao = "authorizing_official"
creator = "creator"
investigator = "investigator"
# Handle the federal_type field. Defaults to the wrong format. # Handle the federal_type field. Defaults to the wrong format.
federal_type = request.get("federal_type") federal_type = request.get("federal_type")
human_readable_federal_type = DomainRequest.BranchChoices.get_branch_label(federal_type) if federal_type else None human_readable_federal_type = (
DomainRequest.BranchChoices.get_branch_label(federal_type) if federal_type else None
)
# Handle the org_type field. Either "Tribal" or "Federal - Executive". # Handle the org_type field
org_type = request.get("organization_type") org_type = request.get("organization_type")
human_readable_org_type = DomainRequest.OrganizationChoices.get_org_label(org_type) if org_type else None human_readable_org_type = DomainRequest.OrganizationChoices.get_org_label(org_type) if org_type else None
if human_readable_org_type and human_readable_federal_type and show_expanded_org_type:
human_readable_org_type = f"{human_readable_org_type} - {human_readable_federal_type}"
# Handle the status field. Defaults to the wrong format. # Handle the status field. Defaults to the wrong format.
status = request.get("status") status = request.get("status")
@ -881,6 +906,7 @@ class DomainRequestExport:
state_territory = request.get("state_territory") state_territory = request.get("state_territory")
region = get_region(state_territory) if state_territory else None region = get_region(state_territory) if state_territory else None
# Handle the requested_domain field (add a default if None)
requested_domain = request.get("requested_domain__name") requested_domain = request.get("requested_domain__name")
requested_domain_name = requested_domain if requested_domain else "No requested domain" requested_domain_name = requested_domain if requested_domain else "No requested domain"
@ -889,8 +915,9 @@ class DomainRequestExport:
if request.get("is_election_board") is not None: if request.get("is_election_board") is not None:
human_readable_election_board = "Yes" if request.is_election_board else "No" human_readable_election_board = "Yes" if request.is_election_board else "No"
# Handle the additional details field. Pipe seperated # Handle the additional details field. Pipe sep.
details = [request.get("cisa_representative_email"), request.get("anything_else")] cisa_rep = request.get("cisa_representative_email")
details = [cisa_rep, request.get("anything_else")]
additional_details = " | ".join([field for field in details if field is not None]) additional_details = " | ".join([field for field in details if field is not None])
# create a dictionary of fields which can be included in output. # create a dictionary of fields which can be included in output.
@ -911,15 +938,15 @@ class DomainRequestExport:
"Other contacts": request.get("all_other_contacts"), "Other contacts": request.get("all_other_contacts"),
"Current websites": request.get("all_current_websites"), "Current websites": request.get("all_current_websites"),
# Untouched FK fields - passed into the request dict. # Untouched FK fields - passed into the request dict.
"Federal agency": request.get(f"{federal_agency}__agency"), "Federal agency": request.get("federal_agency__agency"),
"AO first name": request.get(f"{ao}__first_name"), "AO first name": request.get("authorizing_official__first_name"),
"AO last name": request.get(f"{ao}__last_name"), "AO last name": request.get("authorizing_official__last_name"),
"AO email": request.get(f"{ao}__email"), "AO email": request.get("authorizing_official__email"),
"AO title/role": request.get(f"{ao}__title"), "AO title/role": request.get("authorizing_official__title"),
"Creator first name": request.get(f"{creator}__first_name"), "Creator first name": request.get("creator__first_name"),
"Creator last name": request.get(f"{creator}__last_name"), "Creator last name": request.get("creator__last_name"),
"Creator email": request.get(f"{creator}__email"), "Creator email": request.get("creator__email"),
"Investigator": request.get(f"{investigator}__email"), "Investigator": request.get("investigator__email"),
# Untouched fields # Untouched fields
"Organization name": request.get("organization_name"), "Organization name": request.get("organization_name"),
"City": request.get("city"), "City": request.get("city"),
@ -966,26 +993,14 @@ class DomainRequestExport:
# We are using these rather than pure python for speed reasons. # # We are using these rather than pure python for speed reasons. #
# ============================================================= # # ============================================================= #
@staticmethod
def get_all_other_contacts_query(delimiter=" | "):
""" """
all_other_contacts_query = StringAgg(
Concat(
"other_contacts__first_name",
Value(" "),
"other_contacts__last_name",
Value(" "),
"other_contacts__email",
),
delimiter=delimiter,
distinct=True,
)
return all_other_contacts_query
@staticmethod @staticmethod
def get_creator_approved_domains_count_query(): def get_creator_approved_domains_count_query():
""" """ """
Generates a Count query for distinct approved domain requests per creator.
Returns:
Count: Aggregates distinct 'APPROVED' domain requests by creator.
"""
query = Count( query = Count(
"creator__domain_requests_created__id", "creator__domain_requests_created__id",
@ -996,7 +1011,12 @@ class DomainRequestExport:
@staticmethod @staticmethod
def get_creator_active_requests_count_query(): def get_creator_active_requests_count_query():
""" """ """
Generates a Count query for distinct approved domain requests per creator.
Returns:
Count: Aggregates distinct 'SUBMITTED', 'IN_REVIEW', and 'ACTION_NEEDED' domain requests by creator.
"""
query = Count( query = Count(
"creator__domain_requests_created__id", "creator__domain_requests_created__id",