From 3312ca427104c646e8ef639f1e3058a76b1a3396 Mon Sep 17 00:00:00 2001
From: Hangzhi Yu <hangzhi@protonmail.com>
Date: Mon, 13 Jan 2025 12:06:32 +0100
Subject: [PATCH] Use annotation mechanism to make statistics page permission
 checking faster

---
 aleksis/apps/alsijil/schema/__init__.py       | 122 +++++++++++++++++-
 .../alsijil/schema/participation_status.py    |   4 +
 aleksis/apps/alsijil/schema/personal_note.py  |   4 +
 3 files changed, 127 insertions(+), 3 deletions(-)

diff --git a/aleksis/apps/alsijil/schema/__init__.py b/aleksis/apps/alsijil/schema/__init__.py
index f337be4f..4c026343 100644
--- a/aleksis/apps/alsijil/schema/__init__.py
+++ b/aleksis/apps/alsijil/schema/__init__.py
@@ -1,6 +1,6 @@
 from datetime import datetime
 
-from django.db.models import BooleanField, ExpressionWrapper, Q
+from django.db.models import BooleanField, ExpressionWrapper, F, Q, Value
 
 import graphene
 import graphene_django_optimizer
@@ -308,13 +308,71 @@ class Query(graphene.ObjectType):
         if not info.context.user.has_perm("alsijil.view_person_statistics_rule", person):
             return []
         school_term = get_active_school_term(info.context)
+        global_perm = info.context.user.has_perm("alsijil.change_participationstatus")
         return graphene_django_optimizer.query(
             ParticipationStatus.objects.filter(
                 Q(absence_reason__isnull=False) | Q(tardiness__isnull=False),
                 person=person,
                 datetime_start__date__gte=school_term.date_start,
                 datetime_end__date__lte=school_term.date_end,
-            ).order_by("-related_documentation__datetime_start"),
+            )
+            .annotate(
+                can_edit=ExpressionWrapper(
+                    Value(global_perm)
+                    or ExpressionWrapper(
+                        Q(related_documentation__isnull=False)
+                        & (
+                            Q(related_documentation__amends__isnull=False)
+                            | Q(related_documentation__amends__cancelled=False)
+                        )
+                        & (
+                            Q(related_documentation__teachers__contains=info.context.user.person)
+                            | (
+                                Q(related_documentation__amends__teachers__isnull=False)
+                                & Q(
+                                    related_documentation__amends__teachers__contains=info.context.user.person
+                                )
+                            )
+                            | (
+                                Q(related_documentation__amends__amends__teachers__isnull=False)
+                                & Q(
+                                    related_documentation__amends__amends__teachers__contains=info.context.user.person
+                                )
+                            )
+                            | (
+                                (
+                                    Q(related_documentation__amends__isnull=False)
+                                    & (
+                                        Q(
+                                            related_documentation__amends__groups__owners__contains=info.context.user.person
+                                        )
+                                        | Q(
+                                            related_documentation__amends__groups__parent_groups__owners__contains=info.context.user.person
+                                        )
+                                    )
+                                )
+                                | (
+                                    Q(related_documentation__amends__amends__isnull=False)
+                                    & (
+                                        Q(
+                                            related_documentation__amends__amends__groups__owners__contains=info.context.user.person
+                                        )
+                                        | Q(
+                                            related_documentation__amends__amends__groups__parent_groups__owners__contains=info.context.user.person
+                                        )
+                                    )
+                                )
+                            )
+                        ),
+                        output_field=BooleanField(),
+                    ),
+                    output_field=BooleanField(),
+                ),
+            )
+            .annotate(
+                can_delete=F("can_edit"),
+            )
+            .order_by("-related_documentation__datetime_start"),
             info,
         )
 
@@ -324,6 +382,7 @@ class Query(graphene.ObjectType):
         if not info.context.user.has_perm("alsijil.view_person_statistics_rule", person):
             return []
         school_term = get_active_school_term(info.context)
+        global_perm = info.context.user.has_perm("alsijil.change_newpersonalnote")
         return graphene_django_optimizer.query(
             NewPersonalNote.objects.filter(
                 person=person,
@@ -331,7 +390,64 @@ class Query(graphene.ObjectType):
                     datetime_start__date__gte=school_term.date_start,
                     datetime_end__date__lte=school_term.date_end,
                 ),
-            ).order_by("-documentation__datetime_start"),
+            )
+            .annotate(
+                can_edit=ExpressionWrapper(
+                    Value(global_perm)
+                    or ExpressionWrapper(
+                        Q(documentation__isnull=False)
+                        & (
+                            Q(documentation__amends__isnull=False)
+                            | Q(documentation__amends__cancelled=False)
+                        )
+                        & (
+                            Q(documentation__teachers__contains=info.context.user.person)
+                            | (
+                                Q(documentation__amends__teachers__isnull=False)
+                                & Q(
+                                    documentation__amends__teachers__contains=info.context.user.person
+                                )
+                            )
+                            | (
+                                Q(documentation__amends__amends__teachers__isnull=False)
+                                & Q(
+                                    documentation__amends__amends__teachers__contains=info.context.user.person
+                                )
+                            )
+                            | (
+                                (
+                                    Q(documentation__amends__isnull=False)
+                                    & (
+                                        Q(
+                                            documentation__amends__groups__owners__contains=info.context.user.person
+                                        )
+                                        | Q(
+                                            documentation__amends__groups__parent_groups__owners__contains=info.context.user.person
+                                        )
+                                    )
+                                )
+                                | (
+                                    Q(documentation__amends__amends__isnull=False)
+                                    & (
+                                        Q(
+                                            documentation__amends__amends__groups__owners__contains=info.context.user.person
+                                        )
+                                        | Q(
+                                            documentation__amends__amends__groups__parent_groups__owners__contains=info.context.user.person
+                                        )
+                                    )
+                                )
+                            )
+                        ),
+                        output_field=BooleanField(),
+                    ),
+                    output_field=BooleanField(),
+                ),
+            )
+            .annotate(
+                can_delete=F("can_edit"),
+            )
+            .order_by("-documentation__datetime_start"),
             info,
         )
 
diff --git a/aleksis/apps/alsijil/schema/participation_status.py b/aleksis/apps/alsijil/schema/participation_status.py
index bb90b9be..25df9ff3 100644
--- a/aleksis/apps/alsijil/schema/participation_status.py
+++ b/aleksis/apps/alsijil/schema/participation_status.py
@@ -70,10 +70,14 @@ class ParticipationStatusType(
 
     @staticmethod
     def resolve_can_edit(root: ParticipationStatus, info, **kwargs):
+        if hasattr(root, "can_edit"):
+            return root.can_edit
         return info.context.user.has_perm("alsijil.edit_participation_status_rule", root)
 
     @staticmethod
     def resolve_can_delete(root: ParticipationStatus, info, **kwargs):
+        if hasattr(root, "can_delete"):
+            return root.can_delete
         return info.context.user.has_perm("alsijil.edit_participation_status_rule", root)
 
 
diff --git a/aleksis/apps/alsijil/schema/personal_note.py b/aleksis/apps/alsijil/schema/personal_note.py
index f902639f..9598a277 100644
--- a/aleksis/apps/alsijil/schema/personal_note.py
+++ b/aleksis/apps/alsijil/schema/personal_note.py
@@ -28,10 +28,14 @@ class PersonalNoteType(
 
     @staticmethod
     def resolve_can_edit(root: NewPersonalNote, info, **kwargs):
+        if hasattr(root, "can_edit"):
+            return root.can_edit
         return info.context.user.has_perm("alsijil.edit_personal_note_rule", root)
 
     @staticmethod
     def resolve_can_delete(root: NewPersonalNote, info, **kwargs):
+        if hasattr(root, "can_delete"):
+            return root.can_delete
         return info.context.user.has_perm("alsijil.edit_personal_note_rule", root)
 
 
-- 
GitLab