Skip to content

Commit

Permalink
Fix for Incorrect Discharge Reasons in Patient Transfers and Readmiss…
Browse files Browse the repository at this point in the history
…ions (#1712)

* Add discharge reason to PatientTransferSerializer
and handle patient transfer validation

* remove bed on transfer

* Migration for beds attached to discharged patients

* Apply suggestions from code review

Co-authored-by: Aakash Singh <mail@singhaakash.dev>

* Update patient.py

* lint

* atomic transaction

* add tests

---------

Co-authored-by: Aakash Singh <mail@singhaakash.dev>
  • Loading branch information
Ashesh3 and sainak authored Nov 16, 2023
1 parent 7273298 commit fa5ba42
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 8 deletions.
23 changes: 18 additions & 5 deletions care/facility/api/serializers/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from django.conf import settings
from django.db import transaction
from django.db.models import Q
from django.utils.timezone import localtime, make_aware, now
from django.utils.timezone import make_aware, now
from rest_framework import serializers

from care.abdm.api.serializers.abhanumber import AbhaNumberSerializer
Expand All @@ -27,6 +27,7 @@
PatientNotes,
PatientRegistration,
)
from care.facility.models.bed import ConsultationBed
from care.facility.models.notification import Notification
from care.facility.models.patient_base import (
BLOOD_GROUP_CHOICES,
Expand Down Expand Up @@ -453,10 +454,22 @@ def create(self, validated_data):

def save(self, **kwargs):
self.instance.facility = self.validated_data["facility"]
PatientConsultation.objects.filter(
patient=self.instance, discharge_date__isnull=True
).update(discharge_date=localtime(now()))
self.instance.save()

with transaction.atomic():
consultation = PatientConsultation.objects.filter(
patient=self.instance, discharge_date__isnull=True
).first()

if consultation:
consultation.discharge_date = now()
consultation.discharge_reason = "REF"
consultation.current_bed = None
consultation.save()

ConsultationBed.objects.filter(
consultation=consultation, end_date__isnull=True
).update(end_date=now())
self.instance.save()


class PatientNotesSerializer(serializers.ModelSerializer):
Expand Down
13 changes: 12 additions & 1 deletion care/facility/api/viewsets/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,10 +426,21 @@ def list(self, request, *args, **kwargs):
@action(detail=True, methods=["POST"])
def transfer(self, request, *args, **kwargs):
patient = PatientRegistration.objects.get(external_id=kwargs["external_id"])
facility = Facility.objects.get(external_id=request.data["facility"])

if patient.is_active and facility == patient.facility:
return Response(
{
"Patient": "Patient transfer cannot be completed because the patient has an active consultation in the same facility"
},
status=status.HTTP_406_NOT_ACCEPTABLE,
)

if patient.allow_transfer is False:
return Response(
{"Patient": "Cannot Transfer Patient , Source Facility Does Not Allow"},
{
"Patient": "Patient transfer cannot be completed because the source facility does not permit it"
},
status=status.HTTP_406_NOT_ACCEPTABLE,
)
patient.allow_transfer = False
Expand Down
28 changes: 28 additions & 0 deletions care/facility/migrations/0394_auto_20231114_2219.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 4.2.6 on 2023-11-14 16:49

from django.db import migrations


def free_discharged_beds(apps, schema_editor):
PatientConsultation = apps.get_model("facility", "PatientConsultation")
ConsultationBed = apps.get_model("facility", "ConsultationBed")

for consultation in PatientConsultation.objects.filter(
discharge_date__isnull=False
):
ConsultationBed.objects.filter(
consultation=consultation, end_date__isnull=True
).update(end_date=consultation.discharge_date)


class Migration(migrations.Migration):
dependencies = [
(
"facility",
"0393_rename_diagnosis_patientconsultation_deprecated_diagnosis_and_more",
),
]

operations = [
migrations.RunPython(free_discharged_beds, migrations.RunPython.noop),
]
95 changes: 95 additions & 0 deletions care/facility/tests/test_patient_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,3 +244,98 @@ def test_filter_by_location(self):
self.assertEqual(
response.data["results"][0]["id"], str(self.patient.external_id)
)


class PatientTransferTestCase(TestUtils, APITestCase):
@classmethod
def setUpTestData(cls):
cls.state = cls.create_state()
cls.district = cls.create_district(cls.state)
cls.local_body = cls.create_local_body(cls.district)
cls.super_user = cls.create_super_user("su", cls.district)
cls.facility = cls.create_facility(cls.super_user, cls.district, cls.local_body)
cls.destination_facility = cls.create_facility(
cls.super_user, cls.district, cls.local_body, name="Facility 2"
)
cls.location = cls.create_asset_location(cls.facility)
cls.user = cls.create_user(
"doctor1", cls.district, home_facility=cls.facility, user_type=15
)
cls.patient = cls.create_patient(cls.district, cls.facility)
cls.consultation = cls.create_consultation(
patient_no="IP5678",
patient=cls.patient,
facility=cls.facility,
created_by=cls.user,
suggestion="A",
admission_date=now(),
discharge_date=None, # Patient is currently admitted
discharge_reason=None,
)
cls.bed = cls.create_bed(cls.facility, cls.location)
cls.consultation_bed = cls.create_consultation_bed(cls.consultation, cls.bed)
cls.consultation.current_bed = cls.consultation_bed
cls.consultation.save()
cls.patient.last_consultation = cls.consultation
cls.patient.save()

def test_patient_transfer(self):
self.client.force_authenticate(user=self.super_user)
response = self.client.post(
f"/api/v1/patient/{self.patient.external_id}/transfer/",
{
"date_of_birth": "1992-04-01",
"facility": self.destination_facility.external_id,
},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)

# Refresh patient data
self.patient.refresh_from_db()
self.consultation.refresh_from_db()

# Assert the patient's facility has been updated
self.assertEqual(self.patient.facility, self.destination_facility)

# Assert the consultation discharge reason and date are set correctly
self.assertEqual(self.consultation.discharge_reason, "REF")
self.assertIsNotNone(self.consultation.discharge_date)

def test_transfer_with_active_consultation_same_facility(self):
# Set the patient's facility to allow transfers
self.patient.allow_transfer = True
self.patient.save()

# Ensure transfer fails if the patient has an active consultation
self.client.force_authenticate(user=self.super_user)
response = self.client.post(
f"/api/v1/patient/{self.patient.external_id}/transfer/",
{
"date_of_birth": "1992-04-01",
"facility": self.facility.external_id,
},
)
self.assertEqual(response.status_code, status.HTTP_406_NOT_ACCEPTABLE)
self.assertEqual(
response.data["Patient"],
"Patient transfer cannot be completed because the patient has an active consultation in the same facility",
)

def test_transfer_disallowed_by_facility(self):
# Set the patient's facility to disallow transfers
self.patient.allow_transfer = False
self.patient.save()

self.client.force_authenticate(user=self.super_user)
response = self.client.post(
f"/api/v1/patient/{self.patient.external_id}/transfer/",
{
"date_of_birth": "1992-04-01",
"facility": self.destination_facility.external_id,
},
)
self.assertEqual(response.status_code, status.HTTP_406_NOT_ACCEPTABLE)
self.assertEqual(
response.data["Patient"],
"Patient transfer cannot be completed because the source facility does not permit it",
)
2 changes: 0 additions & 2 deletions care/utils/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@ class TestUtils:
Base class for tests, handles most of the test setup and tools for setting up data
"""

maxDiff = None

def setUp(self) -> None:
self.client.force_login(self.user)

Expand Down

0 comments on commit fa5ba42

Please sign in to comment.