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

Add new medical history options #2190

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
7766a2a
add new fields to disease table
AshrafMd-1 May 22, 2024
c586a14
Update patient_base.py
AshrafMd-1 May 22, 2024
2c9a72c
Merge branch 'develop' into medical-history
AshrafMd-1 May 22, 2024
bed4680
fix disease_map
AshrafMd-1 May 23, 2024
5b538f1
add migrations
AshrafMd-1 May 24, 2024
027fc1b
add validation
AshrafMd-1 May 26, 2024
b1c6200
add tests
AshrafMd-1 May 28, 2024
c46e911
Merge branch 'coronasafe:develop' into medical-history
AshrafMd-1 May 29, 2024
2731569
fix lint
AshrafMd-1 Jun 4, 2024
23dc5f1
Merge branch 'develop' into medical-history
AshrafMd-1 Jun 4, 2024
65a5c2c
merge migrations
AshrafMd-1 Jun 4, 2024
ac563dd
Merge branch 'coronasafe:develop' into medical-history
AshrafMd-1 Jun 6, 2024
28c6696
fix lint
AshrafMd-1 Jun 7, 2024
07a07b0
add migrations
AshrafMd-1 Jun 7, 2024
65e1f1e
Merge branch 'develop' into medical-history
AshrafMd-1 Jun 7, 2024
7a1452d
Merge branch 'coronasafe:develop' into medical-history
AshrafMd-1 Jun 13, 2024
fbdeeb7
fix migrations
AshrafMd-1 Jun 15, 2024
bcf4520
Merge branch 'coronasafe:develop' into medical-history
AshrafMd-1 Jul 5, 2024
23e1e8b
fix migration
AshrafMd-1 Jul 5, 2024
98928f6
Merge branch 'develop' into medical-history
AshrafMd-1 Jul 23, 2024
9f1cc6f
new migration
AshrafMd-1 Jul 23, 2024
4dba549
Merge branch 'develop' into medical-history
AshrafMd-1 Jul 23, 2024
74fdd95
Merge branch 'develop' into medical-history
AshrafMd-1 Jul 29, 2024
0050291
Merge branch 'develop' into medical-history
AshrafMd-1 Aug 9, 2024
1c02013
Merge branch 'develop' into medical-history
nihal467 Aug 20, 2024
3f7d38f
Merge branch 'develop' into medical-history
vigneshhari Aug 21, 2024
6cabe89
Merge branch 'coronasafe:develop' into medical-history
AshrafMd-1 Aug 23, 2024
c960888
fix migrations
AshrafMd-1 Aug 23, 2024
61d6a49
fix lint
AshrafMd-1 Aug 23, 2024
b656f93
Merge branch 'develop' into medical-history
AshrafMd-1 Aug 23, 2024
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
44 changes: 44 additions & 0 deletions care/facility/api/serializers/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from care.facility.models.patient import PatientNotesEdit
from care.facility.models.patient_base import (
BLOOD_GROUP_CHOICES,
CANCER_TYPE,
DISEASE_STATUS_CHOICES,
DiseaseStatusEnum,
NewDischargeReasonEnum,
Expand Down Expand Up @@ -158,6 +159,9 @@
class MedicalHistorySerializer(serializers.Serializer):
disease = ChoiceField(choices=DISEASE_CHOICES)
details = serializers.CharField(required=False, allow_blank=True)
status = serializers.CharField(required=False, allow_null=True)
duration = serializers.CharField(required=False, allow_null=True)
type = serializers.CharField(required=False, allow_null=True)

facility = ExternalIdSerializerField(
queryset=Facility.objects.all(), required=False
Expand Down Expand Up @@ -272,6 +276,46 @@
if validated.get("vaccine_name") is None:
raise serializers.ValidationError("Vaccine name cannot be null")

if medical_history := validated.get("medical_history"):

for disease in medical_history:
disease_id = disease.get("disease")

Check warning on line 282 in care/facility/api/serializers/patient.py

View check run for this annotation

Codecov / codecov/patch

care/facility/api/serializers/patient.py#L282

Added line #L282 was not covered by tests
if disease_id == 1:
continue

Check warning on line 284 in care/facility/api/serializers/patient.py

View check run for this annotation

Codecov / codecov/patch

care/facility/api/serializers/patient.py#L284

Added line #L284 was not covered by tests

# Handle cancer cases
if disease_id == 7:
cancer_type = disease.get("type")

Check warning on line 288 in care/facility/api/serializers/patient.py

View check run for this annotation

Codecov / codecov/patch

care/facility/api/serializers/patient.py#L288

Added line #L288 was not covered by tests
if cancer_type:
gender = validated.get("gender")

Check warning on line 290 in care/facility/api/serializers/patient.py

View check run for this annotation

Codecov / codecov/patch

care/facility/api/serializers/patient.py#L290

Added line #L290 was not covered by tests
if cancer_type not in CANCER_TYPE:
raise serializers.ValidationError("Invalid cancer type")

Check warning on line 292 in care/facility/api/serializers/patient.py

View check run for this annotation

Codecov / codecov/patch

care/facility/api/serializers/patient.py#L292

Added line #L292 was not covered by tests

if (gender == 1 and CANCER_TYPE[cancer_type] in {"1", "5"}) or (
gender == 2 and CANCER_TYPE[cancer_type] == "8"
):
raise serializers.ValidationError(

Check warning on line 297 in care/facility/api/serializers/patient.py

View check run for this annotation

Codecov / codecov/patch

care/facility/api/serializers/patient.py#L297

Added line #L297 was not covered by tests
"Invalid cancer type for specified gender"
)

# Handle TB cases
elif disease_id == 14:
status = disease.get("status")

Check warning on line 303 in care/facility/api/serializers/patient.py

View check run for this annotation

Codecov / codecov/patch

care/facility/api/serializers/patient.py#L303

Added line #L303 was not covered by tests
if status and status not in {"Active", "Old"}:
raise serializers.ValidationError("Invalid TB status")

Check warning on line 305 in care/facility/api/serializers/patient.py

View check run for this annotation

Codecov / codecov/patch

care/facility/api/serializers/patient.py#L305

Added line #L305 was not covered by tests

if duration := disease.get("duration"):
try:
duration = float(duration)

Check warning on line 309 in care/facility/api/serializers/patient.py

View check run for this annotation

Codecov / codecov/patch

care/facility/api/serializers/patient.py#L308-L309

Added lines #L308 - L309 were not covered by tests
if duration < 0:
raise serializers.ValidationError(

Check warning on line 311 in care/facility/api/serializers/patient.py

View check run for this annotation

Codecov / codecov/patch

care/facility/api/serializers/patient.py#L311

Added line #L311 was not covered by tests
"TB duration cannot be negative"
)
except ValueError:
raise serializers.ValidationError(

Check warning on line 315 in care/facility/api/serializers/patient.py

View check run for this annotation

Codecov / codecov/patch

care/facility/api/serializers/patient.py#L314-L315

Added lines #L314 - L315 were not covered by tests
"Duration must be a number"
)

return validated

def check_external_entry(self, srf_id):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Generated by Django 4.2.10 on 2024-08-23 07:19

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("facility", "0449_merge_20240822_1343"),
]

operations = [
migrations.AddField(
model_name="disease",
name="duration",
field=models.IntegerField(blank=True, null=True),
),
migrations.AddField(
model_name="disease",
name="status",
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name="disease",
name="type",
field=models.TextField(blank=True, null=True),
),
migrations.AlterField(
model_name="disease",
name="disease",
field=models.IntegerField(
choices=[
(1, "NO"),
(2, "Diabetes"),
(3, "Heart Disease"),
(4, "HyperTension"),
(5, "Chronic Renal Disease"),
(6, "Other Chronic Lung Diseases"),
(7, "Cancer"),
(8, "OTHER"),
(9, "COPD"),
(10, "Bronchitis"),
(11, "Chronic Neurological Or Neuromuscular Disease"),
(12, "Immunocompromised Condition"),
(13, "Liver Disease"),
(14, "TB"),
(15, "Asthma"),
]
),
),
]
3 changes: 3 additions & 0 deletions care/facility/models/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,9 @@ class Disease(models.Model):
)
disease = models.IntegerField(choices=DISEASE_CHOICES)
details = models.TextField(blank=True, null=True)
status = models.TextField(blank=True, null=True)
duration = models.IntegerField(blank=True, null=True)
type = models.TextField(blank=True, null=True)
deleted = models.BooleanField(default=False)

objects = BaseManager()
Expand Down
29 changes: 27 additions & 2 deletions care/facility/models/patient_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,41 @@ def reverse_choices(choices):
(34, "NEW LOSS OF SMELL"),
]

CANCER_TYPE = {
"Breast": "1",
"Lung": "2",
"Skin": "3",
"Colorectal": "4",
"Uterus": "5",
"Leukaemia": "6",
"Bladder": "7",
"Prostate": "8",
"Melanoma": "9",
"Lymphoma": "10",
"Brain": "11",
"Liver": "12",
"Thyroid": "13",
"Others": "14",
}

DISEASE_CHOICES_MAP = {
"NO": 1,
"Diabetes": 2,
"Heart Disease": 3,
"HyperTension": 4,
"Kidney Diseases": 5,
"Lung Diseases/Asthma": 6,
"Chronic Renal Disease": 5,
"Other Chronic Lung Diseases": 6,
"Cancer": 7,
"OTHER": 8,
"COPD": 9,
"Bronchitis": 10,
"Chronic Neurological Or Neuromuscular Disease": 11,
"Immunocompromised Condition": 12,
"Liver Disease": 13,
"TB": 14,
"Asthma": 15,
}

DISEASE_CHOICES = [(v, k) for k, v in DISEASE_CHOICES_MAP.items()]

COVID_CATEGORY_CHOICES = [
Expand Down
120 changes: 120 additions & 0 deletions care/facility/models/tests/test_patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,123 @@ def test_year_of_birth_validation(self):
response = self.client.post("/api/v1/patient/", sample_data, format="json")
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn("year_of_birth", response.data)

def test_cancer_type_is_invalid(self):
dist_admin = self.create_user("dist_admin", self.district, user_type=30)
sample_data = {
"facility": self.facility.external_id,
"blood_group": "AB+",
"gender": 1,
"date_of_birth": None,
"year_of_birth": now().year - 2,
"disease_status": "NEGATIVE",
"emergency_phone_number": "+919000000666",
"is_vaccinated": "false",
"number_of_doses": 0,
"phone_number": "+919000044343",
"medical_history": {
"disease": "Cancer",
"details": "",
"type": "not_cancer",
},
}
self.client.force_authenticate(user=dist_admin)
response = self.client.post("/api/v1/patient/", sample_data, format="json")
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn("medical_history", response.data)

def test_cancer_type_wrong_for_gender(self):
dist_admin = self.create_user("dist_admin", self.district, user_type=30)
sample_data = {
"facility": self.facility.external_id,
"blood_group": "AB+",
"gender": 1,
"date_of_birth": None,
"year_of_birth": now().year - 2,
"disease_status": "NEGATIVE",
"emergency_phone_number": "+919000000666",
"is_vaccinated": "false",
"number_of_doses": 0,
"phone_number": "+919000044343",
"medical_history": {
"disease": "Cancer",
"details": "",
"type": "Breast",
},
}
self.client.force_authenticate(user=dist_admin)
response = self.client.post("/api/v1/patient/", sample_data, format="json")
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn("medical_history", response.data)

def test_tb_type_invalid(self):
dist_admin = self.create_user("dist_admin", self.district, user_type=30)
sample_data = {
"facility": self.facility.external_id,
"blood_group": "AB+",
"gender": 1,
"date_of_birth": None,
"year_of_birth": now().year - 2,
"disease_status": "NEGATIVE",
"emergency_phone_number": "+919000000666",
"is_vaccinated": "false",
"number_of_doses": 0,
"phone_number": "+919000044343",
"medical_history": {
"disease": "TB",
"details": "",
"status": "not_tb",
},
}
self.client.force_authenticate(user=dist_admin)
response = self.client.post("/api/v1/patient/", sample_data, format="json")
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn("medical_history", response.data)

def test_tb_duration_not_numeric(self):
dist_admin = self.create_user("dist_admin", self.district, user_type=30)
sample_data = {
"facility": self.facility.external_id,
"blood_group": "AB+",
"gender": 1,
"date_of_birth": None,
"year_of_birth": now().year - 2,
"disease_status": "NEGATIVE",
"emergency_phone_number": "+919000000666",
"is_vaccinated": "false",
"number_of_doses": 0,
"phone_number": "+919000044343",
"medical_history": {
"disease": "TB",
"details": "",
"duration": "not_numeric",
},
}
self.client.force_authenticate(user=dist_admin)
response = self.client.post("/api/v1/patient/", sample_data, format="json")
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn("medical_history", response.data)

def test_tb_duration_not_negative(self):
dist_admin = self.create_user("dist_admin", self.district, user_type=30)
sample_data = {
"facility": self.facility.external_id,
"blood_group": "AB+",
"gender": 1,
"date_of_birth": None,
"year_of_birth": now().year - 2,
"disease_status": "NEGATIVE",
"emergency_phone_number": "+919000000666",
"is_vaccinated": "false",
"number_of_doses": 0,
"phone_number": "+919000044343",
"medical_history": {
"disease": "TB",
"details": "",
"duration": -1,
},
}
self.client.force_authenticate(user=dist_admin)
response = self.client.post("/api/v1/patient/", sample_data, format="json")
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn("medical_history", response.data)
18 changes: 16 additions & 2 deletions care/templates/reports/patient_discharge_summary_pdf.html
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -414,10 +414,24 @@ <h4 class="mt-6 font-medium text-gray-500">
{% for disease in medical_history %}
<tr class="bg-white border-b">
<td class="p-1 whitespace-no-wrap text-sm">
{{disease.get_disease_display}}
{% if disease.get_disease_display == "Cancer" and disease.type %}
{{ disease.get_disease_display }} [ {{ disease.type }} ]
{% elif disease.get_disease_display == "TB" %}
{% if disease.status and disease.duration %}
{{ disease.get_disease_display }} [ {{ disease.status }} - {{ disease.duration }} ]
{% elif disease.duration %}
{{ disease.get_disease_display }} [ {{ disease.duration }} Day(s) ]
{% elif disease.status %}
{{ disease.get_disease_display }} [ {{ disease.status }} ]
{% else %}
{{ disease.get_disease_display }}
{% endif %}
{% else %}
{{ disease.get_disease_display }}
{% endif %}
</td>
<td class="p-1 whitespace-normal text-sm">
{{disease.details}}
{{ disease.details }}
</td>
</tr>
{% endfor %}
Expand Down