mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-02 17:23:32 +02:00
Merge pull request #2650 from cisagov/backup/2608-portfolio-update-js
Issue #2608: Portfolio admin - dynamic federal type and portfolio type
This commit is contained in:
commit
c19bc89dca
7 changed files with 136 additions and 6 deletions
|
@ -2997,6 +2997,7 @@ class PortfolioAdmin(ListHeaderAdmin):
|
|||
"domain_requests",
|
||||
"suborganizations",
|
||||
"portfolio_type",
|
||||
"creator",
|
||||
]
|
||||
|
||||
def federal_type(self, obj: models.Portfolio):
|
||||
|
|
|
@ -908,10 +908,28 @@ function initializeWidgetOnList(list, parentId) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Determine if any changes are necessary to the display of portfolio type or federal type
|
||||
// based on changes to the Federal Agency
|
||||
let federalPortfolioApi = document.getElementById("federal_and_portfolio_types_from_agency_json_url").value;
|
||||
fetch(`${federalPortfolioApi}?organization_type=${organizationType.value}&agency_name=${selectedText}`)
|
||||
.then(response => {
|
||||
const statusCode = response.status;
|
||||
return response.json().then(data => ({ statusCode, data }));
|
||||
})
|
||||
.then(({ statusCode, data }) => {
|
||||
if (data.error) {
|
||||
console.error("Error in AJAX call: " + data.error);
|
||||
return;
|
||||
}
|
||||
updateReadOnly(data.federal_type, '.field-federal_type');
|
||||
updateReadOnly(data.portfolio_type, '.field-portfolio_type');
|
||||
})
|
||||
.catch(error => console.error("Error fetching federal and portfolio types: ", error));
|
||||
|
||||
// Hide the contactList initially.
|
||||
// If we can update the contact information, it'll be shown again.
|
||||
hideElement(contactList.parentElement);
|
||||
|
||||
|
||||
let seniorOfficialApi = document.getElementById("senior_official_from_agency_json_url").value;
|
||||
fetch(`${seniorOfficialApi}?agency_name=${selectedText}`)
|
||||
.then(response => {
|
||||
|
@ -954,6 +972,7 @@ function initializeWidgetOnList(list, parentId) {
|
|||
}
|
||||
})
|
||||
.catch(error => console.error("Error fetching senior official: ", error));
|
||||
|
||||
}
|
||||
|
||||
function handleStateTerritoryChange(stateTerritory, urbanizationField) {
|
||||
|
@ -965,6 +984,26 @@ function initializeWidgetOnList(list, parentId) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility that selects a div from the DOM using selectorString,
|
||||
* and updates a div within that div which has class of 'readonly'
|
||||
* so that the text of the div is updated to updateText
|
||||
* @param {*} updateText
|
||||
* @param {*} selectorString
|
||||
*/
|
||||
function updateReadOnly(updateText, selectorString) {
|
||||
// find the div by selectorString
|
||||
const selectedDiv = document.querySelector(selectorString);
|
||||
if (selectedDiv) {
|
||||
// find the nested div with class 'readonly' inside the selectorString div
|
||||
const readonlyDiv = selectedDiv.querySelector('.readonly');
|
||||
if (readonlyDiv) {
|
||||
// Update the text content of the readonly div
|
||||
readonlyDiv.textContent = updateText !== null ? updateText : '-';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateContactInfo(data) {
|
||||
if (!contactList) return;
|
||||
|
||||
|
|
|
@ -24,7 +24,10 @@ from registrar.views.report_views import (
|
|||
|
||||
from registrar.views.domain_request import Step
|
||||
from registrar.views.domain_requests_json import get_domain_requests_json
|
||||
from registrar.views.utility.api_views import get_senior_official_from_federal_agency_json
|
||||
from registrar.views.utility.api_views import (
|
||||
get_senior_official_from_federal_agency_json,
|
||||
get_federal_and_portfolio_types_from_federal_agency_json,
|
||||
)
|
||||
from registrar.views.domains_json import get_domains_json
|
||||
from registrar.views.utility import always_404
|
||||
from api.views import available, get_current_federal, get_current_full
|
||||
|
@ -139,6 +142,11 @@ urlpatterns = [
|
|||
get_senior_official_from_federal_agency_json,
|
||||
name="get-senior-official-from-federal-agency-json",
|
||||
),
|
||||
path(
|
||||
"admin/api/get-federal-and-portfolio-types-from-federal-agency-json/",
|
||||
get_federal_and_portfolio_types_from_federal_agency_json,
|
||||
name="get-federal-and-portfolio-types-from-federal-agency-json",
|
||||
),
|
||||
path("admin/", admin.site.urls),
|
||||
path(
|
||||
"reports/export_data_type_user/",
|
||||
|
|
|
@ -131,9 +131,13 @@ class Portfolio(TimeStampedModel):
|
|||
Returns a combination of organization_type / federal_type, seperated by ' - '.
|
||||
If no federal_type is found, we just return the org type.
|
||||
"""
|
||||
org_type_label = self.OrganizationChoices.get_org_label(self.organization_type)
|
||||
agency_type_label = BranchChoices.get_branch_label(self.federal_type)
|
||||
if self.organization_type == self.OrganizationChoices.FEDERAL and agency_type_label:
|
||||
return self.get_portfolio_type(self.organization_type, self.federal_type)
|
||||
|
||||
@classmethod
|
||||
def get_portfolio_type(cls, organization_type, federal_type):
|
||||
org_type_label = cls.OrganizationChoices.get_org_label(organization_type)
|
||||
agency_type_label = BranchChoices.get_branch_label(federal_type)
|
||||
if organization_type == cls.OrganizationChoices.FEDERAL and agency_type_label:
|
||||
return " - ".join([org_type_label, agency_type_label])
|
||||
else:
|
||||
return org_type_label
|
||||
|
@ -141,7 +145,11 @@ class Portfolio(TimeStampedModel):
|
|||
@property
|
||||
def federal_type(self):
|
||||
"""Returns the federal_type value on the underlying federal_agency field"""
|
||||
return self.federal_agency.federal_type if self.federal_agency else None
|
||||
return self.get_federal_type(self.federal_agency)
|
||||
|
||||
@classmethod
|
||||
def get_federal_type(cls, federal_agency):
|
||||
return federal_agency.federal_type if federal_agency else None
|
||||
|
||||
# == Getters for domains == #
|
||||
def get_domains(self):
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
{% comment %} Stores the json endpoint in a url for easier access {% endcomment %}
|
||||
{% url 'get-senior-official-from-federal-agency-json' as url %}
|
||||
<input id="senior_official_from_agency_json_url" class="display-none" value="{{url}}" />
|
||||
{% url 'get-federal-and-portfolio-types-from-federal-agency-json' as url %}
|
||||
<input id="federal_and_portfolio_types_from_agency_json_url" class="display-none" value="{{url}}" />
|
||||
{{ block.super }}
|
||||
{% endblock content %}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ from django.contrib.auth import get_user_model
|
|||
from registrar.tests.common import create_superuser, create_user
|
||||
|
||||
from api.tests.common import less_console_noise_decorator
|
||||
from registrar.utility.constants import BranchChoices
|
||||
|
||||
|
||||
class GetSeniorOfficialJsonTest(TestCase):
|
||||
|
@ -71,3 +72,40 @@ class GetSeniorOfficialJsonTest(TestCase):
|
|||
self.assertEqual(response.status_code, 404)
|
||||
data = response.json()
|
||||
self.assertEqual(data["error"], "Senior Official not found")
|
||||
|
||||
|
||||
class GetFederalPortfolioTypeJsonTest(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
p = "password"
|
||||
self.user = get_user_model().objects.create_user(username="testuser", password=p)
|
||||
|
||||
self.superuser = create_superuser()
|
||||
self.analyst_user = create_user()
|
||||
|
||||
self.agency = FederalAgency.objects.create(agency="Test Agency", federal_type=BranchChoices.JUDICIAL)
|
||||
|
||||
self.api_url = reverse("get-federal-and-portfolio-types-from-federal-agency-json")
|
||||
|
||||
def tearDown(self):
|
||||
User.objects.all().delete()
|
||||
FederalAgency.objects.all().delete()
|
||||
|
||||
@less_console_noise_decorator
|
||||
def test_get_federal_and_portfolio_types_json_authenticated_superuser(self):
|
||||
"""Test that a superuser can fetch the federal and portfolio types."""
|
||||
p = "adminpass"
|
||||
self.client.login(username="superuser", password=p)
|
||||
response = self.client.get(self.api_url, {"agency_name": "Test Agency", "organization_type": "federal"})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
data = response.json()
|
||||
self.assertEqual(data["federal_type"], "Judicial")
|
||||
self.assertEqual(data["portfolio_type"], "Federal - Judicial")
|
||||
|
||||
@less_console_noise_decorator
|
||||
def test_get_federal_and_portfolio_types_json_authenticated_regularuser(self):
|
||||
"""Test that a regular user receives a 403 with an error message."""
|
||||
p = "password"
|
||||
self.client.login(username="testuser", password=p)
|
||||
response = self.client.get(self.api_url, {"agency_name": "Test Agency", "organization_type": "federal"})
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
|
|
@ -5,6 +5,9 @@ from registrar.models import FederalAgency, SeniorOfficial
|
|||
from django.contrib.admin.views.decorators import staff_member_required
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
from registrar.models.portfolio import Portfolio
|
||||
from registrar.utility.constants import BranchChoices
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -34,3 +37,34 @@ def get_senior_official_from_federal_agency_json(request):
|
|||
return JsonResponse(so_dict)
|
||||
else:
|
||||
return JsonResponse({"error": "Senior Official not found"}, status=404)
|
||||
|
||||
|
||||
@login_required
|
||||
@staff_member_required
|
||||
def get_federal_and_portfolio_types_from_federal_agency_json(request):
|
||||
"""Returns specific portfolio information as a JSON. Request must have
|
||||
both agency_name and organization_type."""
|
||||
|
||||
# This API is only accessible to admins and analysts
|
||||
superuser_perm = request.user.has_perm("registrar.full_access_permission")
|
||||
analyst_perm = request.user.has_perm("registrar.analyst_access_permission")
|
||||
if not request.user.is_authenticated or not any([analyst_perm, superuser_perm]):
|
||||
return JsonResponse({"error": "You do not have access to this resource"}, status=403)
|
||||
|
||||
federal_type = None
|
||||
portfolio_type = None
|
||||
|
||||
agency_name = request.GET.get("agency_name")
|
||||
organization_type = request.GET.get("organization_type")
|
||||
agency = FederalAgency.objects.filter(agency=agency_name).first()
|
||||
if agency:
|
||||
federal_type = Portfolio.get_federal_type(agency)
|
||||
portfolio_type = Portfolio.get_portfolio_type(organization_type, federal_type)
|
||||
federal_type = BranchChoices.get_branch_label(federal_type) if federal_type else "-"
|
||||
|
||||
response_data = {
|
||||
"portfolio_type": portfolio_type,
|
||||
"federal_type": federal_type,
|
||||
}
|
||||
|
||||
return JsonResponse(response_data)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue