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

Adds support for filtering patients by diagnoses #1830

Merged
merged 9 commits into from
Jan 25, 2024
9 changes: 9 additions & 0 deletions care/facility/api/viewsets/icd.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from re import IGNORECASE

from django.http import Http404
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.viewsets import ViewSet
Expand Down Expand Up @@ -29,3 +30,11 @@ def list(self, request):
label=queryset.re_match(r".*" + query + r".*", IGNORECASE)
) # can accept regex from FE if needed.
return Response(serailize_data(queryset[0:100]))

def retrieve(self, request, pk):
from care.facility.static_data.icd11 import get_icd11_diagnosis_object_by_id
rithviknishad marked this conversation as resolved.
Show resolved Hide resolved

obj = get_icd11_diagnosis_object_by_id(pk, as_dict=True)
if not obj:
raise Http404
return Response(obj)
31 changes: 31 additions & 0 deletions care/facility/api/viewsets/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@
)
from care.facility.models.base import covert_choice_dict
from care.facility.models.bed import AssetBed
from care.facility.models.icd11_diagnosis import (
INACTIVE_CONDITION_VERIFICATION_STATUSES,
ConditionVerificationStatus,
)
from care.facility.models.notification import Notification
from care.facility.models.patient_base import (
DISEASE_STATUS_DICT,
Expand Down Expand Up @@ -210,6 +214,33 @@
last_consultation__discharge_date__isnull=True,
)

# Filter consultations by ICD-11 Diagnoses
diagnoses = MultiSelectFilter(method="filter_by_diagnoses")
diagnoses_unconfirmed = MultiSelectFilter(method="filter_by_diagnoses")
diagnoses_provisional = MultiSelectFilter(method="filter_by_diagnoses")
diagnoses_differential = MultiSelectFilter(method="filter_by_diagnoses")
diagnoses_confirmed = MultiSelectFilter(method="filter_by_diagnoses")

def filter_by_diagnoses(self, queryset, name, value):
if not value:
return queryset

Check warning on line 226 in care/facility/api/viewsets/patient.py

View check run for this annotation

Codecov / codecov/patch

care/facility/api/viewsets/patient.py#L226

Added line #L226 was not covered by tests
filter_q = Q(last_consultation__diagnoses__diagnosis_id__in=value.split(","))
if name == "diagnoses":
filter_q &= ~Q(
last_consultation__diagnoses__verification_status__in=INACTIVE_CONDITION_VERIFICATION_STATUSES
)
else:
verification_status = {
"diagnoses_unconfirmed": ConditionVerificationStatus.UNCONFIRMED,
"diagnoses_provisional": ConditionVerificationStatus.PROVISIONAL,
"diagnoses_differential": ConditionVerificationStatus.DIFFERENTIAL,
"diagnoses_confirmed": ConditionVerificationStatus.CONFIRMED,
}[name]
filter_q &= Q(
last_consultation__diagnoses__verification_status=verification_status
)
return queryset.filter(filter_q)


class PatientDRYFilter(DRYPermissionFiltersBase):
def filter_queryset(self, request, queryset, view):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from rest_framework import status
from rest_framework.test import APITestCase

from care.utils.tests.test_utils import TestUtils
Expand Down Expand Up @@ -40,3 +41,13 @@ def test_search_with_disease_code(self):

res = self.search_icd11("1A00 Cholera")
self.assertContains(res, "1A00 Cholera")

def test_get_icd11_by_valid_id(self):
res = self.client.get("/api/v1/icd/133207228/")
self.assertEqual(
res.data["label"], "CA22 Chronic obstructive pulmonary disease"
)

def test_get_icd11_by_invalid_id(self):
res = self.client.get("/api/v1/icd/invalid/")
self.assertEqual(res.status_code, status.HTTP_404_NOT_FOUND)
94 changes: 92 additions & 2 deletions care/facility/tests/test_patient_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
from rest_framework import status
from rest_framework.test import APITestCase

from care.facility.models.icd11_diagnosis import (
ConditionVerificationStatus,
ICD11Diagnosis,
)
from care.facility.models.patient_base import NewDischargeReasonEnum
from care.utils.tests.test_utils import TestUtils

Expand Down Expand Up @@ -244,10 +248,34 @@ def setUpTestData(cls):
cls.consultation.save()
cls.patient.last_consultation = cls.consultation
cls.patient.save()
cls.diagnoses = ICD11Diagnosis.objects.filter(is_leaf=True)[10:15]
cls.create_consultation_diagnosis(
cls.consultation,
cls.diagnoses[0],
verification_status=ConditionVerificationStatus.CONFIRMED,
)
cls.create_consultation_diagnosis(
cls.consultation,
cls.diagnoses[1],
verification_status=ConditionVerificationStatus.DIFFERENTIAL,
)
cls.create_consultation_diagnosis(
cls.consultation,
cls.diagnoses[2],
verification_status=ConditionVerificationStatus.PROVISIONAL,
)
cls.create_consultation_diagnosis(
cls.consultation,
cls.diagnoses[3],
verification_status=ConditionVerificationStatus.UNCONFIRMED,
)

def get_base_url(self) -> str:
return "/api/v1/patient/"

def test_filter_by_patient_no(self):
self.client.force_authenticate(user=self.user)
response = self.client.get("/api/v1/patient/?patient_no=IP5678")
response = self.client.get(self.get_base_url(), {"patient_no": "IP5678"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["count"], 1)
self.assertEqual(
Expand All @@ -257,14 +285,76 @@ def test_filter_by_patient_no(self):
def test_filter_by_location(self):
self.client.force_authenticate(user=self.user)
response = self.client.get(
f"/api/v1/patient/?facility={self.facility.external_id}&location={self.location.external_id}"
self.get_base_url(),
{
"facility": self.facility.external_id,
"location": self.location.external_id,
},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["count"], 1)
self.assertEqual(
response.data["results"][0]["id"], str(self.patient.external_id)
)

def test_filter_by_diagnoses(self):
self.client.force_authenticate(user=self.user)
res = self.client.get(
self.get_base_url(),
{"diagnoses": ",".join([str(x.id) for x in self.diagnoses])},
)
self.assertContains(res, self.patient.external_id)
res = self.client.get(self.get_base_url(), {"diagnoses": self.diagnoses[4].id})
self.assertNotContains(res, self.patient.external_id)

def test_filter_by_diagnoses_unconfirmed(self):
self.client.force_authenticate(user=self.user)
res = self.client.get(
self.get_base_url(),
{"diagnoses_unconfirmed": self.diagnoses[3].id},
)
self.assertContains(res, self.patient.external_id)
res = self.client.get(
self.get_base_url(), {"diagnoses_unconfirmed": self.diagnoses[2].id}
)
self.assertNotContains(res, self.patient.external_id)

def test_filter_by_diagnoses_provisional(self):
self.client.force_authenticate(user=self.user)
res = self.client.get(
self.get_base_url(),
{"diagnoses_provisional": self.diagnoses[2].id},
)
self.assertContains(res, self.patient.external_id)
res = self.client.get(
self.get_base_url(), {"diagnoses_provisional": self.diagnoses[3].id}
)
self.assertNotContains(res, self.patient.external_id)

def test_filter_by_diagnoses_differential(self):
self.client.force_authenticate(user=self.user)
res = self.client.get(
self.get_base_url(),
{"diagnoses_differential": self.diagnoses[1].id},
)
self.assertContains(res, self.patient.external_id)
res = self.client.get(
self.get_base_url(), {"diagnoses_differential": self.diagnoses[0].id}
)
self.assertNotContains(res, self.patient.external_id)

def test_filter_by_diagnoses_confirmed(self):
self.client.force_authenticate(user=self.user)
res = self.client.get(
self.get_base_url(),
{"diagnoses_confirmed": self.diagnoses[0].id},
)
self.assertContains(res, self.patient.external_id)
res = self.client.get(
self.get_base_url(), {"diagnoses_confirmed": self.diagnoses[2].id}
)
self.assertNotContains(res, self.patient.external_id)


class PatientTransferTestCase(TestUtils, APITestCase):
@classmethod
Expand Down
21 changes: 21 additions & 0 deletions care/utils/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
from care.facility.models.asset import Asset, AssetLocation
from care.facility.models.bed import Bed, ConsultationBed
from care.facility.models.facility import FacilityUser
from care.facility.models.icd11_diagnosis import (
ConditionVerificationStatus,
ConsultationDiagnosis,
ICD11Diagnosis,
)
from care.users.models import District, State


Expand Down Expand Up @@ -389,6 +394,22 @@ def create_consultation_bed(
data.update(kwargs)
return ConsultationBed.objects.create(**data)

@classmethod
def create_consultation_diagnosis(
cls,
consultation: PatientConsultation,
diagnosis: ICD11Diagnosis,
verification_status: ConditionVerificationStatus,
**kwargs,
):
data = {
"consultation": consultation,
"diagnosis": diagnosis,
"verification_status": verification_status,
}
data.update(kwargs)
return ConsultationDiagnosis.objects.create(**data)

@classmethod
def clone_object(cls, obj, save=True):
new_obj = obj._meta.model.objects.get(pk=obj.id)
Expand Down
Loading