This commit is contained in:
CocoByte 2024-11-24 23:42:42 -07:00
parent ca8cfda03d
commit 34ba850277
No known key found for this signature in database
GPG key ID: BBFAA2526384C97F
7 changed files with 263 additions and 189 deletions

View file

@ -1,11 +1,10 @@
import csv
from datetime import date from datetime import date
import logging import logging
import copy import copy
from django import forms from django import forms
from django.db.models import Value, CharField, Q from django.db.models import Value, CharField, Q
from django.db.models.functions import Concat, Coalesce from django.db.models.functions import Concat, Coalesce
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponseRedirect
from registrar.models.federal_agency import FederalAgency from registrar.models.federal_agency import FederalAgency
from registrar.utility.admin_helpers import ( from registrar.utility.admin_helpers import (
get_action_needed_reason_default_email, get_action_needed_reason_default_email,
@ -1457,53 +1456,51 @@ class DomainInformationResource(resources.ModelResource):
# Override exports for these columns in DomainInformation to use converted values. These values # Override exports for these columns in DomainInformation to use converted values. These values
# come from @Property functions, which are not automatically included in the export and which we # come from @Property functions, which are not automatically included in the export and which we
# want to use in place of the native fields. # want to use in place of the native fields.
organization_name = fields.Field(attribute='converted_organization_name', column_name='organization_name') organization_name = fields.Field(attribute="converted_organization_name", column_name="organization_name")
generic_org_type = fields.Field(attribute='converted_generic_org_type', column_name='generic_org_type') generic_org_type = fields.Field(attribute="converted_generic_org_type", column_name="generic_org_type")
federal_type = fields.Field(attribute='converted_federal_type', column_name='federal_type') federal_type = fields.Field(attribute="converted_federal_type", column_name="federal_type")
federal_agency = fields.Field(attribute='converted_federal_agency', column_name='federal_agency') federal_agency = fields.Field(attribute="converted_federal_agency", column_name="federal_agency")
senior_official = fields.Field(attribute='converted_senior_official', column_name='senior_official') senior_official = fields.Field(attribute="converted_senior_official", column_name="senior_official")
address_line1 = fields.Field(attribute='converted_address_line1', column_name='address_line1') address_line1 = fields.Field(attribute="converted_address_line1", column_name="address_line1")
address_line2 = fields.Field(attribute='converted_address_line2', column_name='address_line2') address_line2 = fields.Field(attribute="converted_address_line2", column_name="address_line2")
city = fields.Field(attribute='converted_city', column_name='city') city = fields.Field(attribute="converted_city", column_name="city")
state_territory = fields.Field(attribute='converted_state_territory', column_name='state_territory') state_territory = fields.Field(attribute="converted_state_territory", column_name="state_territory")
zipcode = fields.Field(attribute='converted_zipcode', column_name='zipcode') zipcode = fields.Field(attribute="converted_zipcode", column_name="zipcode")
urbanization = fields.Field(attribute='converted_urbanization', column_name='urbanization') urbanization = fields.Field(attribute="converted_urbanization", column_name="urbanization")
# Custom getters for the above columns that map to @property functions instead of fields # Custom getters for the above columns that map to @property functions instead of fields
def dehydrate_organization_name(self, obj): def dehydrate_organization_name(self, obj):
return obj.converted_organization_name return obj.converted_organization_name
def dehydrate_generic_org_type(self, obj): def dehydrate_generic_org_type(self, obj):
return obj.converted_generic_org_type return obj.converted_generic_org_type
def dehydrate_federal_type(self, obj): def dehydrate_federal_type(self, obj):
return obj.converted_federal_type return obj.converted_federal_type
def dehydrate_federal_agency(self, obj): def dehydrate_federal_agency(self, obj):
return obj.converted_federal_agency return obj.converted_federal_agency
def dehydrate_senior_official(self, obj): def dehydrate_senior_official(self, obj):
return obj.converted_senior_official return obj.converted_senior_official
def dehydrate_address_line1(self, obj): def dehydrate_address_line1(self, obj):
return obj.converted_address_line1 return obj.converted_address_line1
def dehydrate_address_line2(self, obj): def dehydrate_address_line2(self, obj):
return obj.converted_address_line2 return obj.converted_address_line2
def dehydrate_city(self, obj): def dehydrate_city(self, obj):
return obj.converted_city return obj.converted_city
def dehydrate_state_territory(self, obj): def dehydrate_state_territory(self, obj):
return obj.converted_state_territory return obj.converted_state_territory
def dehydrate_zipcode(self, obj): def dehydrate_zipcode(self, obj):
return obj.converted_zipcode return obj.converted_zipcode
def dehydrate_urbanization(self, obj): def dehydrate_urbanization(self, obj):
return obj.converted_urbanization return obj.converted_urbanization
class DomainInformationAdmin(ListHeaderAdmin, ImportExportModelAdmin): class DomainInformationAdmin(ListHeaderAdmin, ImportExportModelAdmin):
@ -1515,7 +1512,7 @@ class DomainInformationAdmin(ListHeaderAdmin, ImportExportModelAdmin):
organization in the Domain Information object.""" organization in the Domain Information object."""
title = "generic organization" title = "generic organization"
parameter_name = 'converted_generic_orgs' parameter_name = "converted_generic_orgs"
def lookups(self, request, model_admin): def lookups(self, request, model_admin):
converted_generic_orgs = set() converted_generic_orgs = set()
@ -1524,7 +1521,7 @@ class DomainInformationAdmin(ListHeaderAdmin, ImportExportModelAdmin):
converted_generic_org = domainInfo.converted_generic_org_type converted_generic_org = domainInfo.converted_generic_org_type
if converted_generic_org: if converted_generic_org:
converted_generic_orgs.add(converted_generic_org) converted_generic_orgs.add(converted_generic_org)
return sorted((org, org) for org in converted_generic_orgs) return sorted((org, org) for org in converted_generic_orgs)
# Filter queryset # Filter queryset
@ -1533,7 +1530,10 @@ class DomainInformationAdmin(ListHeaderAdmin, ImportExportModelAdmin):
return queryset.filter( return queryset.filter(
# Filter based on the generic org value returned by converted_generic_org_type # Filter based on the generic org value returned by converted_generic_org_type
id__in=[ id__in=[
domainInfo.id for domainInfo in queryset if domainInfo.converted_generic_org_type and domainInfo.converted_generic_org_type == self.value() domainInfo.id
for domainInfo in queryset
if domainInfo.converted_generic_org_type
and domainInfo.converted_generic_org_type == self.value()
] ]
) )
return queryset return queryset
@ -1709,50 +1709,50 @@ class DomainRequestResource(FsmModelResource):
# Override exports for these columns in DomainInformation to use converted values. These values # Override exports for these columns in DomainInformation to use converted values. These values
# come from @Property functions, which are not automatically included in the export and which we # come from @Property functions, which are not automatically included in the export and which we
# want to use in place of the native fields. # want to use in place of the native fields.
organization_name = fields.Field(attribute='converted_organization_name', column_name='organization_name') organization_name = fields.Field(attribute="converted_organization_name", column_name="organization_name")
generic_org_type = fields.Field(attribute='converted_generic_org_type', column_name='generic_org_type') generic_org_type = fields.Field(attribute="converted_generic_org_type", column_name="generic_org_type")
federal_type = fields.Field(attribute='converted_federal_type', column_name='federal_type') federal_type = fields.Field(attribute="converted_federal_type", column_name="federal_type")
federal_agency = fields.Field(attribute='converted_federal_agency', column_name='federal_agency') federal_agency = fields.Field(attribute="converted_federal_agency", column_name="federal_agency")
senior_official = fields.Field(attribute='converted_senior_official', column_name='senior_official') senior_official = fields.Field(attribute="converted_senior_official", column_name="senior_official")
address_line1 = fields.Field(attribute='converted_address_line1', column_name='address_line1') address_line1 = fields.Field(attribute="converted_address_line1", column_name="address_line1")
address_line2 = fields.Field(attribute='converted_address_line2', column_name='address_line2') address_line2 = fields.Field(attribute="converted_address_line2", column_name="address_line2")
city = fields.Field(attribute='converted_city', column_name='city') city = fields.Field(attribute="converted_city", column_name="city")
state_territory = fields.Field(attribute='converted_state_territory', column_name='state_territory') state_territory = fields.Field(attribute="converted_state_territory", column_name="state_territory")
zipcode = fields.Field(attribute='converted_zipcode', column_name='zipcode') zipcode = fields.Field(attribute="converted_zipcode", column_name="zipcode")
urbanization = fields.Field(attribute='converted_urbanization', column_name='urbanization') urbanization = fields.Field(attribute="converted_urbanization", column_name="urbanization")
senior_official = fields.Field(attribute='converted_urbanization', column_name='senior official') senior_official = fields.Field(attribute="converted_urbanization", column_name="senior official")
# Custom getters for the above columns that map to @property functions instead of fields # Custom getters for the above columns that map to @property functions instead of fields
def dehydrate_organization_name(self, obj): def dehydrate_organization_name(self, obj):
return obj.converted_organization_name return obj.converted_organization_name
def dehydrate_generic_org_type(self, obj): def dehydrate_generic_org_type(self, obj):
return obj.converted_generic_org_type return obj.converted_generic_org_type
def dehydrate_federal_type(self, obj): def dehydrate_federal_type(self, obj):
return obj.converted_federal_type return obj.converted_federal_type
def dehydrate_federal_agency(self, obj): def dehydrate_federal_agency(self, obj):
return obj.converted_federal_agency return obj.converted_federal_agency
def dehydrate_senior_official(self, obj): def dehydrate_senior_official(self, obj):
return obj.converted_senior_official return obj.converted_senior_official
def dehydrate_address_line1(self, obj): def dehydrate_address_line1(self, obj):
return obj.converted_address_line1 return obj.converted_address_line1
def dehydrate_address_line2(self, obj): def dehydrate_address_line2(self, obj):
return obj.converted_address_line2 return obj.converted_address_line2
def dehydrate_city(self, obj): def dehydrate_city(self, obj):
return obj.converted_city return obj.converted_city
def dehydrate_state_territory(self, obj): def dehydrate_state_territory(self, obj):
return obj.converted_state_territory return obj.converted_state_territory
def dehydrate_zipcode(self, obj): def dehydrate_zipcode(self, obj):
return obj.converted_zipcode return obj.converted_zipcode
def dehydrate_urbanization(self, obj): def dehydrate_urbanization(self, obj):
return obj.converted_urbanization return obj.converted_urbanization
@ -2679,7 +2679,7 @@ class DomainResource(FsmModelResource):
class Meta: class Meta:
model = models.Domain model = models.Domain
#Override the default export so that it matches what is displayed in the admin table for Domains # Override the default export so that it matches what is displayed in the admin table for Domains
fields = ( fields = (
"name", "name",
"converted_generic_org_type", "converted_generic_org_type",
@ -2698,27 +2698,28 @@ class DomainResource(FsmModelResource):
) )
# Custom getters to retrieve the values from @Proprerty methods in DomainInfo # Custom getters to retrieve the values from @Proprerty methods in DomainInfo
converted_generic_org_type = fields.Field(attribute='converted_generic_org_type', column_name='generic org type') converted_generic_org_type = fields.Field(attribute="converted_generic_org_type", column_name="generic org type")
converted_federal_agency = fields.Field(attribute='converted_federal_agency', column_name='federal agency') converted_federal_agency = fields.Field(attribute="converted_federal_agency", column_name="federal agency")
converted_organization_name = fields.Field(attribute='converted_organization_name', column_name='organization name') converted_organization_name = fields.Field(attribute="converted_organization_name", column_name="organization name")
converted_city = fields.Field(attribute='converted_city', column_name='city') converted_city = fields.Field(attribute="converted_city", column_name="city")
converted_state_territory = fields.Field(attribute='converted_state_territory', column_name='state territory') converted_state_territory = fields.Field(attribute="converted_state_territory", column_name="state territory")
def dehydrate_converted_generic_org_type(self, obj): def dehydrate_converted_generic_org_type(self, obj):
return obj.domain_info.converted_generic_org_type return obj.domain_info.converted_generic_org_type
def dehydrate_converted_federal_agency(self, obj): def dehydrate_converted_federal_agency(self, obj):
return obj.domain_info.converted_federal_agency return obj.domain_info.converted_federal_agency
def dehydrate_converted_organization_name(self, obj): def dehydrate_converted_organization_name(self, obj):
return obj.domain_info.converted_organization_name return obj.domain_info.converted_organization_name
def dehydrate_converted_city(self, obj): def dehydrate_converted_city(self, obj):
return obj.domain_info.converted_city return obj.domain_info.converted_city
def dehydrate_converted_state_territory(self, obj): def dehydrate_converted_state_territory(self, obj):
return obj.domain_info.converted_state_territory return obj.domain_info.converted_state_territory
class DomainAdmin(ListHeaderAdmin, ImportExportModelAdmin): class DomainAdmin(ListHeaderAdmin, ImportExportModelAdmin):
"""Custom domain admin class to add extra buttons.""" """Custom domain admin class to add extra buttons."""
@ -2742,14 +2743,14 @@ class DomainAdmin(ListHeaderAdmin, ImportExportModelAdmin):
return queryset.filter(domain_info__is_election_board=True) return queryset.filter(domain_info__is_election_board=True)
if self.value() == "0": if self.value() == "0":
return queryset.filter(Q(domain_info__is_election_board=False) | Q(domain_info__is_election_board=None)) return queryset.filter(Q(domain_info__is_election_board=False) | Q(domain_info__is_election_board=None))
class GenericOrgFilter(admin.SimpleListFilter): class GenericOrgFilter(admin.SimpleListFilter):
"""Custom Generic Organization filter that accomodates portfolio feature. """Custom Generic Organization filter that accomodates portfolio feature.
If we have a portfolio, use the portfolio's organization. If not, use the If we have a portfolio, use the portfolio's organization. If not, use the
organization in the Domain Information object.""" organization in the Domain Information object."""
title = "generic organization" title = "generic organization"
parameter_name = 'converted_generic_orgs' parameter_name = "converted_generic_orgs"
def lookups(self, request, model_admin): def lookups(self, request, model_admin):
converted_generic_orgs = set() converted_generic_orgs = set()
@ -2758,7 +2759,7 @@ class DomainAdmin(ListHeaderAdmin, ImportExportModelAdmin):
converted_generic_org = domainInfo.converted_generic_org_type converted_generic_org = domainInfo.converted_generic_org_type
if converted_generic_org: if converted_generic_org:
converted_generic_orgs.add(converted_generic_org) converted_generic_orgs.add(converted_generic_org)
return sorted((org, org) for org in converted_generic_orgs) return sorted((org, org) for org in converted_generic_orgs)
# Filter queryset # Filter queryset
@ -2767,18 +2768,21 @@ class DomainAdmin(ListHeaderAdmin, ImportExportModelAdmin):
return queryset.filter( return queryset.filter(
# Filter based on the generic org value returned by converted_generic_org_type # Filter based on the generic org value returned by converted_generic_org_type
id__in=[ id__in=[
domain.id for domain in queryset if domain.domain_info.converted_generic_org_type and domain.domain_info.converted_generic_org_type == self.value() domain.id
for domain in queryset
if domain.domain_info.converted_generic_org_type
and domain.domain_info.converted_generic_org_type == self.value()
] ]
) )
return queryset return queryset
class FederalTypeFilter(admin.SimpleListFilter): class FederalTypeFilter(admin.SimpleListFilter):
"""Custom Federal Type filter that accomodates portfolio feature. """Custom Federal Type filter that accomodates portfolio feature.
If we have a portfolio, use the portfolio's federal type. If not, use the If we have a portfolio, use the portfolio's federal type. If not, use the
federal type in the Domain Information object.""" federal type in the Domain Information object."""
title = "federal type" title = "federal type"
parameter_name = 'converted_federal_types' parameter_name = "converted_federal_types"
def lookups(self, request, model_admin): def lookups(self, request, model_admin):
converted_federal_types = set() converted_federal_types = set()
@ -2788,7 +2792,7 @@ class DomainAdmin(ListHeaderAdmin, ImportExportModelAdmin):
converted_federal_type = domainInfo.converted_federal_type converted_federal_type = domainInfo.converted_federal_type
if converted_federal_type: if converted_federal_type:
converted_federal_types.add(converted_federal_type) converted_federal_types.add(converted_federal_type)
return sorted((fed, fed) for fed in converted_federal_types) return sorted((fed, fed) for fed in converted_federal_types)
# Filter queryset # Filter queryset
@ -2797,12 +2801,14 @@ class DomainAdmin(ListHeaderAdmin, ImportExportModelAdmin):
return queryset.filter( return queryset.filter(
# Filter based on the generic org value returned by converted_generic_org_type # Filter based on the generic org value returned by converted_generic_org_type
id__in=[ id__in=[
domain.id for domain in queryset if domain.domain_info.converted_federal_type and domain.domain_info.converted_federal_type == self.value() domain.id
for domain in queryset
if domain.domain_info.converted_federal_type
and domain.domain_info.converted_federal_type == self.value()
] ]
) )
return queryset return queryset
def get_queryset(self, request): def get_queryset(self, request):
"""Custom get_queryset to filter by portfolio if portfolio is in the """Custom get_queryset to filter by portfolio if portfolio is in the
request params.""" request params."""
@ -2813,12 +2819,12 @@ class DomainAdmin(ListHeaderAdmin, ImportExportModelAdmin):
# Further filter the queryset by the portfolio # Further filter the queryset by the portfolio
qs = qs.filter(domain_info__portfolio=portfolio_id) qs = qs.filter(domain_info__portfolio=portfolio_id)
return qs return qs
# Filters # Filters
list_filter = [GenericOrgFilter, FederalTypeFilter, ElectionOfficeFilter, "state"] list_filter = [GenericOrgFilter, FederalTypeFilter, ElectionOfficeFilter, "state"]
# ------- END FILTERS # ------- END FILTERS
# Inlines # Inlines
inlines = [DomainInformationInline] inlines = [DomainInformationInline]
@ -2853,8 +2859,9 @@ class DomainAdmin(ListHeaderAdmin, ImportExportModelAdmin):
@admin.display(description=_("Generic Org Type")) @admin.display(description=_("Generic Org Type"))
def converted_generic_org_type(self, obj): def converted_generic_org_type(self, obj):
return obj.domain_info.converted_generic_org_type return obj.domain_info.converted_generic_org_type
converted_generic_org_type.admin_order_field = "domain_info__converted_generic_org_type" # type: ignore converted_generic_org_type.admin_order_field = "domain_info__converted_generic_org_type" # type: ignore
# Use native value for the change form # Use native value for the change form
def generic_org_type(self, obj): def generic_org_type(self, obj):
return obj.domain_info.get_generic_org_type_display() return obj.domain_info.get_generic_org_type_display()
@ -2863,6 +2870,7 @@ class DomainAdmin(ListHeaderAdmin, ImportExportModelAdmin):
@admin.display(description=_("Federal Agency")) @admin.display(description=_("Federal Agency"))
def converted_federal_agency(self, obj): def converted_federal_agency(self, obj):
return obj.domain_info.converted_federal_agency return obj.domain_info.converted_federal_agency
converted_federal_agency.admin_order_field = "domain_info__converted_federal_agency" # type: ignore converted_federal_agency.admin_order_field = "domain_info__converted_federal_agency" # type: ignore
# Use native value for the change form # Use native value for the change form
@ -2877,6 +2885,7 @@ class DomainAdmin(ListHeaderAdmin, ImportExportModelAdmin):
@admin.display(description=_("Federal Type")) @admin.display(description=_("Federal Type"))
def converted_federal_type(self, obj): def converted_federal_type(self, obj):
return obj.domain_info.converted_federal_type return obj.domain_info.converted_federal_type
converted_federal_type.admin_order_field = "domain_info__converted_federal_type" # type: ignore converted_federal_type.admin_order_field = "domain_info__converted_federal_type" # type: ignore
# Use native value for the change form # Use native value for the change form
@ -2888,6 +2897,7 @@ class DomainAdmin(ListHeaderAdmin, ImportExportModelAdmin):
@admin.display(description=_("Organization Name")) @admin.display(description=_("Organization Name"))
def converted_organization_name(self, obj): def converted_organization_name(self, obj):
return obj.domain_info.converted_organization_name return obj.domain_info.converted_organization_name
converted_organization_name.admin_order_field = "domain_info__converted_organization_name" # type: ignore converted_organization_name.admin_order_field = "domain_info__converted_organization_name" # type: ignore
# Use native value for the change form # Use native value for the change form
@ -2899,6 +2909,7 @@ class DomainAdmin(ListHeaderAdmin, ImportExportModelAdmin):
@admin.display(description=_("City")) @admin.display(description=_("City"))
def converted_city(self, obj): def converted_city(self, obj):
return obj.domain_info.converted_city return obj.domain_info.converted_city
converted_city.admin_order_field = "domain_info__converted_city" # type: ignore converted_city.admin_order_field = "domain_info__converted_city" # type: ignore
# Use native value for the change form # Use native value for the change form
@ -2910,13 +2921,13 @@ class DomainAdmin(ListHeaderAdmin, ImportExportModelAdmin):
@admin.display(description=_("State / territory")) @admin.display(description=_("State / territory"))
def converted_state_territory(self, obj): def converted_state_territory(self, obj):
return obj.domain_info.converted_state_territory return obj.domain_info.converted_state_territory
converted_state_territory.admin_order_field = "domain_info__converted_state_territory" # type: ignore converted_state_territory.admin_order_field = "domain_info__converted_state_territory" # type: ignore
# Use native value for the change form # Use native value for the change form
def state_territory(self, obj): def state_territory(self, obj):
return obj.domain_info.state_territory if obj.domain_info else None return obj.domain_info.state_territory if obj.domain_info else None
def dnssecdata(self, obj): def dnssecdata(self, obj):
return "Yes" if obj.dnssecdata else "No" return "Yes" if obj.dnssecdata else "No"
@ -2948,7 +2959,6 @@ class DomainAdmin(ListHeaderAdmin, ImportExportModelAdmin):
custom_election_board.admin_order_field = "domain_info__is_election_board" # type: ignore custom_election_board.admin_order_field = "domain_info__is_election_board" # type: ignore
custom_election_board.short_description = "Election office" # type: ignore custom_election_board.short_description = "Election office" # type: ignore
# Search # Search
search_fields = ["name"] search_fields = ["name"]
search_help_text = "Search by domain name." search_help_text = "Search by domain name."

View file

@ -2078,4 +2078,4 @@ class Domain(TimeStampedModel, DomainHelper):
if property in self._cache: if property in self._cache:
return self._cache[property] return self._cache[property]
else: else:
raise KeyError("Requested key %s was not found in registry cache." % str(property)) raise KeyError("Requested key %s was not found in registry cache." % str(property))

View file

@ -475,7 +475,7 @@ class DomainInformation(TimeStampedModel):
if self.portfolio: if self.portfolio:
return self.portfolio.city return self.portfolio.city
return self.city return self.city
@property @property
def converted_state_territory(self): def converted_state_territory(self):
if self.portfolio: if self.portfolio:
@ -493,5 +493,3 @@ class DomainInformation(TimeStampedModel):
if self.portfolio: if self.portfolio:
return self.portfolio.urbanization return self.portfolio.urbanization
return self.urbanization return self.urbanization

View file

@ -1460,7 +1460,7 @@ class DomainRequest(TimeStampedModel):
if self.portfolio: if self.portfolio:
return self.portfolio.state_territory return self.portfolio.state_territory
return self.state_territory return self.state_territory
@property @property
def converted_urbanization(self): def converted_urbanization(self):
if self.portfolio: if self.portfolio:
@ -1477,4 +1477,4 @@ class DomainRequest(TimeStampedModel):
def converted_senior_official(self): def converted_senior_official(self):
if self.portfolio: if self.portfolio:
return self.portfolio.senior_official return self.portfolio.senior_official
return self.senior_official return self.senior_official

View file

@ -63,10 +63,10 @@ class CsvReportsTest(MockDbForSharedTests):
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,183,,,,(blank)\r\n'), call("cdomain11.gov,Federal - Executive,183,,,,(blank)\r\n"),
call('cdomain1.gov,Federal - Executive,183,,,,(blank)\r\n'), call("cdomain1.gov,Federal - Executive,183,,,,(blank)\r\n"),
call('adomain10.gov,Federal,8,,,,(blank)\r\n'), call("adomain10.gov,Federal,8,,,,(blank)\r\n"),
call('ddomain3.gov,Federal,8,,,,(blank)\r\n'), call("ddomain3.gov,Federal,8,,,,(blank)\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.
@ -85,11 +85,11 @@ class CsvReportsTest(MockDbForSharedTests):
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,183,,,,(blank)\r\n'), call("cdomain11.gov,Federal - Executive,183,,,,(blank)\r\n"),
call('cdomain1.gov,Federal - Executive,183,,,,(blank)\r\n'), call("cdomain1.gov,Federal - Executive,183,,,,(blank)\r\n"),
call('adomain10.gov,Federal,8,,,,(blank)\r\n'), call("adomain10.gov,Federal,8,,,,(blank)\r\n"),
call('ddomain3.gov,Federal,8,,,,(blank)\r\n'), call("ddomain3.gov,Federal,8,,,,(blank)\r\n"),
call('zdomain12.gov,Interstate,,,,,(blank)\r\n'), call("zdomain12.gov,Interstate,,,,,(blank)\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.
@ -247,7 +247,7 @@ class ExportDataTest(MockDbForIndividualTests, MockEppLib):
"SO email,Security contact email,Domain managers,Invited domain managers\n" "SO email,Security contact email,Domain managers,Invited domain managers\n"
"cdomain11.gov,Ready,2024-04-02,(blank),Federal - Executive,188,,,, ,,(blank),meoward@rocks.com,\n" "cdomain11.gov,Ready,2024-04-02,(blank),Federal - Executive,188,,,, ,,(blank),meoward@rocks.com,\n"
"defaultsecurity.gov,Ready,2023-11-01,(blank),Federal - Executive,188,,,, ,,(blank)," "defaultsecurity.gov,Ready,2023-11-01,(blank),Federal - Executive,188,,,, ,,(blank),"
"\"big_lebowski@dude.co, info@example.com, meoward@rocks.com\",woofwardthethird@rocks.com\n" '"big_lebowski@dude.co, info@example.com, meoward@rocks.com",woofwardthethird@rocks.com\n'
"adomain10.gov,Ready,2024-04-03,(blank),Federal,189,,,, ,,(blank),,squeaker@rocks.com\n" "adomain10.gov,Ready,2024-04-03,(blank),Federal,189,,,, ,,(blank),,squeaker@rocks.com\n"
"bdomain4.gov,Unknown,(blank),(blank),Federal,189,,,, ,,(blank),,\n" "bdomain4.gov,Unknown,(blank),(blank),Federal,189,,,, ,,(blank),,\n"
"bdomain5.gov,Deleted,(blank),(blank),Federal,189,,,, ,,(blank),,\n" "bdomain5.gov,Deleted,(blank),(blank),Federal,189,,,, ,,(blank),,\n"
@ -303,9 +303,9 @@ class ExportDataTest(MockDbForIndividualTests, MockEppLib):
"City,State,SO,SO email," "City,State,SO,SO email,"
"Security contact email,Domain managers,Invited domain managers\n" "Security contact email,Domain managers,Invited domain managers\n"
"defaultsecurity.gov,Ready,2023-11-01,(blank),Federal - Executive,190,,,, ,,(blank)," "defaultsecurity.gov,Ready,2023-11-01,(blank),Federal - Executive,190,,,, ,,(blank),"
"\"big_lebowski@dude.co, info@example.com, meoward@rocks.com\",woofwardthethird@rocks.com\n" '"big_lebowski@dude.co, info@example.com, meoward@rocks.com",woofwardthethird@rocks.com\n'
"adomain2.gov,Dns needed,(blank),(blank),Interstate,,,,, ,,(blank)," "adomain2.gov,Dns needed,(blank),(blank),Interstate,,,,, ,,(blank),"
"\"info@example.com, meoward@rocks.com\",squeaker@rocks.com\n" '"info@example.com, meoward@rocks.com",squeaker@rocks.com\n'
) )
# Normalize line endings and remove commas, # Normalize line endings and remove commas,
@ -313,8 +313,6 @@ class ExportDataTest(MockDbForIndividualTests, MockEppLib):
csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip() csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip()
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip() expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.maxDiff = None self.maxDiff = None
self.assertEqual(csv_content, expected_content) self.assertEqual(csv_content, expected_content)

View file

@ -11,11 +11,21 @@ from registrar.models import (
PublicContact, PublicContact,
UserDomainRole, UserDomainRole,
) )
from django.db.models import Case, CharField, Count, DateField, F, ManyToManyField, Q, QuerySet, Value, When, ExpressionWrapper from django.db.models import (
Case,
CharField,
Count,
DateField,
F,
ManyToManyField,
Q,
QuerySet,
Value,
When,
)
from django.utils import timezone from django.utils import timezone
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.user import User
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.constants import BranchChoices from registrar.utility.constants import BranchChoices
@ -286,67 +296,67 @@ class DomainExport(BaseExport):
def model(cls): def model(cls):
# Return the model class that this export handles # Return the model class that this export handles
return DomainInformation return DomainInformation
@classmethod @classmethod
def get_computed_fields(cls, delimiter=", "): def get_computed_fields(cls, delimiter=", "):
""" """
Get a dict of computed fields. Get a dict of computed fields.
""" """
# NOTE: These computed fields imitate @Property functions in the Domain model where needed. # NOTE: These computed fields imitate @Property functions in the Domain model where needed.
# This is for performance purposes. Since we are working with dictionary values and not # This is for performance purposes. Since we are working with dictionary values and not
# model objects as we export data, trying to reinstate model objects in order to grab @property # model objects as we export data, trying to reinstate model objects in order to grab @property
# values negatively impacts performance. Therefore, we will follow best practice and use annotations # values negatively impacts performance. Therefore, we will follow best practice and use annotations
return { return {
"converted_federal_agency" : Case( "converted_federal_agency": Case(
# When portfolio is present, use its value instead # When portfolio is present, use its value instead
When(portfolio__isnull=False, then=F("portfolio__federal_agency")), When(portfolio__isnull=False, then=F("portfolio__federal_agency")),
# Otherwise, return the natively assigned value # Otherwise, return the natively assigned value
default=F("federal_agency"), default=F("federal_agency"),
output_field=CharField(), output_field=CharField(),
), ),
"converted_organization_name" : Case( "converted_organization_name": Case(
# When portfolio is present, use its value instead # When portfolio is present, use its value instead
When(portfolio__isnull=False, then=F("portfolio__organization_name")), When(portfolio__isnull=False, then=F("portfolio__organization_name")),
# Otherwise, return the natively assigned value # Otherwise, return the natively assigned value
default=F("organization_name"), default=F("organization_name"),
output_field=CharField(), output_field=CharField(),
), ),
"converted_city" : Case( "converted_city": Case(
# When portfolio is present, use its value instead # When portfolio is present, use its value instead
When(portfolio__isnull=False, then=F("portfolio__city")), When(portfolio__isnull=False, then=F("portfolio__city")),
# Otherwise, return the natively assigned value # Otherwise, return the natively assigned value
default=F("city"), default=F("city"),
output_field=CharField(), output_field=CharField(),
), ),
"converted_state_territory" : Case( "converted_state_territory": Case(
# When portfolio is present, use its value instead # When portfolio is present, use its value instead
When(portfolio__isnull=False, then=F("portfolio__state_territory")), When(portfolio__isnull=False, then=F("portfolio__state_territory")),
# Otherwise, return the natively assigned value # Otherwise, return the natively assigned value
default=F("state_territory"), default=F("state_territory"),
output_field=CharField(), output_field=CharField(),
), ),
"converted_so_email" : Case( "converted_so_email": Case(
# When portfolio is present, use its value instead # When portfolio is present, use its value instead
When(portfolio__isnull=False, then=F("portfolio__senior_official__email")), When(portfolio__isnull=False, then=F("portfolio__senior_official__email")),
# Otherwise, return the natively assigned senior official # Otherwise, return the natively assigned senior official
default=F("senior_official__email"), default=F("senior_official__email"),
output_field=CharField(), output_field=CharField(),
), ),
"converted_senior_official_last_name" : Case( "converted_senior_official_last_name": Case(
# When portfolio is present, use its value instead # When portfolio is present, use its value instead
When(portfolio__isnull=False, then=F("portfolio__senior_official__last_name")), When(portfolio__isnull=False, then=F("portfolio__senior_official__last_name")),
# Otherwise, return the natively assigned senior official # Otherwise, return the natively assigned senior official
default=F("senior_official__last_name"), default=F("senior_official__last_name"),
output_field=CharField(), output_field=CharField(),
), ),
"converted_senior_official_first_name" : Case( "converted_senior_official_first_name": Case(
# When portfolio is present, use its value instead # When portfolio is present, use its value instead
When(portfolio__isnull=False, then=F("portfolio__senior_official__first_name")), When(portfolio__isnull=False, then=F("portfolio__senior_official__first_name")),
# Otherwise, return the natively assigned senior official # Otherwise, return the natively assigned senior official
default=F("senior_official__first_name"), default=F("senior_official__first_name"),
output_field=CharField(), output_field=CharField(),
), ),
"converted_senior_official_title" : Case( "converted_senior_official_title": Case(
# When portfolio is present, use its value instead # When portfolio is present, use its value instead
When(portfolio__isnull=False, then=F("portfolio__senior_official__title")), When(portfolio__isnull=False, then=F("portfolio__senior_official__title")),
# Otherwise, return the natively assigned senior official # Otherwise, return the natively assigned senior official
@ -355,12 +365,15 @@ class DomainExport(BaseExport):
), ),
"converted_so_name": Case( "converted_so_name": Case(
# When portfolio is present, use that senior official instead # When portfolio is present, use that senior official instead
When(portfolio__isnull=False, then=Concat( When(
Coalesce(F("portfolio__senior_official__first_name"), Value("")), portfolio__isnull=False,
Value(" "), then=Concat(
Coalesce(F("portfolio__senior_official__last_name"), Value("")), Coalesce(F("portfolio__senior_official__first_name"), Value("")),
output_field=CharField(), Value(" "),
)), Coalesce(F("portfolio__senior_official__last_name"), Value("")),
output_field=CharField(),
),
),
# Otherwise, return the natively assigned senior official # Otherwise, return the natively assigned senior official
default=Concat( default=Concat(
Coalesce(F("senior_official__first_name"), Value("")), Coalesce(F("senior_official__first_name"), Value("")),
@ -479,7 +492,6 @@ class DomainExport(BaseExport):
): ):
security_contact_email = "(blank)" security_contact_email = "(blank)"
# 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 = {
@ -508,12 +520,14 @@ class DomainExport(BaseExport):
def get_filtered_domain_infos_by_org(domain_infos_to_filter, org_to_filter_by): def get_filtered_domain_infos_by_org(domain_infos_to_filter, org_to_filter_by):
"""Returns a list of Domain Requests that has been filtered by the given organization value.""" """Returns a list of Domain Requests that has been filtered by the given organization value."""
return domain_infos_to_filter.filter( return domain_infos_to_filter.filter(
# Filter based on the generic org value returned by converted_generic_org_type # Filter based on the generic org value returned by converted_generic_org_type
id__in=[ id__in=[
domainInfos.id for domainInfos in domain_infos_to_filter if domainInfos.converted_generic_org_type and domainInfos.converted_generic_org_type == org_to_filter_by domainInfos.id
] for domainInfos in domain_infos_to_filter
) if domainInfos.converted_generic_org_type and domainInfos.converted_generic_org_type == org_to_filter_by
]
)
@classmethod @classmethod
def get_sliced_domains(cls, filter_condition): def get_sliced_domains(cls, filter_condition):
"""Get filtered domains counts sliced by org type and election office. """Get filtered domains counts sliced by org type and election office.
@ -523,19 +537,47 @@ class DomainExport(BaseExport):
domain_informations = DomainInformation.objects.all().filter(**filter_condition).distinct() domain_informations = DomainInformation.objects.all().filter(**filter_condition).distinct()
domains_count = domain_informations.count() domains_count = domain_informations.count()
federal = cls.get_filtered_domain_infos_by_org(domain_informations, DomainRequest.OrganizationChoices.FEDERAL).distinct().count() federal = (
interstate = cls.get_filtered_domain_infos_by_org(domain_informations, DomainRequest.OrganizationChoices.INTERSTATE).count() cls.get_filtered_domain_infos_by_org(domain_informations, DomainRequest.OrganizationChoices.FEDERAL)
state_or_territory = ( .distinct()
cls.get_filtered_domain_infos_by_org(domain_informations, DomainRequest.OrganizationChoices.STATE_OR_TERRITORY).distinct().count() .count()
)
interstate = cls.get_filtered_domain_infos_by_org(
domain_informations, DomainRequest.OrganizationChoices.INTERSTATE
).count()
state_or_territory = (
cls.get_filtered_domain_infos_by_org(
domain_informations, DomainRequest.OrganizationChoices.STATE_OR_TERRITORY
)
.distinct()
.count()
)
tribal = (
cls.get_filtered_domain_infos_by_org(domain_informations, DomainRequest.OrganizationChoices.TRIBAL)
.distinct()
.count()
)
county = (
cls.get_filtered_domain_infos_by_org(domain_informations, DomainRequest.OrganizationChoices.COUNTY)
.distinct()
.count()
)
city = (
cls.get_filtered_domain_infos_by_org(domain_informations, DomainRequest.OrganizationChoices.CITY)
.distinct()
.count()
) )
tribal = cls.get_filtered_domain_infos_by_org(domain_informations, DomainRequest.OrganizationChoices.TRIBAL).distinct().count()
county = cls.get_filtered_domain_infos_by_org(domain_informations, DomainRequest.OrganizationChoices.COUNTY).distinct().count()
city = cls.get_filtered_domain_infos_by_org(domain_informations, DomainRequest.OrganizationChoices.CITY).distinct().count()
special_district = ( special_district = (
cls.get_filtered_domain_infos_by_org(domain_informations, DomainRequest.OrganizationChoices.SPECIAL_DISTRICT).distinct().count() cls.get_filtered_domain_infos_by_org(
domain_informations, DomainRequest.OrganizationChoices.SPECIAL_DISTRICT
)
.distinct()
.count()
) )
school_district = ( school_district = (
cls.get_filtered_domain_infos_by_org(domain_informations, DomainRequest.OrganizationChoices.SCHOOL_DISTRICT).distinct().count() cls.get_filtered_domain_infos_by_org(domain_informations, DomainRequest.OrganizationChoices.SCHOOL_DISTRICT)
.distinct()
.count()
) )
election_board = domain_informations.filter(is_election_board=True).distinct().count() election_board = domain_informations.filter(is_election_board=True).distinct().count()
@ -632,7 +674,6 @@ class DomainDataType(DomainExport):
""" """
return ["permissions"] return ["permissions"]
@classmethod @classmethod
def get_related_table_fields(cls): def get_related_table_fields(cls):
""" """
@ -1281,77 +1322,79 @@ class DomainRequestExport(BaseExport):
def model(cls): def model(cls):
# Return the model class that this export handles # Return the model class that this export handles
return DomainRequest return DomainRequest
def get_filtered_domain_requests_by_org(domain_requests_to_filter, org_to_filter_by): def get_filtered_domain_requests_by_org(domain_requests_to_filter, org_to_filter_by):
"""Returns a list of Domain Requests that has been filtered by the given organization value""" """Returns a list of Domain Requests that has been filtered by the given organization value"""
return domain_requests_to_filter.filter( return domain_requests_to_filter.filter(
# Filter based on the generic org value returned by converted_generic_org_type # Filter based on the generic org value returned by converted_generic_org_type
id__in=[ id__in=[
domainRequest.id for domainRequest in domain_requests_to_filter if domainRequest.converted_generic_org_type and domainRequest.converted_generic_org_type == org_to_filter_by domainRequest.id
] for domainRequest in domain_requests_to_filter
) if domainRequest.converted_generic_org_type
and domainRequest.converted_generic_org_type == org_to_filter_by
]
)
@classmethod @classmethod
def get_computed_fields(cls, delimiter=", "): def get_computed_fields(cls, delimiter=", "):
""" """
Get a dict of computed fields. Get a dict of computed fields.
""" """
# NOTE: These computed fields imitate @Property functions in the Domain model where needed. # NOTE: These computed fields imitate @Property functions in the Domain model where needed.
# This is for performance purposes. Since we are working with dictionary values and not # This is for performance purposes. Since we are working with dictionary values and not
# model objects as we export data, trying to reinstate model objects in order to grab @property # model objects as we export data, trying to reinstate model objects in order to grab @property
# values negatively impacts performance. Therefore, we will follow best practice and use annotations # values negatively impacts performance. Therefore, we will follow best practice and use annotations
return { return {
"converted_federal_agency" : Case( "converted_federal_agency": Case(
# When portfolio is present, use its value instead # When portfolio is present, use its value instead
When(portfolio__isnull=False, then=F("portfolio__federal_agency")), When(portfolio__isnull=False, then=F("portfolio__federal_agency")),
# Otherwise, return the natively assigned value # Otherwise, return the natively assigned value
default=F("federal_agency"), default=F("federal_agency"),
output_field=CharField(), output_field=CharField(),
), ),
"converted_organization_name" : Case( "converted_organization_name": Case(
# When portfolio is present, use its value instead # When portfolio is present, use its value instead
When(portfolio__isnull=False, then=F("portfolio__organization_name")), When(portfolio__isnull=False, then=F("portfolio__organization_name")),
# Otherwise, return the natively assigned value # Otherwise, return the natively assigned value
default=F("organization_name"), default=F("organization_name"),
output_field=CharField(), output_field=CharField(),
), ),
"converted_city" : Case( "converted_city": Case(
# When portfolio is present, use its value instead # When portfolio is present, use its value instead
When(portfolio__isnull=False, then=F("portfolio__city")), When(portfolio__isnull=False, then=F("portfolio__city")),
# Otherwise, return the natively assigned value # Otherwise, return the natively assigned value
default=F("city"), default=F("city"),
output_field=CharField(), output_field=CharField(),
), ),
"converted_state_territory" : Case( "converted_state_territory": Case(
# When portfolio is present, use its value instead # When portfolio is present, use its value instead
When(portfolio__isnull=False, then=F("portfolio__state_territory")), When(portfolio__isnull=False, then=F("portfolio__state_territory")),
# Otherwise, return the natively assigned value # Otherwise, return the natively assigned value
default=F("state_territory"), default=F("state_territory"),
output_field=CharField(), output_field=CharField(),
), ),
"converted_so_email" : Case( "converted_so_email": Case(
# When portfolio is present, use its value instead # When portfolio is present, use its value instead
When(portfolio__isnull=False, then=F("portfolio__senior_official__email")), When(portfolio__isnull=False, then=F("portfolio__senior_official__email")),
# Otherwise, return the natively assigned senior official # Otherwise, return the natively assigned senior official
default=F("senior_official__email"), default=F("senior_official__email"),
output_field=CharField(), output_field=CharField(),
), ),
"converted_senior_official_last_name" : Case( "converted_senior_official_last_name": Case(
# When portfolio is present, use its value instead # When portfolio is present, use its value instead
When(portfolio__isnull=False, then=F("portfolio__senior_official__last_name")), When(portfolio__isnull=False, then=F("portfolio__senior_official__last_name")),
# Otherwise, return the natively assigned senior official # Otherwise, return the natively assigned senior official
default=F("senior_official__last_name"), default=F("senior_official__last_name"),
output_field=CharField(), output_field=CharField(),
), ),
"converted_senior_official_first_name" : Case( "converted_senior_official_first_name": Case(
# When portfolio is present, use its value instead # When portfolio is present, use its value instead
When(portfolio__isnull=False, then=F("portfolio__senior_official__first_name")), When(portfolio__isnull=False, then=F("portfolio__senior_official__first_name")),
# Otherwise, return the natively assigned senior official # Otherwise, return the natively assigned senior official
default=F("senior_official__first_name"), default=F("senior_official__first_name"),
output_field=CharField(), output_field=CharField(),
), ),
"converted_senior_official_title" : Case( "converted_senior_official_title": Case(
# When portfolio is present, use its value instead # When portfolio is present, use its value instead
When(portfolio__isnull=False, then=F("portfolio__senior_official__title")), When(portfolio__isnull=False, then=F("portfolio__senior_official__title")),
# Otherwise, return the natively assigned senior official # Otherwise, return the natively assigned senior official
@ -1360,12 +1403,15 @@ class DomainRequestExport(BaseExport):
), ),
"converted_so_name": Case( "converted_so_name": Case(
# When portfolio is present, use that senior official instead # When portfolio is present, use that senior official instead
When(portfolio__isnull=False, then=Concat( When(
Coalesce(F("portfolio__senior_official__first_name"), Value("")), portfolio__isnull=False,
Value(" "), then=Concat(
Coalesce(F("portfolio__senior_official__last_name"), Value("")), Coalesce(F("portfolio__senior_official__first_name"), Value("")),
output_field=CharField(), Value(" "),
)), Coalesce(F("portfolio__senior_official__last_name"), Value("")),
output_field=CharField(),
),
),
# Otherwise, return the natively assigned senior official # Otherwise, return the natively assigned senior official
default=Concat( default=Concat(
Coalesce(F("senior_official__first_name"), Value("")), Coalesce(F("senior_official__first_name"), Value("")),
@ -1376,25 +1422,49 @@ class DomainRequestExport(BaseExport):
output_field=CharField(), output_field=CharField(),
), ),
} }
@classmethod @classmethod
def get_sliced_requests(cls, filter_condition): def get_sliced_requests(cls, filter_condition):
"""Get filtered requests counts sliced by org type and election office.""" """Get filtered requests counts sliced by org type and election office."""
requests = DomainRequest.objects.all().filter(**filter_condition).distinct() requests = DomainRequest.objects.all().filter(**filter_condition).distinct()
requests_count = requests.count() requests_count = requests.count()
federal = cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.FEDERAL).distinct().count() federal = (
interstate = cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.INTERSTATE).distinct().count() cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.FEDERAL)
state_or_territory = ( .distinct()
cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.STATE_OR_TERRITORY).distinct().count() .count()
)
interstate = (
cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.INTERSTATE)
.distinct()
.count()
)
state_or_territory = (
cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.STATE_OR_TERRITORY)
.distinct()
.count()
)
tribal = (
cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.TRIBAL)
.distinct()
.count()
)
county = (
cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.COUNTY)
.distinct()
.count()
)
city = (
cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.CITY).distinct().count()
) )
tribal = cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.TRIBAL).distinct().count()
county = cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.COUNTY).distinct().count()
city = cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.CITY).distinct().count()
special_district = ( special_district = (
cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.SPECIAL_DISTRICT).distinct().count() cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.SPECIAL_DISTRICT)
.distinct()
.count()
) )
school_district = ( school_district = (
cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.SCHOOL_DISTRICT).distinct().count() cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.SCHOOL_DISTRICT)
.distinct()
.count()
) )
election_board = requests.filter(is_election_board=True).distinct().count() election_board = requests.filter(is_election_board=True).distinct().count()
@ -1541,7 +1611,8 @@ class DomainRequestGrowth(DomainRequestExport):
Get a list of fields from related tables. Get a list of fields from related tables.
""" """
return ["requested_domain__name"] return ["requested_domain__name"]
class DomainRequestDataFull(DomainRequestExport): class DomainRequestDataFull(DomainRequestExport):
""" """
Shows all but STARTED requests Shows all but STARTED requests
@ -1625,24 +1696,28 @@ class DomainRequestDataFull(DomainRequestExport):
computed_fields = super().get_computed_fields() computed_fields = super().get_computed_fields()
# Add additional computed fields # Add additional computed fields
computed_fields.update({ computed_fields.update(
"creator_approved_domains_count": cls.get_creator_approved_domains_count_query(), {
"creator_active_requests_count": cls.get_creator_active_requests_count_query(), "creator_approved_domains_count": cls.get_creator_approved_domains_count_query(),
"all_current_websites": StringAgg("current_websites__website", delimiter=delimiter, distinct=True), "creator_active_requests_count": cls.get_creator_active_requests_count_query(),
"all_alternative_domains": StringAgg("alternative_domains__website", delimiter=delimiter, distinct=True), "all_current_websites": StringAgg("current_websites__website", delimiter=delimiter, distinct=True),
# Coerce the other contacts object to "{first_name} {last_name} {email}" "all_alternative_domains": StringAgg(
"all_other_contacts": StringAgg( "alternative_domains__website", delimiter=delimiter, distinct=True
Concat(
"other_contacts__first_name",
Value(" "),
"other_contacts__last_name",
Value(" "),
"other_contacts__email",
), ),
delimiter=delimiter, # Coerce the other contacts object to "{first_name} {last_name} {email}"
distinct=True, "all_other_contacts": StringAgg(
), Concat(
}) "other_contacts__first_name",
Value(" "),
"other_contacts__last_name",
Value(" "),
"other_contacts__email",
),
delimiter=delimiter,
distinct=True,
),
}
)
return computed_fields return computed_fields

View file

@ -13,13 +13,9 @@ from registrar.utility import csv_export
import logging import logging
# ---Logger
import logging
from venv import logger
from registrar.management.commands.utility.terminal_helper import TerminalColors, TerminalHelper
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class AnalyticsView(View): class AnalyticsView(View):
def get(self, request): def get(self, request):
thirty_days_ago = datetime.datetime.today() - datetime.timedelta(days=30) thirty_days_ago = datetime.datetime.today() - datetime.timedelta(days=30)
@ -165,10 +161,7 @@ class ExportDataType(View):
class ExportDataTypeUser(View): class ExportDataTypeUser(View):
"""Returns a domain report for a given user on the request""" """Returns a domain report for a given user on the request"""
TerminalHelper.colorful_logger(logger.info, TerminalColors.OKGREEN, f"ExportDataTypeUser")
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
TerminalHelper.colorful_logger(logger.info, TerminalColors.OKGREEN, f"ExportDataTypeUser -- get")
# match the CSV example with all the fields # match the CSV example with all the fields
response = HttpResponse(content_type="text/csv") response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = 'attachment; filename="your-domains.csv"' response["Content-Disposition"] = 'attachment; filename="your-domains.csv"'