Convert investigator id to investigator name in descriptive subheader on admin lists, update unit test, linting

This commit is contained in:
rachidatecs 2023-07-06 13:57:27 -04:00
parent 257db0fae5
commit 10b0700657
No known key found for this signature in database
GPG key ID: 3CEBBFA7325E5525
5 changed files with 81 additions and 21 deletions

View file

@ -41,20 +41,48 @@ class ListHeaderAdmin(AuditedAdmin):
return super().changelist_view(request, extra_context=extra_context) return super().changelist_view(request, extra_context=extra_context)
def get_filters(self, request): def get_filters(self, request):
"""Retrieve the current set of parameters being used to filter the table
Returns:
dictionary objects in the format {parameter_name: string,
parameter_value: string}
TODO: convert investigator id to investigator username
"""
filters = [] filters = []
# Retrieve the filter parameters # Retrieve the filter parameters
for param in request.GET.keys(): for param in request.GET.keys():
# Exclude the default search parameter 'q' # Exclude the default search parameter 'q'
if param != "q" and param != "o": if param != "q" and param != "o":
# Append the filter parameter and its value to the list parameter_name = (
filters.append( param.replace("__exact", "")
{ .replace("_type", "")
"parameter_name": param.replace("__exact", "") .replace("__id", " id")
.replace("_type", "")
.replace("__id", " id"),
"parameter_value": request.GET.get(param),
}
) )
if parameter_name == "investigator id":
# Retrieves the corresponding contact from Users
id_value = request.GET.get(param)
try:
contact = models.User.objects.get(id=id_value)
investigator_name = contact.first_name + " " + contact.last_name
filters.append(
{
"parameter_name": "investigator",
"parameter_value": investigator_name,
}
)
except models.User.DoesNotExist:
pass
else:
# For other parameter names, append a dictionary with the original
# parameter_name and the corresponding parameter_value
filters.append(
{
"parameter_name": parameter_name,
"parameter_value": request.GET.get(param),
}
)
return filters return filters
@ -128,6 +156,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
"""Customize the applications listing view.""" """Customize the applications listing view."""
# Columns
list_display = [ list_display = [
"requested_domain", "requested_domain",
"status", "status",
@ -136,7 +165,11 @@ class DomainApplicationAdmin(ListHeaderAdmin):
"submitter", "submitter",
"investigator", "investigator",
] ]
# Filters
list_filter = ("status", "organization_type", "investigator") list_filter = ("status", "organization_type", "investigator")
# Search
search_fields = [ search_fields = [
"requested_domain__name", "requested_domain__name",
"submitter__email", "submitter__email",
@ -144,6 +177,8 @@ class DomainApplicationAdmin(ListHeaderAdmin):
"submitter__last_name", "submitter__last_name",
] ]
search_help_text = "Search by domain or submitter." search_help_text = "Search by domain or submitter."
# Detail view
fieldsets = [ fieldsets = [
(None, {"fields": ["status", "investigator", "creator"]}), (None, {"fields": ["status", "investigator", "creator"]}),
( (
@ -192,6 +227,8 @@ class DomainApplicationAdmin(ListHeaderAdmin):
{"fields": ["is_policy_acknowledged"]}, {"fields": ["is_policy_acknowledged"]},
), ),
] ]
# Read only that we'll leverage for CISA Analysts
readonly_fields = [ readonly_fields = [
"creator", "creator",
"type_of_work", "type_of_work",
@ -240,7 +277,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
def get_readonly_fields(self, request, obj=None): def get_readonly_fields(self, request, obj=None):
if request.user.is_superuser: if request.user.is_superuser:
# Superusers have full access, no fields are read-only # Superusers have full access, no fields are read-only
return () return []
else: else:
# Regular users can only view the specified fields # Regular users can only view the specified fields
return self.readonly_fields return self.readonly_fields

View file

@ -91,11 +91,11 @@ INSTALLED_APPS = [
# vv Required by django.contrib.admin vv # vv Required by django.contrib.admin vv
# the "user" model! *\o/* # the "user" model! *\o/*
"django.contrib.auth", "django.contrib.auth",
# generic interface for Django models # audit logging of changes to models
"auditlog",
# library to simplify form templating
# it needs to be listed before django.contrib.contenttypes # it needs to be listed before django.contrib.contenttypes
# for a ContentType query in fixtures.py # for a ContentType query in fixtures.py
"auditlog",
# generic interface for Django models
"django.contrib.contenttypes", "django.contrib.contenttypes",
# required for CSRF protection and many other things # required for CSRF protection and many other things
"django.contrib.sessions", "django.contrib.sessions",
@ -108,7 +108,7 @@ INSTALLED_APPS = [
"django.contrib.staticfiles", "django.contrib.staticfiles",
# application used for integrating with Login.gov # application used for integrating with Login.gov
"djangooidc", "djangooidc",
# audit logging of changes to models # library to simplify form templating
"widget_tweaks", "widget_tweaks",
# library for Finite State Machine statuses # library for Finite State Machine statuses
"django_fsm", "django_fsm",

View file

@ -65,6 +65,11 @@ class UserFixture:
"first_name": "Rachid-Analyst", "first_name": "Rachid-Analyst",
"last_name": "Mrad-Analyst", "last_name": "Mrad-Analyst",
}, },
{
"username": "b6a15987-5c88-4e26-8de2-ca71a0bdb2cd",
"first_name": "Alysia-Analyst",
"last_name": "Alysia-Analyst",
},
] ]
STAFF_PERMISSIONS = [ STAFF_PERMISSIONS = [
@ -99,7 +104,7 @@ class UserFixture:
logger.debug("User object created for %s" % admin["first_name"]) logger.debug("User object created for %s" % admin["first_name"])
except Exception as e: except Exception as e:
logger.warning(e) logger.warning(e)
logger.debug("All superusers loaded.") logger.info("All superusers loaded.")
logger.info("Going to load %s CISA analysts (staff)" % str(len(cls.STAFF))) logger.info("Going to load %s CISA analysts (staff)" % str(len(cls.STAFF)))
for staff in cls.STAFF: for staff in cls.STAFF:
@ -150,7 +155,7 @@ class UserFixture:
logger.debug("User object created for %s" % staff["first_name"]) logger.debug("User object created for %s" % staff["first_name"])
except Exception as e: except Exception as e:
logger.warning(e) logger.warning(e)
logger.debug("All CISA analysts (staff) loaded.") logger.info("All CISA analysts (staff) loaded.")
class DomainApplicationFixture: class DomainApplicationFixture:

View file

@ -8,7 +8,7 @@ from typing import List, Dict
from django.conf import settings from django.conf import settings
from django.contrib.auth import get_user_model, login from django.contrib.auth import get_user_model, login
from registrar.models import Contact, DraftDomain, Website, DomainApplication from registrar.models import Contact, DraftDomain, Website, DomainApplication, User
def get_handlers(): def get_handlers():
@ -157,3 +157,17 @@ def completed_application(
application.alternative_domains.add(alt) application.alternative_domains.add(alt)
return application return application
def mock_user():
"""A simple user."""
user_kwargs = dict(
id = 4,
first_name = "Rachid",
last_name = "Mrad",
)
user, _ = User.objects.get_or_create(
**user_kwargs
)
return user

View file

@ -2,7 +2,7 @@ from django.test import TestCase, RequestFactory, Client
from django.contrib.admin.sites import AdminSite from django.contrib.admin.sites import AdminSite
from registrar.admin import DomainApplicationAdmin, ListHeaderAdmin from registrar.admin import DomainApplicationAdmin, ListHeaderAdmin
from registrar.models import DomainApplication, DomainInformation, User from registrar.models import DomainApplication, DomainInformation, User
from .common import completed_application from .common import completed_application, mock_user
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.conf import settings from django.conf import settings
@ -183,13 +183,16 @@ class TestDomainApplicationAdmin(TestCase):
# Have to get creative to get past linter # Have to get creative to get past linter
p = "adminpassword" p = "adminpassword"
self.client.login(username="admin", password=p) self.client.login(username="admin", password=p)
# Mock a user
user = mock_user()
# Make the request using the Client class # Make the request using the Client class
# which handles CSRF # which handles CSRF
# Follow=True handles the redirect # Follow=True handles the redirect
response = self.client.get( response = self.client.get(
"/admin/registrar/domainapplication/", "/admin/registrar/domainapplication/",
{"status__exact": "started", "investigator__id__exact": "4", "q": "Hello"}, {"status__exact": "started", "investigator__id__exact": user.id, "q": "Hello"},
follow=True, follow=True,
) )
@ -204,7 +207,7 @@ class TestDomainApplicationAdmin(TestCase):
filters, filters,
[ [
{"parameter_name": "status", "parameter_value": "started"}, {"parameter_name": "status", "parameter_value": "started"},
{"parameter_name": "investigator id", "parameter_value": "4"}, {"parameter_name": "investigator", "parameter_value": user.first_name + " " + user.last_name},
], ],
) )
@ -212,7 +215,7 @@ class TestDomainApplicationAdmin(TestCase):
# Create a mock request object # Create a mock request object
request = self.factory.get("/admin/yourmodel/") request = self.factory.get("/admin/yourmodel/")
# Set the GET parameters for testing # Set the GET parameters for testing
request.GET = {"status": "started", "investigator id": "4", "q": "search_value"} request.GET = {"status": "started", "investigator": "Rachid Mrad", "q": "search_value"}
# Call the get_filters method # Call the get_filters method
filters = self.admin.get_filters(request) filters = self.admin.get_filters(request)
@ -221,11 +224,12 @@ class TestDomainApplicationAdmin(TestCase):
filters, filters,
[ [
{"parameter_name": "status", "parameter_value": "started"}, {"parameter_name": "status", "parameter_value": "started"},
{"parameter_name": "investigator id", "parameter_value": "4"}, {"parameter_name": "investigator", "parameter_value": "Rachid Mrad"},
], ],
) )
def tearDown(self): def tearDown(self):
# delete any applications too # delete any applications too
DomainApplication.objects.all().delete() DomainApplication.objects.all().delete()
User.objects.all().delete()
self.superuser.delete() self.superuser.delete()