from datetime import datetime, time from typing import Iterable, Union from graphene_django.types import DjangoObjectType from guardian.shortcuts import get_objects_for_user from aleksis.core.schema.base import ( BaseBatchCreateMutation, BaseBatchDeleteMutation, BaseBatchPatchMutation, DjangoFilterMixin, PermissionsTypeMixin, ) from ..models import Absence, AbsenceReason, AbsenceReasonTag class AbsenceReasonTagType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectType): class Meta: model = AbsenceReasonTag fields = ("id", "short_name", "name") filter_fields = { "short_name": ["icontains", "exact"], "name": ["icontains", "exact"], } @classmethod def get_queryset(cls, queryset, info): return get_objects_for_user(info.context.user, "kolego.view_absencereasontag", queryset) class AbsenceReasonType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectType): class Meta: model = AbsenceReason fields = ("id", "short_name", "name", "colour", "count_as_absent", "default", "tags") filter_fields = { "short_name": ["icontains", "exact"], "name": ["icontains", "exact"], } def resolve_tags(root, info, **kwargs): return root.tags.managed_and_unmanaged().filter(absence_reasons=root) @classmethod def get_queryset(cls, queryset, info): if not info.context.user.has_perm("kolego.fetch_absencereasons_rule"): return [] return queryset class AbsenceType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectType): class Meta: model = Absence fields = ( "id", "person", "reason", "comment", "datetime_start", "datetime_end", "date_start", "date_end", ) filter_fields = { "person__full_name": ["icontains", "exact"], "comment": ["icontains", "exact"], } @classmethod def get_queryset(cls, queryset, info): query = get_objects_for_user(info.context.user, "kolego.view_absence", queryset) query = query.select_related("person", "reason") return query class AbsenceBatchCreateMutation(BaseBatchCreateMutation): class Meta: model = Absence fields = ( "person", "reason", "comment", "datetime_start", "datetime_end", "date_start", "date_end", ) optional_fields = ("comment", "reason") permissions = ("kolego.create_absence_rule",) @classmethod def before_save(cls, root, info, input, created_objects): # noqa modified_created_objects = [] for obj in created_objects: events_within = ( Absence.get_objects( None, {"person": obj.person.pk}, start=obj.datetime_start or obj.date_start, end=obj.datetime_end or obj.date_end, ) .order_by("pk") .filter(reason=obj.reason) ) if events_within.exists(): # Convert dates of new event to datetimes in case dates are used new_datetime_start = ( obj.datetime_start if obj.datetime_start else datetime.combine(obj.date_start, time()) ).astimezone(obj.timezone) new_datetime_end = ( obj.datetime_end if obj.datetime_end else datetime.combine(obj.date_end, datetime.max.time()) ).astimezone(obj.timezone) events_within_datetime_start_list = [ ( Absence.value_start_datetime(event_within) if event_within.datetime_start else datetime.combine(Absence.value_start_datetime(event_within), time()) ) for event_within in events_within ] events_within_datetime_end_list = [ ( Absence.value_end_datetime(event_within) if event_within.datetime_end else datetime.combine( Absence.value_end_datetime(event_within), datetime.max.time() ) ).astimezone(event_within.timezone) for event_within in events_within ] # Extend overlapping lesson with same reason last_event_within = events_within.last() last_event_within.datetime_start = min( new_datetime_start, *events_within_datetime_start_list ) last_event_within.datetime_end = max( new_datetime_end, *events_within_datetime_end_list ) events_within.exclude(pk=last_event_within.pk).delete() modified_created_objects.append(last_event_within) else: modified_created_objects.append(obj) return modified_created_objects class AbsenceBatchDeleteMutation(BaseBatchDeleteMutation): class Meta: model = Absence permissions = ("kolego.delete_absence_rule",) class AbsenceBatchPatchMutation(BaseBatchPatchMutation): class Meta: model = Absence fields = ( "id", "person", "reason", "comment", "datetime_start", "datetime_end", "date_start", "date_end", ) permissions = ("kolego.edit_absence_rule",) class AbsenceReasonBatchCreateMutation(BaseBatchCreateMutation): class Meta: model = AbsenceReason fields = ("short_name", "name", "colour", "count_as_absent", "default", "tags") optional_fields = ("name",) permissions = ("kolego.create_absencereason_rule",) @classmethod def get_all_objs(cls, Model, ids: Iterable[Union[str, int]]): return list( Model.objects.managed_and_unmanaged().filter( pk__in=[cls.resolve_id(id_) for id_ in ids] ) ) class AbsenceReasonBatchDeleteMutation(BaseBatchDeleteMutation): class Meta: model = AbsenceReason permissions = ("kolego.delete_absencereason_rule",) class AbsenceReasonBatchPatchMutation(BaseBatchPatchMutation): class Meta: model = AbsenceReason fields = ("id", "short_name", "name", "colour", "count_as_absent", "default", "tags") permissions = ("kolego.edit_absencereason_rule",) @classmethod def get_all_objs(cls, Model, ids: Iterable[Union[str, int]]): return list( Model.objects.managed_and_unmanaged().filter( pk__in=[cls.resolve_id(id_) for id_ in ids] ) ) class AbsenceReasonTagBatchCreateMutation(BaseBatchCreateMutation): class Meta: model = AbsenceReasonTag fields = ("short_name", "name") optional_fields = ("name",) permissions = ("kolego.create_absencereasontag_rule",) class AbsenceReasonTagBatchDeleteMutation(BaseBatchDeleteMutation): class Meta: model = AbsenceReasonTag permissions = ("kolego.delete_absencereasontag_rule",) class AbsenceReasonTagBatchPatchMutation(BaseBatchPatchMutation): class Meta: model = AbsenceReasonTag fields = ("id", "short_name", "name") permissions = ("kolego.edit_absencereasontag_rule",)