Untitled

 avatar
unknown
plain_text
a year ago
22 kB
4
Indexable
from django.http import QueryDict

from appCalendar.models import GenerateMeetingLink
from appCalendar.serializers import MeetingSerializer
from appGH.filters import NurseFilter
from appGH.serializers import DoctorSerializer, NurseSerializer
from rest_framework import viewsets, status, mixins
from rest_framework.exceptions import ValidationError
from rest_framework.response import Response
import datetime
import dateutil.parser
from django.db.models import Q, Prefetch, Count
from rest_framework.permissions import IsAuthenticated

from appUser.permissions import APIUserPermissions
from appSchedule.exception import ScheduleException
from .filters import DoctorScheduleFilters
from . import serializers
from .models import Schedule
from appGH.models import Doctor, Nurse, CompanyGroup, Careteam
from django_filters.rest_framework import DjangoFilterBackend
from .permissions import SchedulePermission
from django.db import transaction

from .utils import user_type, user_companies, validate_careteam_schedule

SCHEDULE_MAX_RECURRENCY = 8

def filter_queryset(self):
    start_date = self.request.query_params.get('startDate')
    stop_date = self.request.query_params.get('stopDate')

    if bool(start_date) and bool(stop_date):
        # filter by start and stop date
        try:
            start_date = dateutil.parser.parse(start_date, dayfirst=True)
            stop_date = dateutil.parser.parse(
                stop_date, dayfirst=True).replace(hour=23, minute=59, second=59)
        except:
            raise ValidationError({"message": "Data inválida"})

    self.request.query_params._mutable = True
    schedule_filters_dict = {}
    for key in list(self.request.query_params.keys()):
        if key.startswith('schedule'):
            schedule_filters_dict[key[len('schedule') + 2:]] = self.request.query_params.pop(key)[0]

    schedule_filters = QueryDict(mutable=True)
    schedule_filters.update(schedule_filters_dict)
    user_request = self.request.user

    careteam_user = user_type(user_request)
    if careteam_user is None:
        raise ValidationError({
            "message": "Usuário sem acesso aos horários médicos"
        })
    companies_user = user_companies(user_request)
    if companies_user is None:
        raise ValidationError({
            "message": "Usuário sem acesso aos horários médicos"
        })

    queryset = self.filter_queryset(self.get_queryset().filter(
        Q(schedule_id__start__gte=start_date, schedule_id__start__lt=stop_date) |
        Q(schedule_id__stop__gt=start_date, schedule_id__stop__lte=stop_date)
    ).prefetch_related(
        Prefetch('schedule_id', queryset=Schedule.objects.filter(
            Q(careteam_id=None) | Q(careteam_id__in=careteam_user),
            Q(company_group_id=None) | Q(company_group_id__in=companies_user),
            **schedule_filters_dict).distinct())
    ))
    return queryset


def list_view(self, request, *args, **kwargs):
    start_date = self.request.query_params.get('startDate')
    stop_date = self.request.query_params.get('stopDate')

    schedule_filters_dict = {}
    self.request.query_params._mutable = False

    if bool(start_date) and bool(stop_date):
        # filter by start and stop date
        try:
            start_date = dateutil.parser.parse(start_date, dayfirst=True)
            stop_date = dateutil.parser.parse(
                stop_date, dayfirst=True).replace(hour=23, minute=59, second=59)
        except:
            raise ValidationError({"message": "Data inválida"})

        queryset = filter_queryset(self)

        instances = []
        for i, instance in enumerate(queryset):
            instances.append(i)
            instances[i] = {}
            instances[i]['id'] = instance.id
            instances[i]['name'] = instance.user_id.name
            instances[i]['schedule_id'] = []
            for schedule in instance.schedule_id.all():
                if start_date <= schedule.start < stop_date or start_date < schedule.stop <= stop_date:
                    instances[i]['schedule_id'].append(serializers.ScheduleSerializer(schedule).data)

        return Response(instances, status=status.HTTP_200_OK)

    queryset = self.filter_queryset(self.get_queryset().prefetch_related(
        Prefetch('schedule_id', queryset=Schedule.objects.filter(
            **schedule_filters_dict))
    ))
    serializer = self.get_serializer(queryset, many=True)
    return Response(serializer.data)


def retrieve_view(self, request, *args, **kwargs):
    instance = self.get_object()

    start_date = self.request.query_params.get('startDate')
    stop_date = self.request.query_params.get('stopDate')
    careteam_id = self.request.query_params.get('careteam_id__id', None)

    if bool(start_date) and bool(stop_date):
        # filter by start and stop date
        try:
            start_date = dateutil.parser.parse(start_date, dayfirst=True)
            stop_date = dateutil.parser.parse(
                stop_date, dayfirst=True).replace(hour=23, minute=59, second=59)
        except:
            raise ValidationError({"message": "Data inválida"})

        doctor = filter_queryset(self).filter(id=instance.id).first()
        response = {'id': instance.id}
        schedules = []
        if doctor is not None:
            instance = doctor

        for schedule in instance.schedule_id.all():
            if start_date <= schedule.start < stop_date or start_date < schedule.stop <= stop_date:
                schedule_data = serializers.ScheduleSerializer(schedule).data
                schedule_data['can_schedule'] = validate_careteam_schedule(schedule, careteam_id)
                schedules.append(schedule_data)

        response['schedule_id'] = schedules
        return Response(response, status=status.HTTP_200_OK)

    serializer = self.get_serializer(instance)
    return Response(serializer.data)

def check_professional_schedule(professional_instance, start, stop, exclude_params={}):
    instance_schedules = professional_instance.schedule_id.all()

    # Verify if time start of last schedule is bigger than time stop
    if start >= stop:
        raise ScheduleException(
            "O horário de início não pode ser posterior ou igual ao horário final.", status.HTTP_400_BAD_REQUEST)

    # Verify if already exists schedule in this hour
    filter_one = instance_schedules.filter(start__lt=stop, stop__gt=stop)
    filter_two = instance_schedules.filter(
        start__gte=start, stop__lte=stop)
    filter_three = instance_schedules.filter(
        start__lt=start, stop__gt=start)

    if exclude_params:
        filter_one = filter_one.exclude(**exclude_params)
        filter_two = filter_two.exclude(**exclude_params)
        filter_three = filter_three.exclude(**exclude_params)

    if filter_one.exists() or filter_two.exists() or filter_three.exists():
        raise ScheduleException(
            f"Conflito entre os horários \'{start.strftime('%d/%m/%Y %X')}\' e \'{stop.strftime('%d/%m/%Y %X')}\'. "
            "Já existe um horário para esse profissional neste horário.", status.HTTP_400_BAD_REQUEST)

    return True


def prebuild_schedule_obj(data, professional_instance):
    start = data["start"]
    stop = data["stop"]
    grade = data["grade"]

    duration = stop - start

    on_duty = data.get("on_duty", False)

    schedule_data = {
        "start": start,
        "stop": stop,
        "grade": grade,
        "duration": duration,
        "description": data.get("description", None),
        "careteam_id": data.get("careteam", None),
        "on_duty": on_duty,
        "recurrent_schedule": data.get("recurrent_schedule", None),
        "recurrent": data.get("recurrent", False),
        "available": data.get("available", True) if not on_duty else True,
        "content_object": professional_instance,
    }
    return Schedule(**schedule_data)


def create_view(model, self, request, *args, **kwargs):
    schedule = request.data.get("schedule_id")
    instance_id = request.data.get("professional_id")
            
    schedule.pop("recurrent_schedule", None)
    # Manually removing it so users can't change it manually

    professional_instance = model.objects.get(id=instance_id)
    start = dateutil.parser.parse(schedule["start"], dayfirst=True)
    stop = dateutil.parser.parse(schedule["stop"], dayfirst=True)

    careteam = schedule.get('careteam', None)
    
    if careteam:
        if not professional_instance.careteam_id.filter(id=careteam).exists():
            raise ScheduleException(
                'Professional ou careteam inválidos', status.HTTP_400_BAD_REQUEST)

    schedule["start"] = start
    schedule["stop"] = stop

    first_schedule_obj = prebuild_schedule_obj(
        schedule, professional_instance)

    check_professional_schedule(
        professional_instance, first_schedule_obj.start, first_schedule_obj.stop)
    first_schedule_obj.save()

    first_schedule_obj.company_group_id.set(schedule.get("company_group_id", []))
    first_schedule_obj.save()

    recurrent = schedule.get("recurrent", None)

    if recurrent:
        first_schedule_obj.recurrent_schedule = first_schedule_obj
        first_schedule_obj.recurrent = True

        schedule["recurrent_schedule"] = first_schedule_obj
        schedule["recurrent"] = True

        validate_schedules = []

        for _ in range(SCHEDULE_MAX_RECURRENCY):
            schedule["start"] += datetime.timedelta(days=7)
            schedule["stop"] += datetime.timedelta(days=7)

            schedule["duration"] = stop - start

            schedule_obj = prebuild_schedule_obj(
                schedule, professional_instance)

            check_professional_schedule(
                professional_instance, schedule_obj.start, schedule_obj.stop)

            validate_schedules.append(schedule_obj)

        schedule_to_update = professional_instance.schedule_id.bulk_create(
            validate_schedules)

        for schedule in schedule_to_update:
            schedule.company_group_id.set(first_schedule_obj.company_group_id.all())
            schedule.save()

    else:
        first_schedule_obj.recurrent_schedule = None
        first_schedule_obj.recurrent = False

    first_schedule_obj.save()

    return Response(serializers.ScheduleSerializerWithProfessional(first_schedule_obj).data,
                    status=status.HTTP_201_CREATED)


def recurrent_update(self, request, *args, **kwargs):
    partial = kwargs.pop('partial', False)
    schedule = self.get_object()

    start_first_schedule = schedule.start
    stop_first_schedule = schedule.stop

    start = dateutil.parser.parse(request.data.get(
        "start"), dayfirst=True)
    stop = dateutil.parser.parse(request.data.get(
        "stop"), dayfirst=True)

    # if already have a meeting don't let update datetimes
    if schedule.meeting_id and (start != schedule.start or stop != schedule.stop):
        raise ScheduleException(
            "Não é possível editar a data .", status.HTTP_400_BAD_REQUEST)

    check_professional_schedule(
        schedule.content_object, start, stop, {'id': schedule.id})

    recurrent_editing = request.data.get('recurrent_editing', False)
    recurrent_schedules = schedule.recurrent_schedules.all()

    request.data["start"] = start
    request.data["stop"] = stop
    request.data["duration"] = stop - start
    on_duty = request.data.get('on_duty', False)

    if on_duty:
        request.data["available"] = True

    serializer = self.get_serializer(
        schedule, data=request.data, partial=partial)
    serializer.is_valid(raise_exception=True)
    self.perform_update(serializer)

    if recurrent_editing and recurrent_schedules:
        divergence_schedule = recurrent_schedules.exclude(
            start__time=start_first_schedule.time(), stop__time=stop_first_schedule.time())[1:]

        if divergence_schedule.exists():
            raise ScheduleException(
                "Não é possível editar esse horário pois há divergências em um dos horários recorrentes.",
                status.HTTP_403_FORBIDDEN)

        for recurrent_schedule in recurrent_schedules[1:]:
            request.data["start"] += datetime.timedelta(days=7)
            request.data["stop"] += datetime.timedelta(days=7)
            request.data["duration"] = stop - start

            recurrent_serializer = self.get_serializer(
                recurrent_schedule, data=request.data, partial=partial)
            recurrent_serializer.is_valid(raise_exception=True)
            self.perform_update(recurrent_serializer)

    # # Verify if time start of last schedule is bigger than time stop
    # if start > stop:
    #     return Response({"message": "O horário de início não pode ser posterior ao horário final."},
    #                     status=status.HTTP_400_BAD_REQUEST)

    # # Verify if already exists schedule in this hour
    # instance_schedules = schedule.content_object.schedule_id.all()
    # filter_one = instance_schedules.filter(
    #     start__lt=stop, stop__gt=stop).filter(~Q(id=schedule.id))
    # filter_two = instance_schedules.filter(
    #     start__gte=start, stop__lte=stop).filter(~Q(id=schedule.id))
    # filter_three = instance_schedules.filter(
    #     start__lt=start, stop__gt=start).filter(~Q(id=schedule.id))

    # if filter_one.exists() or filter_two.exists() or filter_three.exists():
    #     return Response({"message": "Conflito de horário, não é possível marcar em um horário já existente."},
    #                     status=status.HTTP_400_BAD_REQUEST)

    return Response(serializer.data, status=status.HTTP_200_OK)


class ScheduleViewSet(mixins.UpdateModelMixin,
                      mixins.DestroyModelMixin,
                      viewsets.GenericViewSet):
    queryset = Schedule.objects.all()
    serializer_class = serializers.ScheduleSerializer
    permission_classes = [
        IsAuthenticated,
        SchedulePermission,
        APIUserPermissions
    ]

    def update(self, request, *args, **kwargs):
        try:
            with transaction.atomic():
                return recurrent_update(self, request, args, kwargs)
        except Exception as e:
            if isinstance(e, ScheduleException):
                return Response({"message": e.detail},
                                status=e.status_code)

            return Response({"message": "Algo deu errado com o horário informado"},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    def destroy(self, request, *args, **kwargs):
        schedule = self.get_object()
        recurrent_schedules = schedule.recurrent_schedules

        if schedule.meeting_id:
            return Response({"message": "Não é possível deletar um horário associado a uma consulta."},
                            status=status.HTTP_403_FORBIDDEN)

        if recurrent_schedules.exists():
            divergence_schedule = recurrent_schedules.exclude(
                start__time=schedule.start.time(), stop__time=schedule.stop.time())
            recurrent_schedules_has_meeting = recurrent_schedules.exclude(
                meeting_id=None)

            if divergence_schedule.exists() or recurrent_schedules_has_meeting.exists():
                return Response({
                    "message": "Não é possível deletar esse horário pois há divergências em um dos horários recorrentes gerados a partir deste."},
                    status=status.HTTP_403_FORBIDDEN)

        self.perform_destroy(schedule)
        return Response(status=status.HTTP_204_NO_CONTENT)


class ScheduleDoctorViewSet(mixins.RetrieveModelMixin,
                            mixins.CreateModelMixin,
                            mixins.ListModelMixin,
                            viewsets.GenericViewSet):
    """
    ViewSet for the Schedule Doctor class
    """
    queryset = Doctor.objects.all()
    serializer_class = serializers.ProfessionalScheduleSerializer
    filter_backends = [DjangoFilterBackend]
    permission_classes = [
        IsAuthenticated,
        SchedulePermission,
        APIUserPermissions
    ]
    filterset_class = DoctorScheduleFilters

    def get_serializer_class(self):
        if self.action == 'metadata':
            return DoctorSerializer
        if self.action == 'retrieve':
            return serializers.ScheduleSerializerWithProfessional
        return super().get_serializer_class()

    def get_queryset(self):
        user_request = self.request.user

        # admin
        if user_request.active_role.role_name in ['admin', 'financial_administrator']:
            queryset = self.queryset.all().distinct()

        # company adm
        elif user_request.active_role.role_name == 'company_administrator':
            queryset = self.queryset.filter(
                careteam_id__company_id__company_adm__user_id=user_request).distinct()

        # patient
        elif user_request.active_role.role_name == 'patient':
            queryset = self.queryset.filter(
                careteam_id__patient_careteam__user_id=user_request).distinct()

        # secretary
        elif user_request.active_role.role_name == 'secretary':
            queryset = self.queryset.filter(
                careteam_id__secretary_careteam__user_id=user_request).distinct()

        # doctor
        elif user_request.active_role.role_name == 'doctor':
            queryset = self.queryset.filter(
                careteam_id__doctor_careteam__user_id=user_request).distinct()

        # nurse
        elif user_request.active_role.role_name == 'nurse':
            queryset = self.queryset.filter(
                careteam_id__nurse_careteam__user_id=user_request).distinct()

        # auditor
        elif user_request.active_role.role_name == 'auditor':
            queryset = self.queryset.filter(
                careteam_id__auditor_careteam__user_id=user_request).distinct()

        # API_USER
        elif user_request.active_role.role_name == 'api_user':
            queryset = self.queryset.filter(
                careteam_id__company_id__access_company__user_id=user_request).distinct()

        else:
            queryset = self.queryset.none()

        return queryset

    def list(self, request, *args, **kwargs):
        return list_view(self, request, args, kwargs)

    def retrieve(self, request, *args, **kwargs):
        return retrieve_view(self, request, args, kwargs)

    def create(self, request, *args, **kwargs):
        try:
            with transaction.atomic():
                return create_view(Doctor, self, request, args, kwargs)
        except Exception as e:
            if isinstance(e, ScheduleException):
                return Response({"message": e.detail},
                                status=e.status_code)

            return Response({"message": "Algo deu errado com o horário informado"},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class ScheduleNurseViewSet(mixins.ListModelMixin,
                           mixins.CreateModelMixin,
                           mixins.RetrieveModelMixin,
                           viewsets.GenericViewSet):
    """
    ViewSet for the Schedule Nurse class
    """
    queryset = Nurse.objects.all()
    serializer_class = serializers.ProfessionalScheduleSerializer
    permission_classes = [
        IsAuthenticated,
        SchedulePermission,
        APIUserPermissions
    ]
    filterset_class = NurseFilter

    def get_serializer_class(self):
        if self.action == 'metadata':
            return NurseSerializer
        if self.action == 'retrieve':
            return serializers.ScheduleSerializerWithProfessional
        return super().get_serializer_class()

    def get_queryset(self):
        user_request = self.request.user

        # admin
        if user_request.active_role.role_name == 'admin':
            queryset = self.queryset.all().distinct()

        # company adm
        elif user_request.active_role.role_name == 'company_administrator':
            queryset = self.queryset.filter(
                careteam_id__company_id__company_adm__user_id=user_request).distinct()

        # patient
        elif user_request.active_role.role_name == 'patient':
            queryset = self.queryset.filter(
                careteam_id__patient_careteam__user_id=user_request).distinct()

        # secretary
        elif user_request.active_role.role_name == 'secretary':
            queryset = self.queryset.filter(
                careteam_id__secretary_careteam__user_id=user_request).distinct()

        # doctor
        elif user_request.active_role.role_name == 'doctor':
            queryset = self.queryset.filter(
                careteam_id__doctor_careteam__user_id=user_request).distinct()

        # nurse
        elif user_request.active_role.role_name == 'nurse':
            queryset = self.queryset.filter(
                careteam_id__nurse_careteam__user_id=user_request).distinct()

        # auditor
        elif user_request.active_role.role_name == 'auditor':
            queryset = self.queryset.filter(
                careteam_id__auditor_careteam__user_id=user_request).distinct()

        # API_USER
        elif user_request.active_role.role_name == 'api_user':
            queryset = self.queryset.filter(
                careteam_id__company_id__access_company__user_id=user_request).distinct()

        else:
            queryset = self.queryset.none()

        return queryset

    def list(self, request, *args, **kwargs):
        return list_view(self, request, args, kwargs)

    def retrieve(self, request, *args, **kwargs):
        return retrieve_view(self, request, args, kwargs)

    def create(self, request, *args, **kwargs):
        try:
            with transaction.atomic():
                return create_view(Nurse, self, request, args, kwargs)
        except Exception as e:
            if isinstance(e, ScheduleException):
                return Response({"message": e.detail},
                                status=e.status_code)

            return Response({"message": "Algo deu errado com o horário informado"},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)


Editor is loading...
Leave a Comment