From 10406c724fa28b3e38f21c7ef25f1c3f6ade5b69 Mon Sep 17 00:00:00 2001
From: Julian Leucker <leuckerj@gmail.com>
Date: Wed, 10 Jul 2024 21:18:43 +0200
Subject: [PATCH] Create mutation to extend absences to the entire day

---
 aleksis/apps/alsijil/schema/__init__.py       |  6 +-
 .../alsijil/schema/participation_status.py    | 80 +++++++++++++++++++
 2 files changed, 85 insertions(+), 1 deletion(-)

diff --git a/aleksis/apps/alsijil/schema/__init__.py b/aleksis/apps/alsijil/schema/__init__.py
index f63b8c2c9..2e6775bd3 100644
--- a/aleksis/apps/alsijil/schema/__init__.py
+++ b/aleksis/apps/alsijil/schema/__init__.py
@@ -32,7 +32,10 @@ from .extra_marks import (
     ExtraMarkBatchPatchMutation,
     ExtraMarkType,
 )
-from .participation_status import ParticipationStatusBatchPatchMutation
+from .participation_status import (
+    ExtendParticipationStatusToAbsenceBatchMutation,
+    ParticipationStatusBatchPatchMutation,
+)
 from .personal_note import (
     PersonalNoteBatchCreateMutation,
     PersonalNoteBatchDeleteMutation,
@@ -233,6 +236,7 @@ class Mutation(graphene.ObjectType):
     touch_documentation = TouchDocumentationMutation.Field()
     update_participation_statuses = ParticipationStatusBatchPatchMutation.Field()
     create_absences_for_persons = AbsencesForPersonsCreateMutation.Field()
+    extend_participation_statuses = ExtendParticipationStatusToAbsenceBatchMutation.Field()
 
     create_extra_marks = ExtraMarkBatchCreateMutation.Field()
     update_extra_marks = ExtraMarkBatchPatchMutation.Field()
diff --git a/aleksis/apps/alsijil/schema/participation_status.py b/aleksis/apps/alsijil/schema/participation_status.py
index 951ab4419..2b9ebb25f 100644
--- a/aleksis/apps/alsijil/schema/participation_status.py
+++ b/aleksis/apps/alsijil/schema/participation_status.py
@@ -1,10 +1,16 @@
+import datetime
+
 from django.core.exceptions import PermissionDenied
+from django.utils.translation import gettext_lazy as _
 
 import graphene
 from graphene_django import DjangoObjectType
+from reversion import create_revision, set_comment, set_user
 
 from aleksis.apps.alsijil.models import NewPersonalNote, ParticipationStatus
 from aleksis.apps.alsijil.schema.personal_note import PersonalNoteType
+from aleksis.apps.kolego.models import Absence
+from aleksis.apps.kolego.schema.absence import AbsenceType
 from aleksis.core.schema.base import (
     BaseBatchPatchMutation,
     DjangoFilterMixin,
@@ -70,3 +76,77 @@ class ParticipationStatusBatchPatchMutation(BaseBatchPatchMutation):
             "alsijil.edit_participation_status_for_documentation_rule", obj.related_documentation
         ):
             raise PermissionDenied()
+
+
+class ExtendParticipationStatusToAbsenceBatchMutation(graphene.Mutation):
+    class Arguments:
+        input = graphene.List(graphene.ID, description=_("List of ParticipationStatus IDs"))
+
+    participations = graphene.List(ParticipationStatusType)
+    absences = graphene.List(AbsenceType)
+
+    @classmethod
+    def create_absence(cls, info, participation_id):
+        participation = ParticipationStatus.objects.get(pk=participation_id)
+
+        if participation.date_end:
+            end_date = participation.date_end
+        else:
+            end_date = ParticipationStatus.value_end_datetime(participation).date()
+
+        end_datetime = datetime.datetime.combine(
+            end_date, datetime.time.max, participation.timezone
+        )
+
+        if participation.base_absence:
+            # Update the base absence to increase length if needed
+            absence = participation.base_absence
+
+            if absence.date_end:
+                if absence.date_end < end_date:
+                    absence.date_end = end_date
+                    absence.save()
+
+                return participation, absence
+
+            # Absence uses a datetime
+            if absence.datetime_end.astimezone(absence.timezone) < end_datetime:
+                # The end date ends after the previous absence end
+                absence.datetime_end = end_datetime
+                absence.save()
+
+            return participation, absence
+
+        else:
+            # No base absence, simply create one
+            data = dict(
+                reason_id=participation.absence_reason.id,
+                person=participation.person,
+            )
+
+            if participation.date_start:
+                data["date_start"] = participation.date_start
+                data["date_end"] = end_date
+            else:
+                data["datetime_start"] = ParticipationStatus.value_start_datetime(participation)
+                data["datetime_end"] = end_datetime
+
+            absence, __ = Absence.objects.get_or_create(**data)
+
+            participation.base_absence = absence
+            participation.save()
+
+            return participation, absence
+
+    @classmethod
+    def mutate(cls, root, info, input):  # noqa
+        with create_revision():
+            set_user(info.context.user)
+            set_comment(_("Extended absence reason from coursebook."))
+            participations, absences = zip(
+                *[cls.create_absence(info, participation_id) for participation_id in input]
+            )
+
+        return ExtendParticipationStatusToAbsenceBatchMutation(
+            participations=participations, absences=absences
+        )
-- 
GitLab