From 27d9b58dbf6f89a7bf3a7bfbf9562aa3e139a23b Mon Sep 17 00:00:00 2001
From: Jonathan Weth <git@jonathanweth.de>
Date: Thu, 4 Apr 2024 21:56:19 +0200
Subject: [PATCH] Add filter for incomplete documentations

---
 .../components/coursebook/Coursebook.vue      | 76 ++++++++++++-------
 .../components/coursebook/coursebook.graphql  |  2 +
 .../apps/alsijil/frontend/messages/de.json    |  1 +
 .../apps/alsijil/frontend/messages/en.json    |  1 +
 aleksis/apps/alsijil/models.py                | 48 +++++++-----
 aleksis/apps/alsijil/schema/__init__.py       | 13 +++-
 6 files changed, 93 insertions(+), 48 deletions(-)

diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue b/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue
index 3a07a036f..650e1aeb9 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/Coursebook.vue
@@ -12,32 +12,48 @@
     hide-default-footer
   >
     <template #additionalActions="{ attrs, on }">
-      <v-autocomplete
-        :items="selectable"
-        item-text="name"
-        clearable
-        return-object
-        filled
-        dense
-        hide-details
-        :placeholder="$t('alsijil.coursebook.filter.filter_for_obj')"
-        :loading="selectLoading"
-        :value="currentObj"
-        @input="changeSelection"
-        @click:clear="changeSelection"
-      />
-      <v-switch
-        :loading="selectLoading"
-        :label="$t('alsijil.coursebook.filter.own')"
-        :input-value="filterType === 'my'"
-        @change="
-          changeSelection({
-            filterType: $event ? 'my' : 'all',
-            type: objType,
-            id: objId,
-          })
-        "
-      />
+      <div class="d-flex flex-grow-1 justify-end">
+        <v-autocomplete
+          :items="selectable"
+          item-text="name"
+          clearable
+          return-object
+          filled
+          dense
+          hide-details
+          :placeholder="$t('alsijil.coursebook.filter.filter_for_obj')"
+          :loading="selectLoading"
+          :value="currentObj"
+          @input="changeSelection"
+          @click:clear="changeSelection"
+          class="max-width"
+        />
+        <div class="ml-6">
+          <v-switch
+            :loading="selectLoading"
+            :label="$t('alsijil.coursebook.filter.own')"
+            :input-value="filterType === 'my'"
+            @change="
+              changeSelection({
+                filterType: $event ? 'my' : 'all',
+                type: objType,
+                id: objId,
+              })
+            "
+            dense
+            inset
+            hide-details
+          />
+          <v-switch
+            :loading="selectLoading"
+            :label="$t('alsijil.coursebook.filter.missing')"
+            v-model="incomplete"
+            dense
+            inset
+            hide-details
+          />
+        </div>
+      </div>
     </template>
     <template #default="{ items }">
       <v-list-item
@@ -140,6 +156,7 @@ export default {
       courses: [],
       dateStart: null,
       dateEnd: null,
+      incomplete: false,
     };
   },
   apollo: {
@@ -161,6 +178,7 @@ export default {
         dateEnd:
           this.dateEnd ??
           DateTime.fromISO(this.date).plus({ weeks: 1 }).toISODate(),
+        incomplete: !!this.incomplete,
       };
     },
     selectable() {
@@ -282,3 +300,9 @@ export default {
   },
 };
 </script>
+
+<style>
+.max-width {
+  max-width: 25rem;
+}
+</style>
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/coursebook.graphql b/aleksis/apps/alsijil/frontend/components/coursebook/coursebook.graphql
index db39747e9..e782d06ba 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/coursebook.graphql
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/coursebook.graphql
@@ -22,6 +22,7 @@ query documentationsForCoursebook(
   $objType: String
   $dateStart: Date!
   $dateEnd: Date!
+  $incomplete: Boolean
 ) {
   items: documentationsForCoursebook(
     own: $own
@@ -29,6 +30,7 @@ query documentationsForCoursebook(
     objType: $objType
     dateStart: $dateStart
     dateEnd: $dateEnd
+    incomplete: $incomplete
   ) {
     id
     course {
diff --git a/aleksis/apps/alsijil/frontend/messages/de.json b/aleksis/apps/alsijil/frontend/messages/de.json
index 9d52cb9b1..614c3142e 100644
--- a/aleksis/apps/alsijil/frontend/messages/de.json
+++ b/aleksis/apps/alsijil/frontend/messages/de.json
@@ -59,6 +59,7 @@
       },
       "filter": {
         "own": "Nur eigene Stunden anzeigen",
+        "missing": "Nur unvollständige Stunden anzeigen",
         "groups": "Gruppen",
         "courses": "Kurse",
         "filter_for_obj": "Nach Gruppe und Kurs filtern"
diff --git a/aleksis/apps/alsijil/frontend/messages/en.json b/aleksis/apps/alsijil/frontend/messages/en.json
index f5b263f76..679044209 100644
--- a/aleksis/apps/alsijil/frontend/messages/en.json
+++ b/aleksis/apps/alsijil/frontend/messages/en.json
@@ -63,6 +63,7 @@
       },
       "filter": {
         "own": "Only show own lessons",
+        "missing": "Only show incomplete lessons",
         "groups": "Groups",
         "courses": "Courses",
         "filter_for_obj": "Filter for group and course"
diff --git a/aleksis/apps/alsijil/models.py b/aleksis/apps/alsijil/models.py
index 5fcc55309..067818d18 100644
--- a/aleksis/apps/alsijil/models.py
+++ b/aleksis/apps/alsijil/models.py
@@ -534,8 +534,9 @@ class Documentation(CalendarEvent):
         date_start: datetime,
         date_end: datetime,
         request: HttpRequest,
-        obj_type: Optional[str],
-        obj_id: Optional[str],
+        obj_type: Optional[str] = None,
+        obj_id: Optional[str] = None,
+        incomplete: Optional[bool] = False,
     ) -> list:
         """Get all the documentations for an object and a time frame.
 
@@ -564,28 +565,35 @@ class Documentation(CalendarEvent):
 
         # 2. For each lessonEvent → check if there is a documentation
         # if so, add the documentation to a list, if not, create a new one
-        return [
-            (
-                existing_documentations.first()
-                if (
-                    existing_documentations := (
-                        event_reference_obj := event["REFERENCE_OBJECT"]
-                    ).documentation.filter(
+        docs = []
+        for event in events:
+            if incomplete and event["STATUS"] == "CANCELLED":
+                continue
+
+            event_reference_obj = event["REFERENCE_OBJECT"]
+            existing_documentations = event_reference_obj.documentation.filter(
+                datetime_start=event["DTSTART"].dt,
+                datetime_end=event["DTEND"].dt,
+            )
+
+            if existing_documentations.exists():
+                doc = existing_documentations.first()
+                if incomplete and doc.topic:
+                    continue
+                docs.append(doc)
+            else:
+                docs.append(
+                    cls(
+                        pk=f"DUMMY;{event_reference_obj.id};{event['DTSTART'].dt.isoformat()};{event['DTEND'].dt.isoformat()}",
+                        lesson_event=event_reference_obj,
+                        course=event_reference_obj.course,
+                        subject=event_reference_obj.subject,
                         datetime_start=event["DTSTART"].dt,
                         datetime_end=event["DTEND"].dt,
                     )
-                ).exists()
-                else cls(
-                    pk=f"DUMMY;{event_reference_obj.id};{event['DTSTART'].dt.isoformat()};{event['DTEND'].dt.isoformat()}",
-                    lesson_event=event_reference_obj,
-                    course=event_reference_obj.course,
-                    subject=event_reference_obj.subject,
-                    datetime_start=event["DTSTART"].dt,
-                    datetime_end=event["DTEND"].dt,
                 )
-            )
-            for event in events
-        ]
+
+        return docs
 
 
 class ParticipationStatus(ExtensibleModel):
diff --git a/aleksis/apps/alsijil/schema/__init__.py b/aleksis/apps/alsijil/schema/__init__.py
index afc8fd8d2..8cffee5fd 100644
--- a/aleksis/apps/alsijil/schema/__init__.py
+++ b/aleksis/apps/alsijil/schema/__init__.py
@@ -35,6 +35,7 @@ class Query(graphene.ObjectType):
         obj_id=graphene.ID(required=False),
         date_start=graphene.Date(required=True),
         date_end=graphene.Date(required=True),
+        incomplete=graphene.Boolean(required=False),
     )
 
     groups_by_person = FilterOrderList(GroupType, person=graphene.ID())
@@ -47,7 +48,15 @@ class Query(graphene.ObjectType):
         return documentations
 
     def resolve_documentations_for_coursebook(
-        root, info, own, date_start, date_end, obj_type=None, obj_id=None, **kwargs
+        root,
+        info,
+        own,
+        date_start,
+        date_end,
+        obj_type=None,
+        obj_id=None,
+        incomplete=False,
+        **kwargs,
     ):
         datetime_start = datetime.combine(date_start, datetime.min.time())
         datetime_end = datetime.combine(date_end, datetime.max.time())
@@ -75,7 +84,7 @@ class Query(graphene.ObjectType):
             raise PermissionDenied()
 
         return Documentation.get_for_coursebook(
-            own, datetime_start, datetime_end, info.context, obj_type, obj_id
+            own, datetime_start, datetime_end, info.context, obj_type, obj_id, incomplete
         )
 
     @staticmethod
-- 
GitLab