diff --git a/src/registrar/utility/csv_export.py b/src/registrar/utility/csv_export.py index ea56f9b27..598395b54 100644 --- a/src/registrar/utility/csv_export.py +++ b/src/registrar/utility/csv_export.py @@ -1,12 +1,19 @@ -import csv -import logging from abc import ABC, abstractmethod from collections import defaultdict +import csv +import logging from datetime import datetime - -from django.contrib.admin.models import LogEntry, ADDITION -from django.contrib.contenttypes.models import ContentType -from django.contrib.postgres.aggregates import ArrayAgg, StringAgg +from registrar.models import ( + Domain, + DomainInvitation, + DomainRequest, + DomainInformation, + PublicContact, + UserDomainRole, + PortfolioInvitation, + UserGroup, + UserPortfolioPermission, +) from django.db.models import ( Case, CharField, @@ -22,21 +29,13 @@ from django.db.models import ( OuterRef, Subquery, Exists, + Func, ) -from django.db.models.functions import Concat, Coalesce, Cast, Func from django.utils import timezone - -from registrar.models import ( - Domain, - DomainInvitation, - DomainRequest, - DomainInformation, - PublicContact, - UserDomainRole, - PortfolioInvitation, - UserGroup, -) -from registrar.models.user_portfolio_permission import UserPortfolioPermission +from django.db.models.functions import Concat, Coalesce, Cast +from django.contrib.postgres.aggregates import ArrayAgg, StringAgg +from django.contrib.admin.models import LogEntry, ADDITION +from django.contrib.contenttypes.models import ContentType from registrar.models.utility.generic_helper import convert_queryset_to_dict from registrar.models.utility.orm_helper import ArrayRemoveNull from registrar.models.utility.portfolio_helper import UserPortfolioRoleChoices @@ -76,7 +75,7 @@ def format_end_date(end_date): class BaseExport(ABC): """ A generic class for exporting data which returns a csv file for the given model. - 2nd class in an inheritance tree of 4. + Base class in an inheritance tree of 3. """ @classmethod @@ -139,7 +138,7 @@ class BaseExport(ABC): return Q() @classmethod - def get_annotated_fields(cls, **kwargs): + def get_computed_fields(cls, **kwargs): """ Get a dict of computed fields. These are fields that do not exist on the model normally and will be passed to .annotate() when building a queryset. @@ -244,7 +243,7 @@ class BaseExport(ABC): exclusions = cls.get_exclusions() annotations_for_sort = cls.get_annotations_for_sort() filter_conditions = cls.get_filter_conditions(**kwargs) - annotated_fields = cls.get_annotated_fields(**kwargs) + annotated_fields = cls.get_computed_fields(**kwargs) related_table_fields = cls.get_related_table_fields() model_queryset = ( @@ -783,7 +782,7 @@ class DomainDataType(DomainExport): return ["domain__permissions"] @classmethod - def get_annotated_fields(cls, delimiter=", ", **kwargs): + def get_computed_fields(cls, delimiter=", ", **kwargs): """ Get a dict of computed fields. """ @@ -1000,7 +999,7 @@ class DomainDataFull(DomainExport): ) @classmethod - def get_annotated_fields(cls, delimiter=", ", **kwargs): + def get_computed_fields(cls, delimiter=", ", **kwargs): """ Get a dict of computed fields. """ @@ -1095,7 +1094,7 @@ class DomainDataFederal(DomainExport): ) @classmethod - def get_annotated_fields(cls, delimiter=", ", **kwargs): + def get_computed_fields(cls, delimiter=", ", **kwargs): """ Get a dict of computed fields. """ @@ -1729,7 +1728,7 @@ class DomainRequestDataFull(DomainRequestExport): ] @classmethod - def get_annotated_fields(cls, delimiter=", ", **kwargs): + def get_computed_fields(cls, delimiter=", ", **kwargs): """ Get a dict of computed fields. """ diff --git a/src/registrar/views/portfolio_members_json.py b/src/registrar/views/portfolio_members_json.py index 8e5ee902f..232ca2e6c 100644 --- a/src/registrar/views/portfolio_members_json.py +++ b/src/registrar/views/portfolio_members_json.py @@ -1,32 +1,19 @@ -from django.contrib.postgres.aggregates import ArrayAgg +from django.http import JsonResponse from django.core.paginator import Paginator -from django.db.models import ( - Case, - CharField, - F, - Q, - TextField, - Value, - When, - OuterRef, - Subquery, -) +from django.db.models import Value, F, CharField, TextField, Q, Case, When, OuterRef, Subquery from django.db.models.expressions import Func from django.db.models.functions import Cast, Coalesce, Concat -from django.http import JsonResponse +from django.contrib.postgres.aggregates import ArrayAgg from django.urls import reverse from django.views import View -from registrar.models import ( - DomainInvitation, - PortfolioInvitation, - UserPortfolioPermission, -) -from registrar.models.utility.portfolio_helper import ( - UserPortfolioPermissionChoices, - UserPortfolioRoleChoices, -) +from registrar.models.domain_invitation import DomainInvitation +from registrar.models.portfolio_invitation import PortfolioInvitation +from registrar.models.user_portfolio_permission import UserPortfolioPermission +from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices from registrar.views.utility.mixins import PortfolioMembersPermission +from registrar.models.utility.orm_helper import ArrayRemoveNull + class PortfolioMembersJson(PortfolioMembersPermission, View): @@ -53,7 +40,7 @@ class PortfolioMembersJson(PortfolioMembersPermission, View): page_number = request.GET.get("page", 1) page_obj = paginator.get_page(page_number) - members = [self.serialize_members(portfolio, item, request.user) for item in page_obj.object_list] + members = [self.serialize_members(request, portfolio, item, request.user) for item in page_obj.object_list] return JsonResponse( { @@ -148,7 +135,7 @@ class PortfolioMembersJson(PortfolioMembersPermission, View): additional_permissions_display=F("additional_permissions"), member_display=F("email"), # Use ArrayRemove to return an empty list when no domain invitations are found - domain_info=ArrayRemove( + domain_info=ArrayRemoveNull( ArrayAgg( Subquery(domain_invitations.values("domain_info")), distinct=True, @@ -193,7 +180,7 @@ class PortfolioMembersJson(PortfolioMembersPermission, View): queryset = queryset.order_by(sort_by) return queryset - def serialize_members(self, portfolio, item, user): + def serialize_members(self, request, portfolio, item, user): # Check if the user can edit other users user_can_edit_other_users = any( user.has_perm(perm) for perm in ["registrar.full_access_permission", "registrar.change_user"] @@ -228,8 +215,3 @@ class PortfolioMembersJson(PortfolioMembersPermission, View): } return member_json - -# Custom Func to use array_remove to remove null values -class ArrayRemove(Func): - function = "array_remove" - template = "%(function)s(%(expressions)s, NULL)"