Just need unit tests!
This commit is contained in:
zandercymatics 2024-11-18 14:17:19 -07:00
parent 1c0f99eedc
commit 5f5bc4f616
No known key found for this signature in database
GPG key ID: FF4636ABEC9682B7
5 changed files with 20 additions and 34 deletions

View file

@ -1,7 +1,7 @@
from django.db.models.expressions import Func from django.db.models.expressions import Func
class ArrayRemove(Func): class ArrayRemoveNull(Func):
"""Custom Func to use array_remove to remove null values""" """Custom Func to use array_remove to remove null values"""
function = "array_remove" function = "array_remove"

View file

@ -8,7 +8,7 @@
<section class="section-outlined members margin-top-0 section-outlined--border-base-light" id="members"> <section class="section-outlined members margin-top-0 section-outlined--border-base-light" id="members">
<div class="section-outlined__header margin-bottom-3 grid-row"> <div class="section-outlined__header margin-bottom-3 grid-row">
<!-- ---------- SEARCH ---------- --> <!-- ---------- SEARCH ---------- -->
<div class="section-outlined__search mobile:grid-col-12 desktop:grid-col-6"> <div class="section-outlined__search mobile:grid-col-12 desktop:grid-col-6 {% if is_widescreen_mode %} section-outlined__search--widescreen {% endif %}">
<section aria-label="Members search component" class="margin-top-2"> <section aria-label="Members search component" class="margin-top-2">
<form class="usa-search usa-search--small" method="POST" role="search"> <form class="usa-search usa-search--small" method="POST" role="search">
{% csrf_token %} {% csrf_token %}

View file

@ -1,4 +1,4 @@
from abc import ABC, abstractmethod from abc import abstractmethod
from collections import defaultdict from collections import defaultdict
import csv import csv
import logging import logging
@ -17,27 +17,19 @@ from django.db.models import (
Count, Count,
DateField, DateField,
F, F,
ManyToManyField,
Q, Q,
QuerySet,
Value, Value,
When, When,
TextField,
OuterRef,
Subquery,
) )
from django.db.models.functions import Cast
from django.utils import timezone from django.utils import timezone
from django.db.models.functions import Concat, Coalesce from django.db.models.functions import Concat, Coalesce
from django.contrib.postgres.aggregates import StringAgg from django.contrib.postgres.aggregates import StringAgg
from registrar.models.user_portfolio_permission import UserPortfolioPermission from registrar.models.user_portfolio_permission import UserPortfolioPermission
from registrar.models.utility.generic_helper import convert_queryset_to_dict from registrar.models.utility.generic_helper import convert_queryset_to_dict
from registrar.models.utility.orm_helper import ArrayRemove from registrar.models.utility.portfolio_helper import UserPortfolioRoleChoices
from registrar.models.utility.portfolio_helper import UserPortfolioRoleChoices, UserPortfolioPermissionChoices
from registrar.templatetags.custom_filters import get_region from registrar.templatetags.custom_filters import get_region
from registrar.utility.constants import BranchChoices from registrar.utility.constants import BranchChoices
from registrar.utility.enums import DefaultEmail from registrar.utility.enums import DefaultEmail
from django.contrib.postgres.aggregates import ArrayAgg
from registrar.utility.model_annotations import ( from registrar.utility.model_annotations import (
BaseModelAnnotation, BaseModelAnnotation,

View file

@ -45,7 +45,7 @@ from django.db.models.functions import Concat, Coalesce, Cast
from registrar.models.user_group import UserGroup from registrar.models.user_group import UserGroup
from registrar.models.user_portfolio_permission import UserPortfolioPermission from registrar.models.user_portfolio_permission import UserPortfolioPermission
from registrar.models.utility.generic_helper import convert_queryset_to_dict from registrar.models.utility.generic_helper import convert_queryset_to_dict
from registrar.models.utility.orm_helper import ArrayRemove from registrar.models.utility.orm_helper import ArrayRemoveNull
from django.contrib.postgres.aggregates import ArrayAgg from django.contrib.postgres.aggregates import ArrayAgg
from django.contrib.admin.models import LogEntry, ADDITION from django.contrib.admin.models import LogEntry, ADDITION
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@ -252,11 +252,13 @@ class UserPortfolioPermissionModelAnnotation(BaseModelAnnotation):
Subquery( Subquery(
PortfolioInvitation.objects.filter( PortfolioInvitation.objects.filter(
status=PortfolioInvitation.PortfolioInvitationStatus.RETRIEVED, status=PortfolioInvitation.PortfolioInvitationStatus.RETRIEVED,
# Double outer ref because we first go into the LogEntry query,
# then into the
email=OuterRef(OuterRef("user__email")), email=OuterRef(OuterRef("user__email")),
portfolio=OuterRef(OuterRef("portfolio")) portfolio=OuterRef(OuterRef("portfolio")),
).values("id")[:1] ).values("id")[:1]
), ),
output_field=TextField() output_field=TextField(),
) )
@classmethod @classmethod
@ -376,22 +378,20 @@ class PortfolioInvitationModelAnnotation(BaseModelAnnotation):
action_flag=ADDITION, action_flag=ADDITION,
) )
.annotate( .annotate(
# Action time will always be equivalent to created_at in this context # Action time will always be equivalent to created_at in this context.
# Using this instead of created_at is a lot simpler and more performant,
# as otherwise a Case and Subquery need to be used.
display_date=Func( display_date=Func(
F("action_time"), F("action_time"), Value("YYYY-MM-DD"), function="to_char", output_field=TextField()
Value("YYYY-MM-DD"),
function="to_char",
output_field=TextField()
) )
) )
.order_by("action_time") .order_by("action_time")
.values("display_date")[:1] .values("display_date")[:1]
), ),
Value("Invalid date"), Value("Invalid date"),
output_field=TextField() output_field=TextField(),
) )
@classmethod @classmethod
def get_invited_by_query(cls, object_id_query): def get_invited_by_query(cls, object_id_query):
"""Returns the user that created the given portfolio invitation. """Returns the user that created the given portfolio invitation.
@ -413,10 +413,10 @@ class PortfolioInvitationModelAnnotation(BaseModelAnnotation):
user=OuterRef("user"), user=OuterRef("user"),
) )
), ),
then=Value("help@get.gov") then=Value("help@get.gov"),
), ),
default=F("user__email"), default=F("user__email"),
output_field=CharField() output_field=CharField(),
) )
) )
.order_by("action_time") .order_by("action_time")
@ -454,7 +454,7 @@ class PortfolioInvitationModelAnnotation(BaseModelAnnotation):
"last_active": Value("Invited", output_field=TextField()), "last_active": Value("Invited", output_field=TextField()),
"additional_permissions_display": F("additional_permissions"), "additional_permissions_display": F("additional_permissions"),
"member_display": F("email"), "member_display": F("email"),
"domain_info": ArrayRemove( "domain_info": ArrayRemoveNull(
ArrayAgg( ArrayAgg(
Subquery(domain_invitations.values("domain_info")), Subquery(domain_invitations.values("domain_info")),
distinct=True, distinct=True,
@ -467,9 +467,7 @@ class PortfolioInvitationModelAnnotation(BaseModelAnnotation):
# TODO - replace this with a "creator" field on portfolio invitation. This should be another ticket. # TODO - replace this with a "creator" field on portfolio invitation. This should be another ticket.
# Grab the invitation creator from the audit log. This will need to be replaced with a creator field. # Grab the invitation creator from the audit log. This will need to be replaced with a creator field.
# When that happens, just replace this with F("invitation__creator") # When that happens, just replace this with F("invitation__creator")
"invited_by": cls.get_invited_by_query( "invited_by": cls.get_invited_by_query(object_id_query=Cast(OuterRef("id"), output_field=TextField())),
object_id_query=Cast(OuterRef("id"), output_field=TextField())
),
} }
@classmethod @classmethod

View file

@ -390,12 +390,8 @@ class PortfolioMembersView(PortfolioMembersPermissionView, View):
context = {} context = {}
if portfolio: if portfolio:
user_count = portfolio.portfolio_users.count() user_count = portfolio.portfolio_users.count()
invitation_count = PortfolioInvitation.objects.filter( invitation_count = PortfolioInvitation.objects.filter(portfolio=portfolio).count()
portfolio=portfolio context.update({"member_count": user_count + invitation_count})
).count()
context.update({
"member_count": user_count + invitation_count
})
return render(request, "portfolio_members.html", context=context) return render(request, "portfolio_members.html", context=context)