mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-13 21:19:42 +02:00
Updates to analytics exports
This commit is contained in:
parent
76ac7a002d
commit
88554e0e75
1 changed files with 196 additions and 61 deletions
|
@ -11,15 +11,17 @@ from registrar.models import (
|
||||||
PublicContact,
|
PublicContact,
|
||||||
UserDomainRole,
|
UserDomainRole,
|
||||||
)
|
)
|
||||||
from django.db.models import Case, CharField, Count, DateField, F, ManyToManyField, Q, QuerySet, Value, When
|
from django.db.models import Case, CharField, Count, DateField, F, ManyToManyField, Q, QuerySet, Value, When, ExpressionWrapper
|
||||||
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
|
||||||
from registrar.utility.enums import DefaultEmail
|
from registrar.utility.enums import DefaultEmail
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -285,6 +287,91 @@ class DomainExport(BaseExport):
|
||||||
# Return the model class that this export handles
|
# Return the model class that this export handles
|
||||||
return DomainInformation
|
return DomainInformation
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_computed_fields(cls, delimiter=", "):
|
||||||
|
"""
|
||||||
|
Get a dict of computed fields.
|
||||||
|
"""
|
||||||
|
# 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
|
||||||
|
# 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
|
||||||
|
return {
|
||||||
|
"converted_federal_agency" : Case(
|
||||||
|
# When portfolio is present, use its value instead
|
||||||
|
When(portfolio__isnull=False, then=F("portfolio__federal_agency")),
|
||||||
|
# Otherwise, return the natively assigned value
|
||||||
|
default=F("federal_agency"),
|
||||||
|
output_field=CharField(),
|
||||||
|
),
|
||||||
|
"converted_organization_name" : Case(
|
||||||
|
# When portfolio is present, use its value instead
|
||||||
|
When(portfolio__isnull=False, then=F("portfolio__organization_name")),
|
||||||
|
# Otherwise, return the natively assigned value
|
||||||
|
default=F("organization_name"),
|
||||||
|
output_field=CharField(),
|
||||||
|
),
|
||||||
|
"converted_city" : Case(
|
||||||
|
# When portfolio is present, use its value instead
|
||||||
|
When(portfolio__isnull=False, then=F("portfolio__city")),
|
||||||
|
# Otherwise, return the natively assigned value
|
||||||
|
default=F("city"),
|
||||||
|
output_field=CharField(),
|
||||||
|
),
|
||||||
|
"converted_state_territory" : Case(
|
||||||
|
# When portfolio is present, use its value instead
|
||||||
|
When(portfolio__isnull=False, then=F("portfolio__state_territory")),
|
||||||
|
# Otherwise, return the natively assigned value
|
||||||
|
default=F("state_territory"),
|
||||||
|
output_field=CharField(),
|
||||||
|
),
|
||||||
|
"converted_so_email" : Case(
|
||||||
|
# When portfolio is present, use its value instead
|
||||||
|
When(portfolio__isnull=False, then=F("portfolio__senior_official__email")),
|
||||||
|
# Otherwise, return the natively assigned senior official
|
||||||
|
default=F("senior_official__email"),
|
||||||
|
output_field=CharField(),
|
||||||
|
),
|
||||||
|
"converted_senior_official_last_name" : Case(
|
||||||
|
# When portfolio is present, use its value instead
|
||||||
|
When(portfolio__isnull=False, then=F("portfolio__senior_official__last_name")),
|
||||||
|
# Otherwise, return the natively assigned senior official
|
||||||
|
default=F("senior_official__last_name"),
|
||||||
|
output_field=CharField(),
|
||||||
|
),
|
||||||
|
"converted_senior_official_first_name" : Case(
|
||||||
|
# When portfolio is present, use its value instead
|
||||||
|
When(portfolio__isnull=False, then=F("portfolio__senior_official__first_name")),
|
||||||
|
# Otherwise, return the natively assigned senior official
|
||||||
|
default=F("senior_official__first_name"),
|
||||||
|
output_field=CharField(),
|
||||||
|
),
|
||||||
|
"converted_senior_official_title" : Case(
|
||||||
|
# When portfolio is present, use its value instead
|
||||||
|
When(portfolio__isnull=False, then=F("portfolio__senior_official__title")),
|
||||||
|
# Otherwise, return the natively assigned senior official
|
||||||
|
default=F("senior_official__title"),
|
||||||
|
output_field=CharField(),
|
||||||
|
),
|
||||||
|
"converted_so_name": Case(
|
||||||
|
# When portfolio is present, use that senior official instead
|
||||||
|
When(portfolio__isnull=False, then=Concat(
|
||||||
|
Coalesce(F("portfolio__senior_official__first_name"), Value("")),
|
||||||
|
Value(" "),
|
||||||
|
Coalesce(F("portfolio__senior_official__last_name"), Value("")),
|
||||||
|
output_field=CharField(),
|
||||||
|
)),
|
||||||
|
# Otherwise, return the natively assigned senior official
|
||||||
|
default=Concat(
|
||||||
|
Coalesce(F("senior_official__first_name"), Value("")),
|
||||||
|
Value(" "),
|
||||||
|
Coalesce(F("senior_official__last_name"), Value("")),
|
||||||
|
output_field=CharField(),
|
||||||
|
),
|
||||||
|
output_field=CharField(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update_queryset(cls, queryset, **kwargs):
|
def update_queryset(cls, queryset, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -401,12 +488,12 @@ class DomainExport(BaseExport):
|
||||||
"First ready on": first_ready_on,
|
"First ready on": first_ready_on,
|
||||||
"Expiration date": expiration_date,
|
"Expiration date": expiration_date,
|
||||||
"Domain type": domain_type,
|
"Domain type": domain_type,
|
||||||
"Agency": model.get("converted_federal_agency__agency"),
|
"Agency": model.get("converted_federal_agency"),
|
||||||
"Organization name": model.get("converted_organization_name"),
|
"Organization name": model.get("converted_organization_name"),
|
||||||
"City": model.get("converted_city"),
|
"City": model.get("converted_city"),
|
||||||
"State": model.get("converted_state_territory"),
|
"State": model.get("converted_state_territory"),
|
||||||
"SO": model.get("so_name"),
|
"SO": model.get("converted_so_name"),
|
||||||
"SO email": model.get("converted_senior_official__email"),
|
"SO email": model.get("converted_so_email"),
|
||||||
"Security contact email": security_contact_email,
|
"Security contact email": security_contact_email,
|
||||||
"Created at": model.get("domain__created_at"),
|
"Created at": model.get("domain__created_at"),
|
||||||
"Deleted": model.get("domain__deleted"),
|
"Deleted": model.get("domain__deleted"),
|
||||||
|
@ -416,7 +503,6 @@ class DomainExport(BaseExport):
|
||||||
|
|
||||||
row = [FIELDS.get(column, "") for column in columns]
|
row = [FIELDS.get(column, "") for column in columns]
|
||||||
|
|
||||||
|
|
||||||
return row
|
return row
|
||||||
|
|
||||||
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):
|
||||||
|
@ -537,7 +623,7 @@ class DomainDataType(DomainExport):
|
||||||
"""
|
"""
|
||||||
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 ["domain", "converted_senior_official"]
|
return ["domain", "senior_official"]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_prefetch_related(cls):
|
def get_prefetch_related(cls):
|
||||||
|
@ -546,19 +632,6 @@ class DomainDataType(DomainExport):
|
||||||
"""
|
"""
|
||||||
return ["permissions"]
|
return ["permissions"]
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_computed_fields(cls, delimiter=", "):
|
|
||||||
"""
|
|
||||||
Get a dict of computed fields.
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
"so_name": Concat(
|
|
||||||
Coalesce(F("senior_official__first_name"), Value("")),
|
|
||||||
Value(" "),
|
|
||||||
Coalesce(F("senior_official__last_name"), Value("")),
|
|
||||||
output_field=CharField(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_related_table_fields(cls):
|
def get_related_table_fields(cls):
|
||||||
|
@ -673,11 +746,11 @@ class DomainRequestsDataType:
|
||||||
cls.safe_get(getattr(request, "all_alternative_domains", None)),
|
cls.safe_get(getattr(request, "all_alternative_domains", None)),
|
||||||
cls.safe_get(getattr(request, "all_other_contacts", None)),
|
cls.safe_get(getattr(request, "all_other_contacts", None)),
|
||||||
cls.safe_get(getattr(request, "all_current_websites", None)),
|
cls.safe_get(getattr(request, "all_current_websites", None)),
|
||||||
cls.safe_get(getattr(request, "federal_agency", None)),
|
cls.safe_get(getattr(request, "converted_federal_agency", None)),
|
||||||
cls.safe_get(getattr(request.senior_official, "first_name", None)),
|
cls.safe_get(getattr(request.converted_senior_official, "first_name", None)),
|
||||||
cls.safe_get(getattr(request.senior_official, "last_name", None)),
|
cls.safe_get(getattr(request.converted_senior_official, "last_name", None)),
|
||||||
cls.safe_get(getattr(request.senior_official, "email", None)),
|
cls.safe_get(getattr(request.converted_senior_official, "email", None)),
|
||||||
cls.safe_get(getattr(request.senior_official, "title", None)),
|
cls.safe_get(getattr(request.converted_senior_official, "title", None)),
|
||||||
cls.safe_get(getattr(request.creator, "first_name", None)),
|
cls.safe_get(getattr(request.creator, "first_name", None)),
|
||||||
cls.safe_get(getattr(request.creator, "last_name", None)),
|
cls.safe_get(getattr(request.creator, "last_name", None)),
|
||||||
cls.safe_get(getattr(request.creator, "email", None)),
|
cls.safe_get(getattr(request.creator, "email", None)),
|
||||||
|
@ -763,20 +836,6 @@ class DomainDataFull(DomainExport):
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_computed_fields(cls, delimiter=", "):
|
|
||||||
"""
|
|
||||||
Get a dict of computed fields.
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
"so_name": Concat(
|
|
||||||
Coalesce(F("senior_official__first_name"), Value("")),
|
|
||||||
Value(" "),
|
|
||||||
Coalesce(F("senior_official__last_name"), Value("")),
|
|
||||||
output_field=CharField(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_related_table_fields(cls):
|
def get_related_table_fields(cls):
|
||||||
"""
|
"""
|
||||||
|
@ -858,20 +917,6 @@ class DomainDataFederal(DomainExport):
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_computed_fields(cls, delimiter=", "):
|
|
||||||
"""
|
|
||||||
Get a dict of computed fields.
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
"so_name": Concat(
|
|
||||||
Coalesce(F("senior_official__first_name"), Value("")),
|
|
||||||
Value(" "),
|
|
||||||
Coalesce(F("senior_official__last_name"), Value("")),
|
|
||||||
output_field=CharField(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_related_table_fields(cls):
|
def get_related_table_fields(cls):
|
||||||
"""
|
"""
|
||||||
|
@ -1247,6 +1292,91 @@ class DomainRequestExport(BaseExport):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_computed_fields(cls, delimiter=", "):
|
||||||
|
"""
|
||||||
|
Get a dict of computed fields.
|
||||||
|
"""
|
||||||
|
# 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
|
||||||
|
# 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
|
||||||
|
return {
|
||||||
|
"converted_federal_agency" : Case(
|
||||||
|
# When portfolio is present, use its value instead
|
||||||
|
When(portfolio__isnull=False, then=F("portfolio__federal_agency")),
|
||||||
|
# Otherwise, return the natively assigned value
|
||||||
|
default=F("federal_agency"),
|
||||||
|
output_field=CharField(),
|
||||||
|
),
|
||||||
|
"converted_organization_name" : Case(
|
||||||
|
# When portfolio is present, use its value instead
|
||||||
|
When(portfolio__isnull=False, then=F("portfolio__organization_name")),
|
||||||
|
# Otherwise, return the natively assigned value
|
||||||
|
default=F("organization_name"),
|
||||||
|
output_field=CharField(),
|
||||||
|
),
|
||||||
|
"converted_city" : Case(
|
||||||
|
# When portfolio is present, use its value instead
|
||||||
|
When(portfolio__isnull=False, then=F("portfolio__city")),
|
||||||
|
# Otherwise, return the natively assigned value
|
||||||
|
default=F("city"),
|
||||||
|
output_field=CharField(),
|
||||||
|
),
|
||||||
|
"converted_state_territory" : Case(
|
||||||
|
# When portfolio is present, use its value instead
|
||||||
|
When(portfolio__isnull=False, then=F("portfolio__state_territory")),
|
||||||
|
# Otherwise, return the natively assigned value
|
||||||
|
default=F("state_territory"),
|
||||||
|
output_field=CharField(),
|
||||||
|
),
|
||||||
|
"converted_so_email" : Case(
|
||||||
|
# When portfolio is present, use its value instead
|
||||||
|
When(portfolio__isnull=False, then=F("portfolio__senior_official__email")),
|
||||||
|
# Otherwise, return the natively assigned senior official
|
||||||
|
default=F("senior_official__email"),
|
||||||
|
output_field=CharField(),
|
||||||
|
),
|
||||||
|
"converted_senior_official_last_name" : Case(
|
||||||
|
# When portfolio is present, use its value instead
|
||||||
|
When(portfolio__isnull=False, then=F("portfolio__senior_official__last_name")),
|
||||||
|
# Otherwise, return the natively assigned senior official
|
||||||
|
default=F("senior_official__last_name"),
|
||||||
|
output_field=CharField(),
|
||||||
|
),
|
||||||
|
"converted_senior_official_first_name" : Case(
|
||||||
|
# When portfolio is present, use its value instead
|
||||||
|
When(portfolio__isnull=False, then=F("portfolio__senior_official__first_name")),
|
||||||
|
# Otherwise, return the natively assigned senior official
|
||||||
|
default=F("senior_official__first_name"),
|
||||||
|
output_field=CharField(),
|
||||||
|
),
|
||||||
|
"converted_senior_official_title" : Case(
|
||||||
|
# When portfolio is present, use its value instead
|
||||||
|
When(portfolio__isnull=False, then=F("portfolio__senior_official__title")),
|
||||||
|
# Otherwise, return the natively assigned senior official
|
||||||
|
default=F("senior_official__title"),
|
||||||
|
output_field=CharField(),
|
||||||
|
),
|
||||||
|
"converted_so_name": Case(
|
||||||
|
# When portfolio is present, use that senior official instead
|
||||||
|
When(portfolio__isnull=False, then=Concat(
|
||||||
|
Coalesce(F("portfolio__senior_official__first_name"), Value("")),
|
||||||
|
Value(" "),
|
||||||
|
Coalesce(F("portfolio__senior_official__last_name"), Value("")),
|
||||||
|
output_field=CharField(),
|
||||||
|
)),
|
||||||
|
# Otherwise, return the natively assigned senior official
|
||||||
|
default=Concat(
|
||||||
|
Coalesce(F("senior_official__first_name"), Value("")),
|
||||||
|
Value(" "),
|
||||||
|
Coalesce(F("senior_official__last_name"), Value("")),
|
||||||
|
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."""
|
||||||
|
@ -1340,11 +1470,11 @@ class DomainRequestExport(BaseExport):
|
||||||
"Other contacts": model.get("all_other_contacts"),
|
"Other contacts": model.get("all_other_contacts"),
|
||||||
"Current websites": model.get("all_current_websites"),
|
"Current websites": model.get("all_current_websites"),
|
||||||
# Untouched FK fields - passed into the request dict.
|
# Untouched FK fields - passed into the request dict.
|
||||||
"Federal agency": model.get("federal_agency__agency"),
|
"Federal agency": model.get("converted_federal_agency"),
|
||||||
"SO first name": model.get("senior_official__first_name"),
|
"SO first name": model.get("converted_senior_official_first_name"),
|
||||||
"SO last name": model.get("senior_official__last_name"),
|
"SO last name": model.get("converted_senior_official_last_name"),
|
||||||
"SO email": model.get("senior_official__email"),
|
"SO email": model.get("converted_so_email"),
|
||||||
"SO title/role": model.get("senior_official__title"),
|
"SO title/role": model.get("converted_senior_official_title"),
|
||||||
"Creator first name": model.get("creator__first_name"),
|
"Creator first name": model.get("creator__first_name"),
|
||||||
"Creator last name": model.get("creator__last_name"),
|
"Creator last name": model.get("creator__last_name"),
|
||||||
"Creator email": model.get("creator__email"),
|
"Creator email": model.get("creator__email"),
|
||||||
|
@ -1412,7 +1542,6 @@ class DomainRequestGrowth(DomainRequestExport):
|
||||||
"""
|
"""
|
||||||
return ["requested_domain__name"]
|
return ["requested_domain__name"]
|
||||||
|
|
||||||
|
|
||||||
class DomainRequestDataFull(DomainRequestExport):
|
class DomainRequestDataFull(DomainRequestExport):
|
||||||
"""
|
"""
|
||||||
Shows all but STARTED requests
|
Shows all but STARTED requests
|
||||||
|
@ -1492,7 +1621,11 @@ class DomainRequestDataFull(DomainRequestExport):
|
||||||
"""
|
"""
|
||||||
Get a dict of computed fields.
|
Get a dict of computed fields.
|
||||||
"""
|
"""
|
||||||
return {
|
# Get computed fields from the parent class
|
||||||
|
computed_fields = super().get_computed_fields()
|
||||||
|
|
||||||
|
# Add additional computed fields
|
||||||
|
computed_fields.update({
|
||||||
"creator_approved_domains_count": cls.get_creator_approved_domains_count_query(),
|
"creator_approved_domains_count": cls.get_creator_approved_domains_count_query(),
|
||||||
"creator_active_requests_count": cls.get_creator_active_requests_count_query(),
|
"creator_active_requests_count": cls.get_creator_active_requests_count_query(),
|
||||||
"all_current_websites": StringAgg("current_websites__website", delimiter=delimiter, distinct=True),
|
"all_current_websites": StringAgg("current_websites__website", delimiter=delimiter, distinct=True),
|
||||||
|
@ -1509,7 +1642,9 @@ class DomainRequestDataFull(DomainRequestExport):
|
||||||
delimiter=delimiter,
|
delimiter=delimiter,
|
||||||
distinct=True,
|
distinct=True,
|
||||||
),
|
),
|
||||||
}
|
})
|
||||||
|
|
||||||
|
return computed_fields
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_related_table_fields(cls):
|
def get_related_table_fields(cls):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue