From e64fc1f671c4811d336082ee246c4f7bdc7c30ae Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Wed, 17 Jan 2024 15:41:17 +0530 Subject: [PATCH] Allows diagnoses starting with disease code to be available in ICD11 Search (#1823) * allow diagnoses starting with disease code to be searchable * Add tests * Fix regex pattern and improve tests * ensure ICD11 is lazy loaded --- care/abdm/utils/fhir.py | 3 +- .../api/serializers/consultation_diagnosis.py | 3 +- care/facility/api/viewsets/icd.py | 5 +-- care/facility/models/patient.py | 3 +- care/facility/static_data/icd11.py | 9 ++-- care/facility/tests/test_icd11_search.py | 42 +++++++++++++++++++ .../utils/reports/discharge_summary.py | 3 +- care/hcx/api/viewsets/gateway.py | 3 +- 8 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 care/facility/tests/test_icd11_search.py diff --git a/care/abdm/utils/fhir.py b/care/abdm/utils/fhir.py index d862f079fe..c504b64e27 100644 --- a/care/abdm/utils/fhir.py +++ b/care/abdm/utils/fhir.py @@ -34,7 +34,6 @@ from care.facility.models.file_upload import FileUpload from care.facility.models.icd11_diagnosis import REVERSE_CONDITION_VERIFICATION_STATUSES from care.facility.models.patient_investigation import InvestigationValue -from care.facility.static_data.icd11 import get_icd11_diagnosis_object_by_id class Fhir: @@ -138,6 +137,8 @@ def _organization(self): return self._organization_profile def _condition(self, diagnosis_id, verification_status): + from care.facility.static_data.icd11 import get_icd11_diagnosis_object_by_id + diagnosis = get_icd11_diagnosis_object_by_id(diagnosis_id) [code, label] = diagnosis.label.split(" ", 1) condition_profile = Condition( diff --git a/care/facility/api/serializers/consultation_diagnosis.py b/care/facility/api/serializers/consultation_diagnosis.py index 62f5d80a6a..90e6810b32 100644 --- a/care/facility/api/serializers/consultation_diagnosis.py +++ b/care/facility/api/serializers/consultation_diagnosis.py @@ -7,7 +7,6 @@ ConsultationDiagnosis, ) from care.facility.models.icd11_diagnosis import ICD11Diagnosis -from care.facility.static_data.icd11 import get_icd11_diagnosis_object_by_id from care.users.api.serializers.user import UserBaseMinimumSerializer @@ -31,6 +30,8 @@ class ConsultationDiagnosisSerializer(serializers.ModelSerializer): created_by = UserBaseMinimumSerializer(read_only=True) def get_diagnosis_object(self, obj): + from care.facility.static_data.icd11 import get_icd11_diagnosis_object_by_id + return get_icd11_diagnosis_object_by_id(obj.diagnosis_id, as_dict=True) class Meta: diff --git a/care/facility/api/viewsets/icd.py b/care/facility/api/viewsets/icd.py index d4de44d9f4..8bcfc0d25b 100644 --- a/care/facility/api/viewsets/icd.py +++ b/care/facility/api/viewsets/icd.py @@ -22,11 +22,10 @@ class ICDViewSet(ViewSet): def list(self, request): from care.facility.static_data.icd11 import ICDDiseases - queryset = ICDDiseases + queryset = ICDDiseases.where(has_code=True) if request.GET.get("query", False): query = request.GET.get("query") queryset = queryset.where( - label=queryset.re_match(r".*" + query + r".*", IGNORECASE), - is_leaf=True, + label=queryset.re_match(r".*" + query + r".*", IGNORECASE) ) # can accept regex from FE if needed. return Response(serailize_data(queryset[0:100])) diff --git a/care/facility/models/patient.py b/care/facility/models/patient.py index 4085a5da5b..0eb568d869 100644 --- a/care/facility/models/patient.py +++ b/care/facility/models/patient.py @@ -35,7 +35,6 @@ REVERSE_ROUTE_TO_FACILITY_CHOICES, ) from care.facility.models.patient_consultation import PatientConsultation -from care.facility.static_data.icd11 import get_icd11_diagnoses_objects_by_ids from care.users.models import GENDER_CHOICES, REVERSE_GENDER_CHOICES, User from care.utils.models.base import BaseManager, BaseModel from care.utils.models.validators import mobile_or_landline_number_validator @@ -539,6 +538,8 @@ def format_as_time(time): return time.strftime("%H:%M") def format_diagnoses(diagnosis_ids): + from care.facility.static_data.icd11 import get_icd11_diagnoses_objects_by_ids + diagnoses = get_icd11_diagnoses_objects_by_ids(diagnosis_ids) return ", ".join([diagnosis["label"] for diagnosis in diagnoses]) diff --git a/care/facility/static_data/icd11.py b/care/facility/static_data/icd11.py index dbdcb176a7..8afc87228a 100644 --- a/care/facility/static_data/icd11.py +++ b/care/facility/static_data/icd11.py @@ -1,10 +1,13 @@ import contextlib +import re from django.db import connection from littletable import Table from care.facility.models.icd11_diagnosis import ICD11Diagnosis +DISEASE_CODE_PATTERN = r"^(?:[A-Z]+\d|\d+[A-Z])[A-Z\d.]*\s" + def fetch_from_db(): # This is a hack to prevent the migration from failing when the table does not exist @@ -14,11 +17,11 @@ def fetch_from_db(): { "id": str(diagnosis["id"]), "label": diagnosis["label"], - "is_leaf": diagnosis["is_leaf"], + "has_code": bool(re.match(DISEASE_CODE_PATTERN, diagnosis["label"])), "chapter": diagnosis["meta_chapter_short"], } - for diagnosis in ICD11Diagnosis.objects.filter().values( - "id", "label", "is_leaf", "meta_chapter_short" + for diagnosis in ICD11Diagnosis.objects.values( + "id", "label", "meta_chapter_short" ) ] return [] diff --git a/care/facility/tests/test_icd11_search.py b/care/facility/tests/test_icd11_search.py new file mode 100644 index 0000000000..933ed1b74e --- /dev/null +++ b/care/facility/tests/test_icd11_search.py @@ -0,0 +1,42 @@ +from rest_framework.test import APITestCase + +from care.utils.tests.test_utils import TestUtils + + +class TestICD11Api(TestUtils, APITestCase): + @classmethod + def setUpTestData(cls) -> None: + 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.user = cls.create_user( + "icd11_doctor", cls.district, home_facility=cls.facility + ) + + def search_icd11(self, query): + return self.client.get("/api/v1/icd/", {"query": query}) + + def test_search_no_disease_code(self): + res = self.search_icd11("14 Diseases of the skin") + self.assertNotContains(res, "14 Diseases of the skin") + + res = self.search_icd11("Acute effects of ionizing radiation on the skin") + self.assertNotContains(res, "Acute effects of ionizing radiation on the skin") + + def test_search_with_disease_code(self): + res = self.search_icd11("aCuTe radiodermatitis following radiotherapy") + self.assertContains(res, "EL60 Acute radiodermatitis following radiotherapy") + + res = self.search_icd11("cutaneous insect bite reactions") + self.assertContains(res, "EK50.0 Cutaneous insect bite reactions") + + res = self.search_icd11("Haemorrhage of anus and rectum") + self.assertContains(res, "ME24.A1 Haemorrhage of anus and rectum") + + res = self.search_icd11("ME24.A1") + self.assertContains(res, "ME24.A1 Haemorrhage of anus and rectum") + + res = self.search_icd11("1A00 Cholera") + self.assertContains(res, "1A00 Cholera") diff --git a/care/facility/utils/reports/discharge_summary.py b/care/facility/utils/reports/discharge_summary.py index 48af9e6c66..0a8037ab0d 100644 --- a/care/facility/utils/reports/discharge_summary.py +++ b/care/facility/utils/reports/discharge_summary.py @@ -25,7 +25,6 @@ ACTIVE_CONDITION_VERIFICATION_STATUSES, ConditionVerificationStatus, ) -from care.facility.static_data.icd11 import get_icd11_diagnoses_objects_by_ids from care.hcx.models.policy import Policy logger = logging.getLogger(__name__) @@ -50,6 +49,8 @@ def clear_lock(consultation_ext_id: str): def get_diagnoses_data(consultation: PatientConsultation): + from care.facility.static_data.icd11 import get_icd11_diagnoses_objects_by_ids + entries = ( consultation.diagnoses.filter( verification_status__in=ACTIVE_CONDITION_VERIFICATION_STATUSES diff --git a/care/hcx/api/viewsets/gateway.py b/care/hcx/api/viewsets/gateway.py index e04f92ebe2..5363fd427e 100644 --- a/care/hcx/api/viewsets/gateway.py +++ b/care/hcx/api/viewsets/gateway.py @@ -14,7 +14,6 @@ from care.facility.models.file_upload import FileUpload from care.facility.models.icd11_diagnosis import ConditionVerificationStatus from care.facility.models.patient_consultation import PatientConsultation -from care.facility.static_data.icd11 import get_icd11_diagnosis_object_by_id from care.facility.utils.reports.discharge_summary import ( generate_discharge_report_signed_url, ) @@ -106,6 +105,8 @@ def check_eligibility(self, request): @extend_schema(tags=["hcx"], request=MakeClaimSerializer()) @action(detail=False, methods=["post"]) def make_claim(self, request): + from care.facility.static_data.icd11 import get_icd11_diagnosis_object_by_id + data = request.data serializer = MakeClaimSerializer(data=data)