Skip to content

Commit

Permalink
Merge pull request #3005 from cisagov/ad/2852-property-to-portfolio-r…
Browse files Browse the repository at this point in the history
…elated-fields

#2852: property to portfolio related fields - [AD]
  • Loading branch information
asaki222 authored Nov 7, 2024
2 parents ee3b568 + 5e9b9a7 commit 8a973a6
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 21 deletions.
107 changes: 98 additions & 9 deletions src/registrar/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1640,6 +1640,70 @@ class StatusListFilter(MultipleChoiceListFilter):
def lookups(self, request, model_admin):
return DomainRequest.DomainRequestStatus.choices

class GenericOrgFilter(admin.SimpleListFilter):
"""Custom Generic Organization filter that accomodates portfolio feature.
If we have a portfolio, use the portfolio's organization. If not, use the
organization in the Domain Request object."""

title = "generic organization"
parameter_name = "converted_generic_orgs"

def lookups(self, request, model_admin):
converted_generic_orgs = set()

for domain_request in DomainRequest.objects.all():
converted_generic_org = domain_request.converted_generic_org_type
if converted_generic_org:
converted_generic_orgs.add(converted_generic_org)

return sorted((org, org) for org in converted_generic_orgs)

# Filter queryset
def queryset(self, request, queryset):
if self.value(): # Check if a generic org is selected in the filter
return queryset.filter(
# Filter based on the generic org value returned by converted_generic_org_type
id__in=[
domain_request.id
for domain_request in queryset
if domain_request.converted_generic_org_type
and domain_request.converted_generic_org_type == self.value()
]
)
return queryset

class FederalTypeFilter(admin.SimpleListFilter):
"""Custom Federal Type filter that accomodates portfolio feature.
If we have a portfolio, use the portfolio's federal type. If not, use the
organization in the Domain Request object."""

title = "federal Type"
parameter_name = "converted_federal_types"

def lookups(self, request, model_admin):
converted_federal_types = set()

for domain_request in DomainRequest.objects.all():
converted_federal_type = domain_request.converted_federal_type
if converted_federal_type:
converted_federal_types.add(converted_federal_type)

return sorted((type, type) for type in converted_federal_types)

# Filter queryset
def queryset(self, request, queryset):
if self.value(): # Check if federal Type is selected in the filter
return queryset.filter(
# Filter based on the federal type returned by converted_federal_type
id__in=[
domain_request.id
for domain_request in queryset
if domain_request.converted_federal_type
and domain_request.converted_federal_type == self.value()
]
)
return queryset

class InvestigatorFilter(admin.SimpleListFilter):
"""Custom investigator filter that only displays users with the manager role"""

Expand Down Expand Up @@ -1700,20 +1764,44 @@ def queryset(self, request, queryset):
if self.value() == "0":
return queryset.filter(Q(is_election_board=False) | Q(is_election_board=None))

@admin.display(description=_("Generic Org Type"))
def converted_generic_org_type(self, obj):
return obj.converted_generic_org_type

@admin.display(description=_("Organization Name"))
def converted_organization_name(self, obj):
return obj.converted_organization_name

@admin.display(description=_("Federal Agency"))
def converted_federal_agency(self, obj):
return obj.converted_federal_agency

@admin.display(description=_("Federal Type"))
def converted_federal_type(self, obj):
return obj.converted_federal_type

@admin.display(description=_("City"))
def converted_city(self, obj):
return obj.converted_city

@admin.display(description=_("State/Territory"))
def converted_state_territory(self, obj):
return obj.converted_state_territory

# Columns
list_display = [
"requested_domain",
"first_submitted_date",
"last_submitted_date",
"last_status_update",
"status",
"generic_org_type",
"federal_type",
"federal_agency",
"organization_name",
"custom_election_board",
"city",
"state_territory",
"converted_generic_org_type",
"converted_organization_name",
"converted_federal_agency",
"converted_federal_type",
"converted_city",
"converted_state_territory",
"investigator",
]

Expand All @@ -1738,8 +1826,8 @@ def status_history(self, obj):
# Filters
list_filter = (
StatusListFilter,
"generic_org_type",
"federal_type",
GenericOrgFilter,
FederalTypeFilter,
ElectionOfficeFilter,
"rejection_reason",
InvestigatorFilter,
Expand Down Expand Up @@ -1869,15 +1957,16 @@ def status_history(self, obj):
"suborganization_city",
"suborganization_state_territory",
]

autocomplete_fields = [
"approved_domain",
"requested_domain",
"creator",
"senior_official",
"investigator",
"portfolio",
"sub_organization",
]

filter_horizontal = ("current_websites", "alternative_domains", "other_contacts")

# Table ordering
Expand Down
45 changes: 45 additions & 0 deletions src/registrar/models/domain_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -1409,3 +1409,48 @@ def _form_complete(self, request):
if not is_complete or not self._is_general_form_complete(request):
return False
return True

"""The following converted_ property methods get field data from this domain request's portfolio,
if there is an associated portfolio. If not, they return data from the domain request model."""

@property
def converted_organization_name(self):
if self.portfolio:
return self.portfolio.organization_name
return self.organization_name

@property
def converted_generic_org_type(self):
if self.portfolio:
return self.portfolio.organization_type
return self.generic_org_type

@property
def converted_federal_agency(self):
if self.portfolio:
return self.portfolio.federal_agency
return self.federal_agency

@property
def converted_federal_type(self):
if self.portfolio:
return self.portfolio.federal_type
return self.federal_type

@property
def converted_city(self):
if self.portfolio:
return self.portfolio.city
return self.city

@property
def converted_state_territory(self):
if self.portfolio:
return self.portfolio.state_territory
return self.state_territory

@property
def converted_senior_official(self):
if self.portfolio:
return self.portfolio.senior_official
return self.senior_official
8 changes: 4 additions & 4 deletions src/registrar/tests/test_admin_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,9 +576,9 @@ def test_short_org_name_in_domain_requests_list(self):
response = self.client.get("/admin/registrar/domainrequest/?generic_org_type__exact=federal")
# There are 2 template references to Federal (4) and two in the results data
# of the request
self.assertContains(response, "Federal", count=52)
self.assertContains(response, "Federal", count=51)
# This may be a bit more robust
self.assertContains(response, '<td class="field-generic_org_type">Federal</td>', count=1)
self.assertContains(response, '<td class="field-converted_generic_org_type">federal</td>', count=1)
# Now let's make sure the long description does not exist
self.assertNotContains(response, "Federal: an agency of the U.S. government")

Expand Down Expand Up @@ -1935,8 +1935,8 @@ def test_has_correct_filters(self):
readonly_fields = self.admin.get_list_filter(request)
expected_fields = (
DomainRequestAdmin.StatusListFilter,
"generic_org_type",
"federal_type",
DomainRequestAdmin.GenericOrgFilter,
DomainRequestAdmin.FederalTypeFilter,
DomainRequestAdmin.ElectionOfficeFilter,
"rejection_reason",
DomainRequestAdmin.InvestigatorFilter,
Expand Down
25 changes: 25 additions & 0 deletions src/registrar/tests/test_models_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
DraftDomain,
FederalAgency,
AllowedEmail,
Portfolio,
)

import boto3_mocking
Expand Down Expand Up @@ -95,6 +96,7 @@ def tearDown(self):
DomainRequest.objects.all().delete()
DraftDomain.objects.all().delete()
Domain.objects.all().delete()
Portfolio.objects.all().delete()
User.objects.all().delete()
self.mock_client.EMAILS_SENT.clear()

Expand Down Expand Up @@ -1045,3 +1047,26 @@ def test_has_other_contacts_returns_false(self):
status=DomainRequest.DomainRequestStatus.STARTED, name="no-others.gov", has_other_contacts=False
)
self.assertEquals(domain_request.has_other_contacts(), False)

@less_console_noise_decorator
def test_converted_type(self):
"""test that new property fields works as expected to pull domain req info such as fed agency,
generic org type, and others from portfolio"""
fed_agency = FederalAgency.objects.filter(agency="Non-Federal Agency").first()
portfolio = Portfolio.objects.create(
organization_name="Test Portfolio",
creator=self.dummy_user_2,
federal_agency=fed_agency,
organization_type=DomainRequest.OrganizationChoices.FEDERAL,
)

domain_request = completed_domain_request(name="domainre1.gov", portfolio=portfolio)

self.assertEqual(portfolio.organization_type, domain_request.converted_generic_org_type)
self.assertEqual(portfolio.federal_agency, domain_request.converted_federal_agency)

domain_request2 = completed_domain_request(
name="domainreq2.gov", federal_agency=fed_agency, generic_org_type=DomainRequest.OrganizationChoices.TRIBAL
)
self.assertEqual(domain_request2.generic_org_type, domain_request2.converted_generic_org_type)
self.assertEqual(domain_request2.federal_agency, domain_request2.converted_federal_agency)
1 change: 1 addition & 0 deletions src/registrar/tests/test_reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,7 @@ def test_domain_request_growth(self):
csv_file.seek(0)
# Read the content into a variable
csv_content = csv_file.read()

expected_content = (
"Domain request,Domain type,Federal type\n"
"city3.gov,Federal,Executive\n"
Expand Down
16 changes: 8 additions & 8 deletions src/registrar/utility/csv_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -660,17 +660,17 @@ 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, "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, "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.creator, "first_name", None)),
cls.safe_get(getattr(request.creator, "last_name", None)),
cls.safe_get(getattr(request.creator, "email", None)),
cls.safe_get(getattr(request, "organization_name", None)),
cls.safe_get(getattr(request, "city", None)),
cls.safe_get(getattr(request, "state_territory", None)),
cls.safe_get(getattr(request, "converted_organization_name", None)),
cls.safe_get(getattr(request, "converted_city", None)),
cls.safe_get(getattr(request, "converted_state_territory", None)),
cls.safe_get(getattr(request, "purpose", None)),
cls.safe_get(getattr(request, "cisa_representative_email", None)),
cls.safe_get(getattr(request, "last_submitted_date", None)),
Expand Down

0 comments on commit 8a973a6

Please sign in to comment.