diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationDialog.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationDialog.vue index 7a4b17c803ac4193f70ef413e913bd4a330c5733..690b07972cd932a8d5eb0fd91609e0a662d829b0 100644 --- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationDialog.vue +++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/AbsenceCreationDialog.vue @@ -161,14 +161,17 @@ export default { reason: this.absenceReason, }, (storedDocumentations, incomingStatuses) => { - const documentation = storedDocumentations.find( - (doc) => doc.id === this.documentation.id, - ); - incomingStatuses.forEach((newStatus) => { + const documentation = storedDocumentations.find( + (doc) => doc.id === newStatus.relatedDocumentation.id, + ); + if (!documentation) { + return; + } const participationStatus = documentation.participations.find( (part) => part.id === newStatus.id, ); + participationStatus.absenceReason = newStatus.absenceReason; participationStatus.isOptimistic = newStatus.isOptimistic; }); diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue b/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue index bfd62a4937047d52df4bb6a4f0d7d644df1de615..1fdbd333e2fc21fb44470382e515159e1bc743fe 100644 --- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue +++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/ManageStudentsDialog.vue @@ -129,15 +129,18 @@ export default { input: this.markAsAbsentDay.participationIDs, }, (storedDocumentations, incomingStatuses) => { - const documentation = storedDocumentations.find( - (doc) => doc.id === this.documentation.id, - ); - incomingStatuses.forEach((newStatus) => { + const documentation = storedDocumentations.find( + (doc) => doc.id === newStatus.relatedDocumentation.id, + ); + if (!documentation) { + return; + } const participationStatus = documentation.participations.find( (part) => part.id === newStatus.id, ); - participationStatus.baseAbsence = newStatus.baseAbsence; + + participationStatus.absenceReason = newStatus.absenceReason; participationStatus.isOptimistic = newStatus.isOptimistic; }); diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/absences/participationStatus.graphql b/aleksis/apps/alsijil/frontend/components/coursebook/absences/participationStatus.graphql index 39eb59528c4f876eb2c75c20f187e5ded6fae2bc..a20cb091a4a3352a8e182330453972e40a539800 100644 --- a/aleksis/apps/alsijil/frontend/components/coursebook/absences/participationStatus.graphql +++ b/aleksis/apps/alsijil/frontend/components/coursebook/absences/participationStatus.graphql @@ -69,6 +69,15 @@ mutation extendParticipationStatuses($input: [ID]!) { extendParticipationStatuses(input: $input) { items: participations { id + relatedDocumentation { + id + } + absenceReason { + id + name + shortName + colour + } } absences { id diff --git a/aleksis/apps/alsijil/models.py b/aleksis/apps/alsijil/models.py index 2f52a8988fa07991ff602f71d7ec5f46506555df..8ab95cdfac80cb73a3d556137c6e60e95ca208cb 100644 --- a/aleksis/apps/alsijil/models.py +++ b/aleksis/apps/alsijil/models.py @@ -472,6 +472,29 @@ class ParticipationStatus(CalendarEvent): """Return the title of the calendar event.""" return "" + @classmethod + def set_from_kolego_by_datetimes( + cls, kolego_absence: KolegoAbsence, person: Person, start: datetime, end: datetime + ) -> list["ParticipationStatus"]: + participation_statuses = [] + + events = cls.get_single_events( + start, + end, + None, + {"person": person}, + with_reference_object=True, + ) + + for event in events: + participation_status = event["REFERENCE_OBJECT"] + participation_status.absence_reason = kolego_absence.reason + participation_status.base_absence = kolego_absence + participation_status.save() + participation_statuses.append(participation_status) + + return participation_statuses + def fill_from_kolego(self, kolego_absence: KolegoAbsence): """Take over data from a Kolego absence.""" self.base_absence = kolego_absence diff --git a/aleksis/apps/alsijil/schema/absences.py b/aleksis/apps/alsijil/schema/absences.py index ab006e1d875e053e061d04c9c800ae0c3ef15f2e..28eca0172e7caf203214d6db27eee5c31d6c0b09 100644 --- a/aleksis/apps/alsijil/schema/absences.py +++ b/aleksis/apps/alsijil/schema/absences.py @@ -2,7 +2,6 @@ import datetime from typing import List from django.core.exceptions import PermissionDenied -from django.db.models import Q import graphene @@ -43,41 +42,18 @@ class AbsencesForPersonsCreateMutation(graphene.Mutation): if not info.context.user.has_perm("alsijil.register_absence_rule", person): raise PermissionDenied() - # Check if there is an existing absence with overlapping datetime - absences = Absence.objects.filter( - Q(datetime_start__lte=start) | Q(date_start__lte=start.date()), - Q(datetime_end__gte=end) | Q(date_end__gte=end.date()), + kolego_absence = Absence.get_for_person_by_datetimes( + datetime_start=start, + datetime_end=end, reason_id=reason, person=person, + defaults={"comment": comment}, ) - if len(absences) > 0: - kolego_absence = absences.first() - else: - # Check for same times and create otherwise - kolego_absence, __ = Absence.objects.get_or_create( - datetime_start=start, - datetime_end=end, - reason_id=reason, - person=person, - defaults={"comment": comment}, - ) - - events = ParticipationStatus.get_single_events( - start, - end, - None, - {"person": person}, - with_reference_object=True, + participation_statuses += ParticipationStatus.set_from_kolego_by_datetimes( + kolego_absence=kolego_absence, person=person, start=start, end=end ) - for event in events: - participation_status = event["REFERENCE_OBJECT"] - participation_status.absence_reason_id = reason - participation_status.base_absence = kolego_absence - participation_status.save() - participation_statuses.append(participation_status) - return AbsencesForPersonsCreateMutation( ok=True, participation_statuses=participation_statuses ) diff --git a/aleksis/apps/alsijil/schema/participation_status.py b/aleksis/apps/alsijil/schema/participation_status.py index bb0e24e0564ab5f87a3fe1a25c85f4d8c5766241..2f6a830e2e292edd79c8cd0fce30fb14ce0f536b 100644 --- a/aleksis/apps/alsijil/schema/participation_status.py +++ b/aleksis/apps/alsijil/schema/participation_status.py @@ -1,6 +1,7 @@ import datetime from django.core.exceptions import PermissionDenied +from django.utils.formats import date_format from django.utils.translation import gettext_lazy as _ import graphene @@ -105,60 +106,57 @@ class ExtendParticipationStatusToAbsenceBatchMutation(graphene.Mutation): if participation.date_end: end_date = participation.date_end else: - end_date = ParticipationStatus.value_end_datetime(participation).date() + end_date = participation.datetime_end.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 + data = dict( + reason=participation.absence_reason if participation.absence_reason else None, + person=participation.person, + ) + if participation.date_start: + data["date_start"] = participation.date_start + data["date_end"] = end_date + start_datetime = datetime.datetime.combine( + participation.date_start, datetime.time.min, participation.timezone + ) else: - # No base absence, simply create one if absence reason is given - data = dict( - reason_id=participation.absence_reason.id if participation.absence_reason else None, - person=participation.person, + data["datetime_start"] = participation.datetime_start + data["datetime_end"] = end_datetime + start_datetime = participation.datetime_start + + defaults = dict( + comment=_("Extended by {full_name} on {datetime}").format( + full_name=info.context.user.person.full_name, + datetime=date_format(participation.date_start or participation.datetime_start), ) + ) - 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) + absence = Absence.get_for_person_by_datetimes(**data, defaults=defaults) - participation.base_absence = absence - participation.save() + participations = ParticipationStatus.set_from_kolego_by_datetimes( + kolego_absence=absence, + person=participation.person, + start=start_datetime, + end=end_datetime, + ) - return participation, absence + return participations, 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] - ) + participations = [] + absences = [] + for participation_id in input: + p, a = cls.create_absence(info, participation_id) + participations += p + absences.append(a) return ExtendParticipationStatusToAbsenceBatchMutation( participations=participations, absences=absences