Merge pull request #1997 from cisagov/dk/1850-request-user-details

Issue #1850 : Indicate whether creator has other domains
This commit is contained in:
dave-kennedy-ecs 2024-04-12 13:20:53 -04:00 committed by GitHub
commit 64d0ec3f93
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 180 additions and 2 deletions

View file

@ -536,6 +536,18 @@ address.dja-address-contact-list {
} }
} }
.dja-status-list {
border-top: solid 1px var(--border-color);
margin-left: 0 !important;
padding-left: 0 !important;
padding-top: 10px;
li {
line-height: 1.5;
font-family: "Source Sans Pro Web", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif !important;
padding-top: 0;
padding-bottom: 0;
}
}
// Make the clipboard button "float" inside of the input box // Make the clipboard button "float" inside of the input box
.admin-icon-group { .admin-icon-group {

View file

@ -94,6 +94,9 @@ class Contact(TimeStampedModel):
names = [n for n in [self.first_name, self.middle_name, self.last_name] if n] names = [n for n in [self.first_name, self.middle_name, self.last_name] if n]
return " ".join(names) if names else "Unknown" return " ".join(names) if names else "Unknown"
def has_contact_info(self):
return bool(self.title or self.email or self.phone)
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
# Call the parent class's save method to perform the actual save # Call the parent class's save method to perform the actual save
super().save(*args, **kwargs) super().save(*args, **kwargs)

View file

@ -9,6 +9,7 @@ from .domain_invitation import DomainInvitation
from .transition_domain import TransitionDomain from .transition_domain import TransitionDomain
from .verified_by_staff import VerifiedByStaff from .verified_by_staff import VerifiedByStaff
from .domain import Domain from .domain import Domain
from .domain_request import DomainRequest
from phonenumber_field.modelfields import PhoneNumberField # type: ignore from phonenumber_field.modelfields import PhoneNumberField # type: ignore
@ -67,6 +68,33 @@ class User(AbstractUser):
def is_restricted(self): def is_restricted(self):
return self.status == self.RESTRICTED return self.status == self.RESTRICTED
def get_approved_domains_count(self):
"""Return count of approved domains"""
allowed_states = [Domain.State.UNKNOWN, Domain.State.DNS_NEEDED, Domain.State.READY, Domain.State.ON_HOLD]
approved_domains_count = self.domains.filter(state__in=allowed_states).count()
return approved_domains_count
def get_active_requests_count(self):
"""Return count of active requests"""
allowed_states = [
DomainRequest.DomainRequestStatus.SUBMITTED,
DomainRequest.DomainRequestStatus.IN_REVIEW,
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
]
active_requests_count = self.domain_requests_created.filter(status__in=allowed_states).count()
return active_requests_count
def get_rejected_requests_count(self):
"""Return count of rejected requests"""
return self.domain_requests_created.filter(status=DomainRequest.DomainRequestStatus.REJECTED).count()
def get_ineligible_requests_count(self):
"""Return count of ineligible requests"""
return self.domain_requests_created.filter(status=DomainRequest.DomainRequestStatus.INELIGIBLE).count()
def has_contact_info(self):
return bool(self.contact.title or self.contact.email or self.contact.phone)
@classmethod @classmethod
def needs_identity_verification(cls, email, uuid): def needs_identity_verification(cls, email, uuid):
"""A method used by our oidc classes to test whether a user needs email/uuid verification """A method used by our oidc classes to test whether a user needs email/uuid verification

View file

@ -1,6 +1,6 @@
{% load i18n static %} {% load i18n static %}
<address class="{% if no_title_top_padding %}margin-top-neg-1__detail-list{% endif %} dja-address-contact-list"> <address class="{% if no_title_top_padding %}margin-top-neg-1__detail-list{% endif %} {% if user.has_contact_info %}margin-bottom-1{% endif %} dja-address-contact-list">
{% if show_formatted_name %} {% if show_formatted_name %}
{% if contact.get_formatted_name %} {% if contact.get_formatted_name %}
@ -10,7 +10,7 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if user.title or user.contact.title or user.email or user.contact.email or user.phone or user.contact.phone %} {% if user.has_contact_info %}
{# Title #} {# Title #}
{% if user.title or user.contact.title %} {% if user.title or user.contact.title %}
{% if user.contact.title %} {% if user.contact.title %}

View file

@ -71,6 +71,10 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
<label aria-label="Creator contact details"></label> <label aria-label="Creator contact details"></label>
{% include "django/admin/includes/contact_detail_list.html" with user=original.creator no_title_top_padding=field.is_readonly %} {% include "django/admin/includes/contact_detail_list.html" with user=original.creator no_title_top_padding=field.is_readonly %}
</div> </div>
<div class="flex-container">
<label aria-label="User summary details"></label>
{% include "django/admin/includes/user_detail_list.html" with user=original.creator no_title_top_padding=field.is_readonly %}
</div>
{% elif field.field.name == "submitter" %} {% elif field.field.name == "submitter" %}
<div class="flex-container"> <div class="flex-container">
<label aria-label="Submitter contact details"></label> <label aria-label="Submitter contact details"></label>

View file

@ -0,0 +1,30 @@
{% load i18n static %}
{% with approved_domains_count=user.get_approved_domains_count %}
{% with active_requests_count=user.get_active_requests_count %}
{% with rejected_requests_count=user.get_rejected_requests_count %}
{% with ineligible_requests_count=user.get_ineligible_requests_count %}
{% if approved_domains_count|add:active_requests_count|add:rejected_requests_count|add:ineligible_requests_count > 0 %}
<ul class="dja-status-list">
{% if approved_domains_count > 0 %}
{# Approved domains #}
<li>Approved domains: {{ approved_domains_count }}</li>
{% endif %}
{% if active_requests_count > 0 %}
{# Active requests #}
<li>Active requests: {{ active_requests_count }}</li>
{% endif %}
{% if rejected_requests_count > 0 %}
{# Rejected requests #}
<li>Rejected requests: {{ rejected_requests_count }}</li>
{% endif %}
{% if ineligible_requests_count > 0 %}
{# Ineligible requests #}
<li>Ineligible requests: {{ ineligible_requests_count }}</li>
{% endif %}
</ul>
{% endif %}
{% endwith %}
{% endwith %}
{% endwith %}
{% endwith %}

View file

@ -1676,6 +1676,10 @@ class TestDomainRequestAdmin(MockEppLib):
# Test for the copy link # Test for the copy link
self.assertContains(response, "usa-button__clipboard", count=4) self.assertContains(response, "usa-button__clipboard", count=4)
# Test that Creator counts display properly
self.assertNotContains(response, "Approved domains")
self.assertContains(response, "Active requests")
def test_save_model_sets_restricted_status_on_user(self): def test_save_model_sets_restricted_status_on_user(self):
with less_console_noise(): with less_console_noise():
# make sure there is no user with this email # make sure there is no user with this email

View file

@ -1004,6 +1004,8 @@ class TestUser(TestCase):
Domain.objects.all().delete() Domain.objects.all().delete()
DomainInvitation.objects.all().delete() DomainInvitation.objects.all().delete()
DomainInformation.objects.all().delete() DomainInformation.objects.all().delete()
DomainRequest.objects.all().delete()
DraftDomain.objects.all().delete()
TransitionDomain.objects.all().delete() TransitionDomain.objects.all().delete()
User.objects.all().delete() User.objects.all().delete()
UserDomainRole.objects.all().delete() UserDomainRole.objects.all().delete()
@ -1060,6 +1062,91 @@ class TestUser(TestCase):
# Domain Invitation, then save routine should be called exactly once # Domain Invitation, then save routine should be called exactly once
save_mock.assert_called_once() save_mock.assert_called_once()
def test_approved_domains_count(self):
"""Test that the correct approved domain count is returned for a user"""
# with no associated approved domains, expect this to return 0
self.assertEquals(self.user.get_approved_domains_count(), 0)
# with one approved domain, expect this to return 1
UserDomainRole.objects.get_or_create(user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER)
self.assertEquals(self.user.get_approved_domains_count(), 1)
# with one approved domain, expect this to return 1 (domain2 is deleted, so not considered approved)
domain2, _ = Domain.objects.get_or_create(name="igorville2.gov", state=Domain.State.DELETED)
UserDomainRole.objects.get_or_create(user=self.user, domain=domain2, role=UserDomainRole.Roles.MANAGER)
self.assertEquals(self.user.get_approved_domains_count(), 1)
# with two approved domains, expect this to return 2
domain3, _ = Domain.objects.get_or_create(name="igorville3.gov", state=Domain.State.DNS_NEEDED)
UserDomainRole.objects.get_or_create(user=self.user, domain=domain3, role=UserDomainRole.Roles.MANAGER)
self.assertEquals(self.user.get_approved_domains_count(), 2)
# with three approved domains, expect this to return 3
domain4, _ = Domain.objects.get_or_create(name="igorville4.gov", state=Domain.State.ON_HOLD)
UserDomainRole.objects.get_or_create(user=self.user, domain=domain4, role=UserDomainRole.Roles.MANAGER)
self.assertEquals(self.user.get_approved_domains_count(), 3)
# with four approved domains, expect this to return 4
domain5, _ = Domain.objects.get_or_create(name="igorville5.gov", state=Domain.State.READY)
UserDomainRole.objects.get_or_create(user=self.user, domain=domain5, role=UserDomainRole.Roles.MANAGER)
self.assertEquals(self.user.get_approved_domains_count(), 4)
def test_active_requests_count(self):
"""Test that the correct active domain requests count is returned for a user"""
# with no associated active requests, expect this to return 0
self.assertEquals(self.user.get_active_requests_count(), 0)
# with one active request, expect this to return 1
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville1.gov")
DomainRequest.objects.create(
creator=self.user, requested_domain=draft_domain, status=DomainRequest.DomainRequestStatus.SUBMITTED
)
self.assertEquals(self.user.get_active_requests_count(), 1)
# with two active requests, expect this to return 2
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville2.gov")
DomainRequest.objects.create(
creator=self.user, requested_domain=draft_domain, status=DomainRequest.DomainRequestStatus.IN_REVIEW
)
self.assertEquals(self.user.get_active_requests_count(), 2)
# with three active requests, expect this to return 3
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville3.gov")
DomainRequest.objects.create(
creator=self.user, requested_domain=draft_domain, status=DomainRequest.DomainRequestStatus.ACTION_NEEDED
)
self.assertEquals(self.user.get_active_requests_count(), 3)
# with three active requests, expect this to return 3 (STARTED is not considered active)
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville4.gov")
DomainRequest.objects.create(
creator=self.user, requested_domain=draft_domain, status=DomainRequest.DomainRequestStatus.STARTED
)
self.assertEquals(self.user.get_active_requests_count(), 3)
def test_rejected_requests_count(self):
"""Test that the correct rejected domain requests count is returned for a user"""
# with no associated rejected requests, expect this to return 0
self.assertEquals(self.user.get_rejected_requests_count(), 0)
# with one rejected request, expect this to return 1
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville1.gov")
DomainRequest.objects.create(
creator=self.user, requested_domain=draft_domain, status=DomainRequest.DomainRequestStatus.REJECTED
)
self.assertEquals(self.user.get_rejected_requests_count(), 1)
def test_ineligible_requests_count(self):
"""Test that the correct ineligible domain requests count is returned for a user"""
# with no associated ineligible requests, expect this to return 0
self.assertEquals(self.user.get_ineligible_requests_count(), 0)
# with one ineligible request, expect this to return 1
draft_domain, _ = DraftDomain.objects.get_or_create(name="igorville1.gov")
DomainRequest.objects.create(
creator=self.user, requested_domain=draft_domain, status=DomainRequest.DomainRequestStatus.INELIGIBLE
)
self.assertEquals(self.user.get_ineligible_requests_count(), 1)
def test_has_contact_info(self):
"""Test that has_contact_info properly returns"""
# test with a user with contact info defined
self.assertTrue(self.user.has_contact_info())
# test with a user without contact info defined
self.user.contact.title = None
self.user.contact.email = None
self.user.contact.phone = None
self.assertFalse(self.user.has_contact_info())
class TestContact(TestCase): class TestContact(TestCase):
def setUp(self): def setUp(self):
@ -1162,6 +1249,16 @@ class TestContact(TestCase):
self.assertFalse(self.contact_as_ao.has_more_than_one_join("authorizing_official")) self.assertFalse(self.contact_as_ao.has_more_than_one_join("authorizing_official"))
self.assertTrue(self.contact_as_ao.has_more_than_one_join("submitted_domain_requests")) self.assertTrue(self.contact_as_ao.has_more_than_one_join("submitted_domain_requests"))
def test_has_contact_info(self):
"""Test that has_contact_info properly returns"""
# test with a contact with contact info defined
self.assertTrue(self.contact.has_contact_info())
# test with a contact without contact info defined
self.contact.title = None
self.contact.email = None
self.contact.phone = None
self.assertFalse(self.contact.has_contact_info())
class TestDomainRequestCustomSave(TestCase): class TestDomainRequestCustomSave(TestCase):
"""Tests custom save behaviour on the DomainRequest object""" """Tests custom save behaviour on the DomainRequest object"""