Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #2975 - domain and domain info portfolio fields -[HOTGOV] #3044

Merged
merged 37 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
9f9147e
Initial draft
CocoByte Nov 1, 2024
7292926
WIP on nl/2975-domain-and-domain-info-portfolio-fields
CocoByte Nov 6, 2024
0ac4e57
cleanup stray readonly field
CocoByte Nov 6, 2024
dcc0a51
Merge remote-tracking branch 'origin/main' into nl/2975-domain-and-do…
CocoByte Nov 6, 2024
b0e1b5a
updates to DomainAdmin
CocoByte Nov 6, 2024
ae0eb45
revert read-only field
CocoByte Nov 7, 2024
c481c3c
Merge remote-tracking branch 'origin/main' into nl/2975-domain-and-do…
CocoByte Nov 7, 2024
32232c5
export updates
CocoByte Nov 13, 2024
ba06660
Remove logs
CocoByte Nov 13, 2024
503d214
Cleanup
CocoByte Nov 25, 2024
76ac7a0
Added missing converted fields to domain request
CocoByte Nov 25, 2024
88554e0
Updates to analytics exports
CocoByte Nov 25, 2024
ca8cfda
unit test adjustments
CocoByte Nov 25, 2024
34ba850
linted
CocoByte Nov 25, 2024
e75c027
Fixed annotations and sort
CocoByte Nov 25, 2024
0446aab
unit test fixes
CocoByte Nov 25, 2024
3156315
cleanup
CocoByte Nov 25, 2024
c0a0ac3
linted
CocoByte Nov 25, 2024
36c19d6
fixes for unit test (and admin change form)
CocoByte Nov 26, 2024
bf34df5
resolved merge
CocoByte Nov 26, 2024
e7c22ce
linted
CocoByte Nov 26, 2024
d6f4b71
Merge remote-tracking branch 'origin/main' into nl/2975-domain-and-do…
CocoByte Nov 29, 2024
6ca5356
Re-added missing function (merge error)
CocoByte Nov 29, 2024
a6308ed
A few updates based on feedback
CocoByte Dec 2, 2024
9a8ac32
Remove converted values from exports + fixes
CocoByte Dec 3, 2024
fb2fad6
Efficiency updates and sorting fixes
CocoByte Dec 4, 2024
88e27c8
Merge remote-tracking branch 'origin/main' into nl/2975-domain-and-do…
CocoByte Dec 4, 2024
3b9e9e3
remove dead code (missed in another merge)
CocoByte Dec 4, 2024
87a9655
Cleanup and unit test fixes
CocoByte Dec 5, 2024
d5f7993
cleanup
CocoByte Dec 5, 2024
958d6dc
linted
CocoByte Dec 5, 2024
0a36b10
more unit test adjustments
CocoByte Dec 5, 2024
162ba73
Merge remote-tracking branch 'origin/main' into nl/2975-domain-and-do…
CocoByte Dec 5, 2024
a81cf60
Merge remote-tracking branch 'origin/main' into nl/2975-domain-and-do…
CocoByte Dec 5, 2024
77d4f0a
unit test fix
CocoByte Dec 5, 2024
4e1797a
remove maxDiff = None
CocoByte Dec 5, 2024
4be2aa1
Merge branch 'main' into nl/2975-domain-and-domain-info-portfolio-fields
CocoByte Dec 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
355 changes: 317 additions & 38 deletions src/registrar/admin.py

Large diffs are not rendered by default.

5 changes: 0 additions & 5 deletions src/registrar/config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,11 +226,6 @@
ExportDataTypeRequests.as_view(),
name="export_data_type_requests",
),
path(
"reports/export_data_type_requests/",
ExportDataTypeRequests.as_view(),
name="export_data_type_requests",
),
CocoByte marked this conversation as resolved.
Show resolved Hide resolved
path(
"domain-request/<int:id>/edit/",
views.DomainRequestWizard.as_view(),
Expand Down
2 changes: 1 addition & 1 deletion src/registrar/models/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -2078,4 +2078,4 @@ def _get_property(self, property):
if property in self._cache:
return self._cache[property]
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))
11 changes: 7 additions & 4 deletions src/registrar/models/domain_information.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,13 +426,14 @@ def get_state_display_of_domain(self):
else:
return None

# ----- Portfolio Properties -----

@property
def converted_organization_name(self):
if self.portfolio:
return self.portfolio.organization_name
return self.organization_name
return "portoflio name" #self.portfolio.organization_name
CocoByte marked this conversation as resolved.
Show resolved Hide resolved
return "self name" #self.organization_name

# ----- Portfolio Properties -----
@property
def converted_generic_org_type(self):
if self.portfolio:
Expand Down Expand Up @@ -474,7 +475,7 @@ def converted_city(self):
if self.portfolio:
return self.portfolio.city
return self.city

@property
def converted_state_territory(self):
if self.portfolio:
Expand All @@ -492,3 +493,5 @@ def converted_urbanization(self):
if self.portfolio:
return self.portfolio.urbanization
return self.urbanization


12 changes: 9 additions & 3 deletions src/registrar/models/domain_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -1416,8 +1416,8 @@ def _form_complete(self, request):
@property
def converted_organization_name(self):
if self.portfolio:
return self.portfolio.organization_name
return self.organization_name
return "portfolio name" #self.portfolio.organization_name
CocoByte marked this conversation as resolved.
Show resolved Hide resolved
return "self name" #self.organization_name

@property
def converted_generic_org_type(self):
Expand Down Expand Up @@ -1448,9 +1448,15 @@ def converted_state_territory(self):
if self.portfolio:
return self.portfolio.state_territory
return self.state_territory

@property
def converted_urbanization(self):
if self.portfolio:
return self.portfolio.urbanization
return self.urbanization

@property
def converted_senior_official(self):
if self.portfolio:
return self.portfolio.senior_official
return self.senior_official
return self.senior_official
99 changes: 61 additions & 38 deletions src/registrar/utility/csv_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from registrar.utility.constants import BranchChoices
from registrar.utility.enums import DefaultEmail


logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -197,6 +196,7 @@ def export_data_to_csv(cls, csv_file, **export_kwargs):
All domain metadata:
Exports domains of all statuses plus domain managers.
"""

writer = csv.writer(csv_file)
columns = cls.get_columns()
sort_fields = cls.get_sort_fields()
Expand Down Expand Up @@ -374,8 +374,8 @@ def parse_row(cls, columns, model):
if first_ready_on is None:
first_ready_on = "(blank)"

# organization_type has generic_org_type AND is_election
domain_org_type = model.get("organization_type")
# organization_type has organization_type AND is_election
domain_org_type = model.get("converted_generic_org_type") or model.get("organization_type")
human_readable_domain_org_type = DomainRequest.OrgChoicesElectionOffice.get_org_label(domain_org_type)
domain_federal_type = model.get("federal_type")
human_readable_domain_federal_type = BranchChoices.get_branch_label(domain_federal_type)
Expand All @@ -392,6 +392,7 @@ def parse_row(cls, columns, model):
):
security_contact_email = "(blank)"


# create a dictionary of fields which can be included in output.
# "extra_fields" are precomputed fields (generated in the DB or parsed).
FIELDS = {
Expand All @@ -400,12 +401,12 @@ def parse_row(cls, columns, model):
"First ready on": first_ready_on,
"Expiration date": expiration_date,
"Domain type": domain_type,
"Agency": model.get("federal_agency__agency"),
"Organization name": model.get("organization_name"),
"City": model.get("city"),
"State": model.get("state_territory"),
"Agency": model.get("converted_federal_agency__agency"),
"Organization name": model.get("converted_organization_name"),
"City": model.get("converted_city"),
"State": model.get("converted_state_territory"),
"SO": model.get("so_name"),
"SO email": model.get("senior_official__email"),
"SO email": model.get("converted_senior_official__email"),
"Security contact email": security_contact_email,
"Created at": model.get("domain__created_at"),
"Deleted": model.get("domain__deleted"),
Expand All @@ -414,32 +415,43 @@ def parse_row(cls, columns, model):
}

row = [FIELDS.get(column, "") for column in columns]


return row

def get_filtered_domain_infos_by_org(domain_infos_to_filter, org_to_filter_by):
CocoByte marked this conversation as resolved.
Show resolved Hide resolved
"""Returns a list of Domain Requests that has been filtered by the given organization value."""
return domain_infos_to_filter.filter(
# Filter based on the generic org value returned by converted_generic_org_type
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
]
)

@classmethod
def get_sliced_domains(cls, filter_condition):
"""Get filtered domains counts sliced by org type and election office.
Pass distinct=True when filtering by permissions so we do not to count multiples
when a domain has more that one manager.
"""

domains = DomainInformation.objects.all().filter(**filter_condition).distinct()
domains_count = domains.count()
federal = domains.filter(generic_org_type=DomainRequest.OrganizationChoices.FEDERAL).distinct().count()
interstate = domains.filter(generic_org_type=DomainRequest.OrganizationChoices.INTERSTATE).count()
domain_informations = DomainInformation.objects.all().filter(**filter_condition).distinct()
domains_count = domain_informations.count()
federal = cls.get_filtered_domain_infos_by_org(domain_informations, DomainRequest.OrganizationChoices.FEDERAL).distinct().count()
interstate = cls.get_filtered_domain_infos_by_org(domain_informations, DomainRequest.OrganizationChoices.INTERSTATE).count()
state_or_territory = (
domains.filter(generic_org_type=DomainRequest.OrganizationChoices.STATE_OR_TERRITORY).distinct().count()
cls.get_filtered_domain_infos_by_org(domain_informations, DomainRequest.OrganizationChoices.STATE_OR_TERRITORY).distinct().count()
)
tribal = domains.filter(generic_org_type=DomainRequest.OrganizationChoices.TRIBAL).distinct().count()
county = domains.filter(generic_org_type=DomainRequest.OrganizationChoices.COUNTY).distinct().count()
city = domains.filter(generic_org_type=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 = (
domains.filter(generic_org_type=DomainRequest.OrganizationChoices.SPECIAL_DISTRICT).distinct().count()
cls.get_filtered_domain_infos_by_org(domain_informations, DomainRequest.OrganizationChoices.SPECIAL_DISTRICT).distinct().count()
)
school_district = (
domains.filter(generic_org_type=DomainRequest.OrganizationChoices.SCHOOL_DISTRICT).distinct().count()
cls.get_filtered_domain_infos_by_org(domain_informations, DomainRequest.OrganizationChoices.SCHOOL_DISTRICT).distinct().count()
)
election_board = domains.filter(is_election_board=True).distinct().count()
election_board = domain_informations.filter(is_election_board=True).distinct().count()

return [
domains_count,
Expand All @@ -466,6 +478,7 @@ def get_columns(cls):
"""
Overrides the columns for CSV export specific to DomainExport.
"""

return [
"Domain name",
"Status",
Expand Down Expand Up @@ -524,7 +537,7 @@ def get_select_related(cls):
"""
Get a list of tables to pass to select_related when building queryset.
"""
return ["domain", "senior_official"]
return ["domain", "converted_senior_official"]

@classmethod
def get_prefetch_related(cls):
Expand Down Expand Up @@ -660,11 +673,11 @@ def exporting_dr_data_to_csv(cls, response, request=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_current_websites", None)),
cls.safe_get(getattr(request, "converted_federal_agency", None)),
cls.safe_get(getattr(request.converted_senior_official, "first_name", None)),
cls.safe_get(getattr(request.converted_senior_official, "last_name", None)),
cls.safe_get(getattr(request.converted_senior_official, "email", None)),
cls.safe_get(getattr(request.converted_senior_official, "title", None)),
cls.safe_get(getattr(request, "federal_agency", None)),
cls.safe_get(getattr(request.senior_official, "first_name", None)),
cls.safe_get(getattr(request.senior_official, "last_name", None)),
cls.safe_get(getattr(request.senior_official, "email", None)),
cls.safe_get(getattr(request.senior_official, "title", 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, "email", None)),
Expand Down Expand Up @@ -1223,25 +1236,35 @@ class DomainRequestExport(BaseExport):
def model(cls):
# Return the model class that this export handles
return DomainRequest


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"""
return domain_requests_to_filter.filter(
# Filter based on the generic org value returned by converted_generic_org_type
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
]
)


@classmethod
def get_sliced_requests(cls, filter_condition):
"""Get filtered requests counts sliced by org type and election office."""
requests = DomainRequest.objects.all().filter(**filter_condition).distinct()
requests_count = requests.count()
federal = requests.filter(generic_org_type=DomainRequest.OrganizationChoices.FEDERAL).distinct().count()
interstate = requests.filter(generic_org_type=DomainRequest.OrganizationChoices.INTERSTATE).distinct().count()
federal = cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.FEDERAL).distinct().count()
interstate = cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.INTERSTATE).distinct().count()
state_or_territory = (
requests.filter(generic_org_type=DomainRequest.OrganizationChoices.STATE_OR_TERRITORY).distinct().count()
cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.STATE_OR_TERRITORY).distinct().count()
)
tribal = requests.filter(generic_org_type=DomainRequest.OrganizationChoices.TRIBAL).distinct().count()
county = requests.filter(generic_org_type=DomainRequest.OrganizationChoices.COUNTY).distinct().count()
city = requests.filter(generic_org_type=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 = (
requests.filter(generic_org_type=DomainRequest.OrganizationChoices.SPECIAL_DISTRICT).distinct().count()
cls.get_filtered_domain_requests_by_org(requests, DomainRequest.OrganizationChoices.SPECIAL_DISTRICT).distinct().count()
)
school_district = (
requests.filter(generic_org_type=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()

Expand Down Expand Up @@ -1269,7 +1292,7 @@ def parse_row(cls, columns, model):
human_readable_federal_type = BranchChoices.get_branch_label(federal_type) if federal_type else None

# Handle the org_type field
org_type = model.get("generic_org_type") or model.get("organization_type")
org_type = model.get("converted_generic_org_type") or model.get("organization_type")
human_readable_org_type = DomainRequest.OrganizationChoices.get_org_label(org_type) if org_type else None

# Handle the status field. Defaults to the wrong format.
Expand Down Expand Up @@ -1327,9 +1350,9 @@ def parse_row(cls, columns, model):
"Creator email": model.get("creator__email"),
"Investigator": model.get("investigator__email"),
# Untouched fields
"Organization name": model.get("organization_name"),
"City": model.get("city"),
"State/territory": model.get("state_territory"),
"Organization name": model.get("converted_organization_name"),
"City": model.get("converted_city"),
"State/territory": model.get("converted_state_territory"),
"Request purpose": model.get("purpose"),
"CISA regional representative": model.get("cisa_representative_email"),
"Last submitted date": model.get("last_submitted_date"),
Expand Down
9 changes: 8 additions & 1 deletion src/registrar/views/report_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@

import logging

logger = logging.getLogger(__name__)

# ---Logger
import logging
from venv import logger
from registrar.management.commands.utility.terminal_helper import TerminalColors, TerminalHelper
logger = logging.getLogger(__name__)

class AnalyticsView(View):
def get(self, request):
Expand Down Expand Up @@ -161,7 +165,10 @@ def get(self, request, *args, **kwargs):
class ExportDataTypeUser(View):
"""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):
TerminalHelper.colorful_logger(logger.info, TerminalColors.OKGREEN, f"ExportDataTypeUser -- get")
# match the CSV example with all the fields
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = 'attachment; filename="your-domains.csv"'
Expand Down
Loading