Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor discharge summary endpoints #1252

Merged
merged 20 commits into from
May 16, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 57 additions & 14 deletions care/facility/api/serializers/patient_consultation.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
SuggestionChoices,
)
from care.facility.models.patient_consultation import PatientConsultation
from care.facility.static_data.icd11 import get_icd11_diagnoses_objects_by_ids
from care.users.api.serializers.user import (
UserAssignedSerializer,
UserBaseMinimumSerializer,
Expand Down Expand Up @@ -97,23 +98,13 @@ class PatientConsultationSerializer(serializers.ModelSerializer):
read_only=True
)

def get_icd11_diagnoses_objects_by_ids(self, diagnoses_ids):
from care.facility.static_data.icd11 import ICDDiseases

diagnosis_objects = []
for diagnosis in diagnoses_ids:
try:
diagnosis_object = ICDDiseases.by.id[diagnosis].__dict__
diagnosis_objects.append(diagnosis_object)
except BaseException:
pass
return diagnosis_objects

def get_icd11_diagnoses_object(self, consultation):
return self.get_icd11_diagnoses_objects_by_ids(consultation.icd11_diagnoses)
return get_icd11_diagnoses_objects_by_ids(consultation.icd11_diagnoses)

def get_icd11_provisional_diagnoses_object(self, consultation):
return self.get_icd11_diagnoses_objects_by_ids(
return get_icd11_diagnoses_objects_by_ids(
consultation.icd11_provisional_diagnoses
)

Expand All @@ -137,7 +128,6 @@ def validate_bed_number(self, bed_number):
return bed_number

def update(self, instance, validated_data):

instance.last_edited_by = self.context["request"].user

if instance.discharge_date:
Expand Down Expand Up @@ -203,7 +193,6 @@ def update(self, instance, validated_data):
return consultation

def create(self, validated_data):

action = -1
review_interval = -1
if "action" in validated_data:
Expand Down Expand Up @@ -385,6 +374,60 @@ def validate(self, attrs):
return validated


class PatientConsultationDischargeSerializer(serializers.ModelSerializer):
discharge_reason = serializers.ChoiceField(
choices=DISCHARGE_REASON_CHOICES, required=True
)
discharge_notes = serializers.CharField(required=True)

discharge_date = serializers.DateTimeField(required=False)
discharge_prescription = serializers.JSONField(required=False)
discharge_prn_prescription = serializers.JSONField(required=False)

death_datetime = serializers.DateTimeField(required=False, allow_null=True)
death_confirmed_doctor = serializers.CharField(required=False, allow_null=True)

class Meta:
model = PatientConsultation
fields = (
"discharge_reason",
"discharge_notes",
"discharge_date",
"discharge_prescription",
"discharge_prn_prescription",
"death_datetime",
"death_confirmed_doctor",
)

def validate(self, attrs):
print(attrs)
sainak marked this conversation as resolved.
Show resolved Hide resolved
if attrs.get("discharge_reason") == "EXP":
if not attrs.get("death_datetime"):
raise ValidationError({"death_datetime": "This field is required"})
if not attrs.get("death_confirmed_doctor"):
raise ValidationError(
{"death_confirmed_doctor": "This field is required"}
)
attrs["discharge_date"] = now()
elif not attrs.get("discharge_date"):
raise ValidationError({"discharge_date": "This field is required"})
return attrs

def save(self, **kwargs):
super().save(**kwargs)
sainak marked this conversation as resolved.
Show resolved Hide resolved
patient: PatientRegistration = self.instance.patient
patient.is_active = False
patient.allow_transfer = True
patient.review_time = None
patient.save(update_fields=["allow_transfer", "is_active", "review_time"])
ConsultationBed.objects.filter(
consultation=self.instance, end_date__isnull=True
).update(end_date=now())
sainak marked this conversation as resolved.
Show resolved Hide resolved

def create(self, validated_data):
raise NotImplementedError


class PatientConsultationIDSerializer(serializers.ModelSerializer):
consultation_id = serializers.UUIDField(source="external_id", read_only=True)
patient_id = serializers.UUIDField(source="patient.external_id", read_only=True)
Expand Down
84 changes: 3 additions & 81 deletions care/facility/api/viewsets/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

from django.conf import settings
from django.contrib.postgres.search import TrigramSimilarity
from django.core.validators import validate_email
from django.db import models
from django.db.models import Case, When
from django.db.models.query_utils import Q
Expand Down Expand Up @@ -51,9 +50,8 @@
ShiftingRequest,
)
from care.facility.models.base import covert_choice_dict
from care.facility.models.bed import AssetBed, ConsultationBed
from care.facility.models.bed import AssetBed
from care.facility.models.patient_base import DISEASE_STATUS_DICT
from care.facility.tasks.patient.discharge_report import generate_discharge_report
from care.users.models import User
from care.utils.cache.cache_allowed_facilities import get_accessible_facilities
from care.utils.filters import CareChoiceFilter, MultiSelectFilter
Expand Down Expand Up @@ -397,79 +395,6 @@ def list(self, request, *args, **kwargs):

return super(PatientViewSet, self).list(request, *args, **kwargs)

@action(detail=True, methods=["POST"])
def discharge_patient(self, request, *args, **kwargs):
discharged = bool(request.data.get("discharge", False))
patient = self.get_object()
patient.is_active = discharged
patient.allow_transfer = not discharged
patient.review_time = None
patient.save(update_fields=["allow_transfer", "is_active", "review_time"])
last_consultation = (
PatientConsultation.objects.filter(patient=patient).order_by("-id").first()
)
current_time = localtime(now())
if last_consultation:
reason = request.data.get("discharge_reason")
notes = request.data.get("discharge_notes", "")
if reason not in DISCHARGE_REASONS:
raise serializers.ValidationError(
{"discharge_reason": "discharge reason is not valid"}
)
discharge_date = request.data.get("discharge_date", "")
last_consultation.discharge_reason = reason
last_consultation.discharge_notes = notes
if last_consultation.discharge_date is None:
if discharge_date:
last_consultation.discharge_date = discharge_date
else:
last_consultation.discharge_date = current_time
last_consultation.current_bed = None
if reason == "EXP":
death_datetime = request.data.get("death_datetime")
death_confirmed_doctor = request.data.get("death_confirmed_doctor")
if death_datetime is None:
raise serializers.ValidationError(
{"death_datetime": "Please provide death date and time"}
)
if death_confirmed_doctor is None:
raise serializers.ValidationError(
{"death_confirmed_doctor": "Please provide doctor details"}
)
last_consultation.death_datetime = death_datetime
last_consultation.death_confirmed_doctor = death_confirmed_doctor
if reason == "REC":
discharge_prescription = request.data.get("discharge_prescription", [])
discharge_prn_prescription = request.data.get(
"discharge_prn_prescription", []
)
if discharge_date is None:
raise serializers.ValidationError(
{"discharge_date": "Please set the discharge date"}
)
last_consultation.discharge_prescription = discharge_prescription
last_consultation.discharge_prn_prescription = (
discharge_prn_prescription
)
last_consultation.discharge_date = discharge_date
last_consultation.save()
ConsultationBed.objects.filter(
consultation=last_consultation, end_date__isnull=True
).update(end_date=current_time)

return Response(status=status.HTTP_200_OK)

@action(detail=True, methods=["POST"])
def discharge_summary(self, request, *args, **kwargs):
patient = self.get_object()
email = request.data.get("email", "")
try:
validate_email(email)
except Exception:
email = request.user.email
generate_discharge_report.delay(patient.id, email)
return Response(status=status.HTTP_200_OK)

@action(detail=True, methods=["POST"])
def transfer(self, request, *args, **kwargs):
patient = PatientRegistration.objects.get(
Expand All @@ -481,8 +406,8 @@ def transfer(self, request, *args, **kwargs):
{"Patient": "Cannot Transfer Patient , Source Facility Does Not Allow"},
status=status.HTTP_406_NOT_ACCEPTABLE,
)
patient.is_active = True
patient.allow_transfer = False
patient.is_active = True
serializer = self.get_serializer_class()(patient, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
Expand All @@ -497,10 +422,7 @@ def transfer(self, request, *args, **kwargs):
~Q(status__in=[30, 50, 80]), patient=patient
):
shifting_request.status = 30
shifting_request.comments = (
shifting_request.comments
+ f"\n The shifting request was auto rejected by the system as the patient was moved to {patient.facility.name}"
)
shifting_request.comments = f"{shifting_request.comments}\n The shifting request was auto rejected by the system as the patient was moved to {patient.facility.name}"
shifting_request.save(update_fields=["status", "comments"])
return Response(data=response_serializer.data, status=status.HTTP_200_OK)

Expand Down
94 changes: 93 additions & 1 deletion care/facility/api/viewsets/patient_consultation.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,35 @@
import time
from datetime import timedelta

from django.core.validators import validate_email
from django.db.models.query_utils import Q
from django.shortcuts import render
from django_filters import rest_framework as filters
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from dry_rest_permissions.generics import DRYPermissions
from rest_framework import mixins
from rest_framework import mixins, status
from rest_framework.decorators import action
from rest_framework.exceptions import NotFound
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet
from django.utils import timezone

from care.facility.api.serializers.file_upload import FileUploadRetrieveSerializer
from care.facility.api.serializers.patient_consultation import (
PatientConsultationDischargeSerializer,
PatientConsultationIDSerializer,
PatientConsultationSerializer,
)
from care.facility.api.viewsets.mixins.access import AssetUserAccessMixin
from care.facility.models.file_upload import FileUpload
from care.facility.models.mixins.permissions.asset import IsAssetUser
from care.facility.models.patient_consultation import PatientConsultation
from care.facility.tasks.patient.discharge_report import (
email_discharge_summary,
generate_and_upload_discharge_summary,
)
from care.users.models import User
from care.utils.cache.cache_allowed_facilities import get_accessible_facilities

Expand Down Expand Up @@ -45,6 +59,14 @@ class PatientConsultationViewSet(
filter_backends = (filters.DjangoFilterBackend,)
filterset_class = PatientConsultationFilter

def get_serializer_class(self):
if self.action == "patient_from_asset":
return PatientConsultationIDSerializer
elif self.action == "discharge_patient":
return PatientConsultationDischargeSerializer
else:
return self.serializer_class

def get_permissions(self):
if self.action == "patient_from_asset":
return (IsAssetUser(),)
Expand All @@ -67,6 +89,76 @@ def get_queryset(self):
applied_filters |= Q(patient__assigned_to=self.request.user)
return self.queryset.filter(applied_filters)

@action(detail=True, methods=["POST"])
def discharge_patient(self, request, *args, **kwargs):
consultation = self.get_object()
serializer = self.get_serializer(consultation, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
generate_and_upload_discharge_summary.delay(consultation.external_id)
return Response(status=status.HTTP_200_OK)

@swagger_auto_schema(
operation_description="Get the discharge summary",
responses={
200: "Success",
},
)
@action(detail=True, methods=["GET"])
def preview_discharge_summary(self, request, *args, **kwargs):
consultation = self.get_object()
file = (
FileUpload.objects.filter(
file_type=FileUpload.FileType.DISCHARGE_SUMMARY.value,
associating_id=consultation.external_id,
)
.order_by("-created_date")
.first()
)
if file is not None and file.upload_completed:
return Response(FileUploadRetrieveSerializer(file).data)

if file.created_date <= timezone.now() - timedelta(minutes=10):
# If the file is not uploaded in 10 minutes, delete the file and generate a new one
file.delete()
file = None

if file is None:
generate_and_upload_discharge_summary.delay(consultation.external_id)
time.sleep(2) # Wait for 2 seconds for the file to be generated

raise Response(
{
"message": "Discharge summary is not ready yet. Please try again after a few moments."
},
status=status.HTTP_404_NOT_FOUND,
)

@swagger_auto_schema(
operation_description="Email the discharge summary to the user",
request_body=openapi.Schema(
type=openapi.TYPE_OBJECT,
properties={
"email": openapi.Schema(
type=openapi.TYPE_STRING, description="Email address"
)
},
required=["email"],
),
responses={200: "Success"},
)
@action(detail=True, methods=["POST"])
def email_discharge_summary(self, request, *args, **kwargs):
consultation = self.get_object()
email = request.data.get("email", "")
try:
validate_email(email)
except Exception:
email = request.user.email

email_discharge_summary.delay(consultation.external_id, email)
return Response(status=status.HTTP_200_OK)

@swagger_auto_schema(
responses={200: PatientConsultationIDSerializer},
)
Expand Down
Loading