11from typing import Any , Optional
22
3- from django .db .models import Exists , QuerySet
3+ from django .db .models import Exists , OuterRef , Q , QuerySet
44from django_filters import CharFilter , FilterSet , NumberFilter , OrderingFilter
55from rest_framework .request import Request
66
1010 Authorization_Group_Member ,
1111 User ,
1212)
13+ from application .authorization .services .roles_permissions import Roles
14+ from application .core .models import (
15+ Product ,
16+ Product_Authorization_Group_Member ,
17+ Product_Member ,
18+ )
19+
20+
21+ def _get_product_and_group_ids (product_id : int ) -> list [int ]:
22+ product = Product .objects .filter (id = product_id ).select_related ("product_group" ).first ()
23+ if product is None :
24+ return []
25+
26+ product_ids = [product .id ]
27+ if product .product_group_id :
28+ product_ids .append (product .product_group_id )
29+ return product_ids
1330
1431
1532class UserFilter (FilterSet ):
@@ -23,6 +40,57 @@ class UserFilter(FilterSet):
2340 exclude_license_group = NumberFilter (field_name = "exclude_license_group" , method = "get_exclude_license_group" )
2441 exclude_license_policy = NumberFilter (field_name = "exclude_license_policy" , method = "get_exclude_license_policy" )
2542 exclude_product = NumberFilter (field_name = "exclude_product" , method = "get_exclude_product" )
43+ member_of_product = NumberFilter (field_name = "member_of_product" , method = "get_member_of_product" )
44+ assessment_approver_for_product = NumberFilter (
45+ field_name = "assessment_approver_for_product" , method = "get_assessment_approver_for_product"
46+ )
47+
48+ def get_member_of_product (
49+ self ,
50+ queryset : QuerySet ,
51+ name : Any , # pylint: disable=unused-argument
52+ value : Any ,
53+ ) -> QuerySet :
54+ # Users who are members of the product - directly or via an authorization group - including
55+ # membership inherited from the product's product group. The "authorization_groups" relation
56+ # is traversed twice: User -> authorization groups -> products the group is assigned to.
57+ if value is not None :
58+ return queryset .filter (
59+ Q (product_members__id = value ) # direct member of the product
60+ | Q (product_members__products__id = value ) # direct member of the product group
61+ | Q (authorization_groups__authorization_groups__id = value ) # via an authorization group of the product
62+ | Q (authorization_groups__authorization_groups__products__id = value ) # via an auth group of the group
63+ ).distinct ()
64+ return queryset
65+
66+ def get_assessment_approver_for_product (
67+ self ,
68+ queryset : QuerySet ,
69+ name : Any , # pylint: disable=unused-argument
70+ value : Any ,
71+ ) -> QuerySet :
72+ product_ids = _get_product_and_group_ids (value )
73+ if not product_ids :
74+ return queryset .none ()
75+
76+ product_members = Product_Member .objects .filter (
77+ product_id__in = product_ids ,
78+ user = OuterRef ("pk" ),
79+ role__gte = Roles .Writer ,
80+ )
81+ product_authorization_group_members = Product_Authorization_Group_Member .objects .filter (
82+ product_id__in = product_ids ,
83+ authorization_group__users = OuterRef ("pk" ),
84+ role__gte = Roles .Writer ,
85+ )
86+ return (
87+ queryset .annotate (
88+ assessment_approver_product_member = Exists (product_members ),
89+ assessment_approver_group_member = Exists (product_authorization_group_members ),
90+ )
91+ .filter (Q (assessment_approver_product_member = True ) | Q (assessment_approver_group_member = True ))
92+ .distinct ()
93+ )
2694
2795 def get_exclude_authorization_group (
2896 self ,
@@ -116,6 +184,43 @@ class AuthorizationGroupFilter(FilterSet):
116184 exclude_license_group = NumberFilter (field_name = "exclude_license_group" , method = "get_exclude_license_group" )
117185 exclude_license_policy = NumberFilter (field_name = "exclude_license_policy" , method = "get_exclude_license_policy" )
118186 exclude_product = NumberFilter (field_name = "exclude_product" , method = "get_exclude_product" )
187+ member_of_product = NumberFilter (field_name = "member_of_product" , method = "get_member_of_product" )
188+ assessment_approver_for_product = NumberFilter (
189+ field_name = "assessment_approver_for_product" , method = "get_assessment_approver_for_product"
190+ )
191+
192+ def get_member_of_product (
193+ self ,
194+ queryset : QuerySet ,
195+ name : Any , # pylint: disable=unused-argument
196+ value : Any ,
197+ ) -> QuerySet :
198+ # Authorization groups assigned to the product, including those inherited from its product group.
199+ if value is not None :
200+ return queryset .filter (
201+ Q (authorization_groups__id = value ) # assigned to the product
202+ | Q (authorization_groups__products__id = value ) # assigned to the product's product group
203+ ).distinct ()
204+ return queryset
205+
206+ def get_assessment_approver_for_product (
207+ self ,
208+ queryset : QuerySet ,
209+ name : Any , # pylint: disable=unused-argument
210+ value : Any ,
211+ ) -> QuerySet :
212+ product_ids = _get_product_and_group_ids (value )
213+ if not product_ids :
214+ return queryset .none ()
215+
216+ product_authorization_group_members = Product_Authorization_Group_Member .objects .filter (
217+ product_id__in = product_ids ,
218+ authorization_group = OuterRef ("pk" ),
219+ role__gte = Roles .Writer ,
220+ )
221+ return queryset .annotate (
222+ assessment_approver_product_authorization_group = Exists (product_authorization_group_members ),
223+ ).filter (assessment_approver_product_authorization_group = True )
119224
120225 def get_exclude_license_group (
121226 self ,
0 commit comments