From 4a0d618969697c4103af4011bc03e2c39d216685 Mon Sep 17 00:00:00 2001
From: Hangzhi Yu <hangzhi@protonmail.com>
Date: Wed, 19 Jun 2024 20:38:47 +0200
Subject: [PATCH] Add filter for lessons with absent students

---
 .../components/coursebook/Coursebook.vue      | 10 +++++++++-
 .../coursebook/CoursebookFilters.vue          | 19 +++++++++++++++++++
 .../components/coursebook/coursebook.graphql  |  2 ++
 .../apps/alsijil/frontend/messages/en.json    |  3 ++-
 aleksis/apps/alsijil/models.py                |  5 +++--
 aleksis/apps/alsijil/schema/__init__.py       |  4 +++-
 6 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue b/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue
index 97bd708a7..9685fe60e 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue
@@ -14,7 +14,7 @@
     use-deep-search
   >
     <template #additionalActions="{ attrs, on }">
-      <coursebook-filters v-model="filters" />
+      <coursebook-filters :page-type="pageType" v-model="filters" />
       <v-expand-transition>
         <v-card outlined class="full-width" v-show="pageType === 'absences' && selectedParticipations.length">
           <v-card-text>
@@ -133,6 +133,7 @@ export default {
       groups: [],
       courses: [],
       incomplete: false,
+      absencesExist: false,
       ready: false,
       initDate: false,
       currentDate: "",
@@ -151,6 +152,7 @@ export default {
         dateStart: this.dateStart,
         dateEnd: this.dateEnd,
         incomplete: !!this.incomplete,
+        absencesExist: !!this.absencesExist,
       };
     },
     filters: {
@@ -161,11 +163,14 @@ export default {
           filterType: this.filterType,
           incomplete: this.incomplete,
           pageType: this.pageType,
+          absencesExist: this.absencesExist,
         };
       },
       set(selectedFilters) {
         if (Object.hasOwn(selectedFilters, "incomplete")) {
           this.incomplete = selectedFilters.incomplete;
+        } else if (Object.hasOwn(selectedFilters, "absencesExist")) {
+          this.absencesExist = selectedFilters.absencesExist;
         } else if (
           Object.hasOwn(selectedFilters, "filterType") ||
           Object.hasOwn(selectedFilters, "objId") ||
@@ -191,6 +196,9 @@ export default {
           // its own component
           this.$refs.iterator.resetDate();
           // might skip query until both set = atomic
+          if (Object.hasOwn(selectedFilters, "pageType")) {
+            this.absencesExist = false;
+          }
         }
       },
     },
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookFilters.vue b/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookFilters.vue
index 54f419b21..d67a388ea 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookFilters.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookFilters.vue
@@ -38,6 +38,20 @@
         inset
         hide-details
       />
+      <v-switch
+        v-if="pageType === 'absences'"
+        :loading="selectLoading"
+        :label="$t('alsijil.coursebook.filter.absences_exist')"
+        :input-value="value.absencesExist"
+        @change="
+          $emit('input', {
+            absencesExist: $event,
+          })
+        "
+        dense
+        inset
+        hide-details
+      />
     </div>
     <v-btn
       outlined
@@ -71,6 +85,11 @@ export default {
       type: Object,
       required: true,
     },
+    pageType: {
+      type: String,
+      required: false,
+      default: "documentations",
+    },
   },
   emits: ["input"],
   apollo: {
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/coursebook.graphql b/aleksis/apps/alsijil/frontend/components/coursebook/coursebook.graphql
index 1e00bab2e..8e92f9b48 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/coursebook.graphql
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/coursebook.graphql
@@ -19,6 +19,7 @@ query documentationsForCoursebook(
   $dateStart: Date!
   $dateEnd: Date!
   $incomplete: Boolean
+  $absencesExist: Boolean
 ) {
   items: documentationsForCoursebook(
     own: $own
@@ -27,6 +28,7 @@ query documentationsForCoursebook(
     dateStart: $dateStart
     dateEnd: $dateEnd
     incomplete: $incomplete
+    absencesExist: $absencesExist
   ) {
     id
     course {
diff --git a/aleksis/apps/alsijil/frontend/messages/en.json b/aleksis/apps/alsijil/frontend/messages/en.json
index 71e5225a6..17ebf62bf 100644
--- a/aleksis/apps/alsijil/frontend/messages/en.json
+++ b/aleksis/apps/alsijil/frontend/messages/en.json
@@ -76,7 +76,8 @@
         "page_type": {
           "documentations": "Show documentations",
           "absences": "Show absences"
-        }
+        },
+        "absences_exist": "Only show lessons with absent participants"
       },
       "present_number": "{present}/{total} present",
       "no_data": "No lessons for the selected groups and courses in this period",
diff --git a/aleksis/apps/alsijil/models.py b/aleksis/apps/alsijil/models.py
index 57e485d03..94c1a1f88 100644
--- a/aleksis/apps/alsijil/models.py
+++ b/aleksis/apps/alsijil/models.py
@@ -531,6 +531,7 @@ class Documentation(CalendarEvent):
         cls,
         events: list,
         incomplete: Optional[bool] = False,
+        absences_exist: Optional[bool] = False,
     ) -> tuple:
         """Get all the documentations for the events.
         Create dummy documentations if none exist.
@@ -550,10 +551,10 @@ class Documentation(CalendarEvent):
 
             if existing_documentations.exists():
                 doc = existing_documentations.first()
-                if incomplete and doc.topic:
+                if (incomplete and doc.topic) or (absences_exist and (not doc.participations.exists or not doc.participations.filter(absence_reason__isnull=False).exists())):
                     continue
                 docs.append(doc)
-            else:
+            elif not absences_exist:
                 if event_reference_obj.amends:
                     if event_reference_obj.course:
                         course = event_reference_obj.course
diff --git a/aleksis/apps/alsijil/schema/__init__.py b/aleksis/apps/alsijil/schema/__init__.py
index 48e1c20c6..c46fbc45f 100644
--- a/aleksis/apps/alsijil/schema/__init__.py
+++ b/aleksis/apps/alsijil/schema/__init__.py
@@ -36,6 +36,7 @@ class Query(graphene.ObjectType):
         date_start=graphene.Date(required=True),
         date_end=graphene.Date(required=True),
         incomplete=graphene.Boolean(required=False),
+        absences_exist=graphene.Boolean(required=False)
     )
 
     groups_by_person = FilterOrderList(GroupType, person=graphene.ID())
@@ -63,6 +64,7 @@ class Query(graphene.ObjectType):
         obj_type=None,
         obj_id=None,
         incomplete=False,
+        absences_exist=False,
         **kwargs,
     ):
         if (
@@ -108,7 +110,7 @@ class Query(graphene.ObjectType):
         )
 
         # Lookup or create documentations and return them all.
-        docs, dummies = Documentation.get_documentations_for_events(events, incomplete)
+        docs, dummies = Documentation.get_documentations_for_events(events, incomplete, absences_exist)
         return docs + dummies
 
     @staticmethod
-- 
GitLab