77from django .contrib .postgres .fields import JSONField
88from django .core .exceptions import PermissionDenied
99from django .db import models
10- from django .db .models import Case , Count , IntegerField , OuterRef , Subquery , Sum , Q , Prefetch , When
10+ from django .db .models import (
11+ Case ,
12+ Count ,
13+ IntegerField ,
14+ F ,
15+ OuterRef ,
16+ Prefetch ,
17+ Q ,
18+ Subquery ,
19+ Sum ,
20+ When ,
21+ )
1122from django .db .models .expressions import RawSQL , OrderBy
1223from django .db .models .functions import Coalesce
1324from django .dispatch import receiver
2233
2334from opentech .apply .activity .messaging import messenger , MESSAGES
2435from opentech .apply .determinations .models import Determination
25- from opentech .apply .review .models import Review , ReviewOpinion
36+ from opentech .apply .review .models import ReviewOpinion
2637from opentech .apply .review .options import MAYBE , AGREE , DISAGREE
2738from opentech .apply .stream_forms .blocks import UploadableMediaBlock
2839from opentech .apply .stream_forms .files import StreamFieldDataEncoder
2940from opentech .apply .stream_forms .models import BaseStreamForm
3041
3142from .mixins import AccessFormData
3243from .utils import (
44+ COMMUNITY_REVIEWER_GROUP_NAME ,
3345 LIMIT_TO_STAFF ,
3446 LIMIT_TO_REVIEWER_GROUPS ,
3547 LIMIT_TO_PARTNERS ,
48+ PARTNER_GROUP_NAME ,
3649 REVIEW_GROUPS ,
3750 REVIEWER_GROUP_NAME ,
3851 STAFF_GROUP_NAME ,
@@ -167,7 +180,9 @@ def for_table(self, user):
167180 output_field = IntegerField (),
168181 ),
169182 review_submitted_count = Subquery (
170- reviewers .reviewed ().values ('submission' ).annotate (count = Count ('pk' )).values ('count' ),
183+ reviewers .reviewed ().values ('submission' ).annotate (
184+ count = Count ('pk' , distinct = True )
185+ ).values ('count' ),
171186 output_field = IntegerField (),
172187 ),
173188 review_recommendation = Case (
@@ -182,10 +197,18 @@ def for_table(self, user):
182197 role_icon = Subquery (roles_for_review [:1 ].values ('role__icon' )),
183198 ).prefetch_related (
184199 Prefetch (
185- 'reviews' , queryset = Review .objects .select_related ('author' ).prefetch_related (
186- Prefetch ('opinions' , queryset = ReviewOpinion .objects .select_related ('author' ))
187- )
200+ 'assigned' ,
201+ queryset = AssignedReviewers .objects .reviewed ().review_order ().prefetch_related (
202+ Prefetch ('opinions' , queryset = ReviewOpinion .objects .select_related ('author__reviewer' ))
203+ ),
204+ to_attr = 'has_reviewed'
205+ ),
206+ Prefetch (
207+ 'assigned' ,
208+ queryset = AssignedReviewers .objects .not_reviewed ().staff (),
209+ to_attr = 'hasnt_reviewed'
188210 )
211+
189212 ).select_related (
190213 'page' ,
191214 'round' ,
@@ -758,6 +781,43 @@ def get_absolute_url(self):
758781
759782
760783class AssignedReviewersQuerySet (models .QuerySet ):
784+ def review_order (self ):
785+ review_order = [
786+ STAFF_GROUP_NAME ,
787+ PARTNER_GROUP_NAME ,
788+ COMMUNITY_REVIEWER_GROUP_NAME ,
789+ REVIEWER_GROUP_NAME ,
790+ ]
791+
792+ ordering = [
793+ models .When (type__name = review_type , then = models .Value (i ))
794+ for i , review_type in enumerate (review_order )
795+ ]
796+ return self .exclude (
797+ # Remove people from the list who are opinionated but
798+ # didn't review, they appear elsewhere
799+ opinions__isnull = False ,
800+ review__isnull = True ,
801+ ).annotate (
802+ type_order = models .Case (
803+ * ordering ,
804+ output_field = models .IntegerField (),
805+ ),
806+ has_review = models .Case (
807+ models .When (review__isnull = True , then = models .Value (1 )),
808+ models .When (review__is_draft = True , then = models .Value (1 )),
809+ default = models .Value (0 ),
810+ output_field = models .IntegerField (),
811+ )
812+ ).order_by (
813+ 'type_order' ,
814+ 'has_review' ,
815+ F ('role__order' ).asc (nulls_last = True ),
816+ ).select_related (
817+ 'reviewer' ,
818+ 'role' ,
819+ )
820+
761821 def with_roles (self ):
762822 return self .filter (role__isnull = False )
763823
@@ -766,13 +826,14 @@ def without_roles(self):
766826
767827 def reviewed (self ):
768828 return self .filter (
769- Q (opinions__isnull = False ) | Q (Q (review__isnull = False ) & Q (review__is_draft = False ))
829+ Q (opinions__opinion = AGREE ) |
830+ Q (Q (review__isnull = False ) & Q (review__is_draft = False ))
770831 ).distinct ()
771832
772833 def not_reviewed (self ):
773834 return self .filter (
774835 Q (review__isnull = True ) | Q (review__is_draft = True ),
775- opinions__isnull = True ,
836+ Q ( opinions__isnull = True ) | Q ( opinions__opinion = DISAGREE ) ,
776837 ).distinct ()
777838
778839 def never_tried_to_review (self ):
0 commit comments