From 4e97b71b008d934b4d306f86ec6a395c8571f8a0 Mon Sep 17 00:00:00 2001 From: Hangzhi Yu <hangzhi@protonmail.com> Date: Thu, 21 Nov 2024 11:34:50 +0100 Subject: [PATCH] Cut existing absences on absence creation to avoid overlaps --- aleksis/apps/kolego/models/absence.py | 77 +++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/aleksis/apps/kolego/models/absence.py b/aleksis/apps/kolego/models/absence.py index a2c5892..7ac0443 100644 --- a/aleksis/apps/kolego/models/absence.py +++ b/aleksis/apps/kolego/models/absence.py @@ -1,3 +1,5 @@ +from datetime import datetime, time + from django.db import models from django.db.models import Q, QuerySet from django.http import HttpRequest @@ -145,6 +147,81 @@ class Absence(FreeBusy): def __str__(self): return f"{self.person} ({self.datetime_start} - {self.datetime_end})" + def save(self, *args, skip_overlap_handling=False, **kwargs): + if not skip_overlap_handling: + events_within = Absence.get_objects( + None, + {"person": self.person.pk}, + start=self.datetime_start or self.date_start, + end=self.datetime_end or self.date_end, + ) + + # Convert dates of new event to datetimes in case dates are used + new_datetime_start = ( + self.datetime_start + if self.datetime_start + else datetime.combine(self.date_start, time()) + ).astimezone(self.timezone) + new_datetime_end = ( + self.datetime_end + if self.datetime_end + else datetime.combine(self.date_end, datetime.max.time()) + ).astimezone(self.timezone) + + for event_within in events_within: + event_within_datetime_start = ( + Absence.value_start_datetime(event_within) + if event_within.datetime_start + else datetime.combine(Absence.value_start_datetime(event_within), time()) + ) + event_within_datetime_end = ( + Absence.value_end_datetime(event_within) + if event_within.datetime_end + else datetime.combine( + Absence.value_end_datetime(event_within), datetime.max.time() + ) + ) + + if ( + new_datetime_start > event_within_datetime_start + and new_datetime_end < event_within_datetime_end + ): + # Cut existing event in two parts + # First, cut end date of existing one + event_within.datetime_end = new_datetime_start + event_within.save(skip_overlap_handling=True) + # Then, create new event filling up the remaining time span + end_filler_event = event_within + end_filler_event.pk = None + end_filler_event.id = None + end_filler_event._state.adding = True + end_filler_event.datetime_start = new_datetime_end + end_filler_event.datetime_end = event_within_datetime_end + + end_filler_event.save(skip_overlap_handling=True) + elif ( + new_datetime_start <= event_within_datetime_start + and new_datetime_end >= event_within_datetime_end + ): + # Delete existing event + event_within.delete() + elif ( + new_datetime_start > event_within_datetime_start + and new_datetime_end >= event_within_datetime_end + ): + # Cut end of existing event + event_within.datetime_end = new_datetime_start + event_within.save(skip_overlap_handling=True) + elif ( + new_datetime_start <= event_within_datetime_start + and new_datetime_end < event_within_datetime_end + ): + # Cut start of existing event + event_within.datetime_start = new_datetime_end + event_within.save(skip_overlap_handling=True) + + super().save(*args, **kwargs) + class Meta: verbose_name = _("Absence") verbose_name_plural = _("Absences") -- GitLab