Skip to content

Commit

Permalink
added contraints in consultations and custom migrations (#1917)
Browse files Browse the repository at this point in the history
* added contraints in consultations and custom migrations

* added noop reverse

* updated test_utils to handle the constraint

* added a test for consultations

* removed print statements

* added one more test

* updated tests for consultations

* updated dummy data

---------

Co-authored-by: Aakash Singh <mail@singhaakash.dev>
  • Loading branch information
DraKen0009 and sainak committed Mar 4, 2024
1 parent 5b317ca commit 284c639
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 16 deletions.
13 changes: 13 additions & 0 deletions care/facility/api/serializers/patient_consultation.py
Expand Up @@ -512,6 +512,19 @@ def validate_encounter_date(self, value):
def validate(self, attrs):
validated = super().validate(attrs)
# TODO Add Bed Authorisation Validation
if "patient_no" in validated:
facility = (
self.instance.facility
if self.instance
else validated.get("patient").facility
)
patient_no = validated["patient_no"]
if PatientConsultation.objects.filter(
patient_no=patient_no, facility=facility
).exists():
raise ValidationError(
"Patient number must be unique within the facility."
)

if (
"suggestion" in validated
Expand Down
38 changes: 38 additions & 0 deletions care/facility/migrations/0415_auto_20240227_0130.py
@@ -0,0 +1,38 @@
# Generated by Django 4.2.10 on 2024-02-26 20:00

from django.db import migrations
from django.db.models import Count, F, Value, Window
from django.db.models.functions import RowNumber


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

window = Window(
expression=RowNumber(),
partition_by=[F("patient_no"), F("facility")],
order_by=F("id").asc(),
)

consultations = PatientConsultation.objects.annotate(row_number=window).filter(
row_number__gt=1
)

for consultation in consultations:
consultation.patient_no = (
f"{consultation.patient_no}_{consultation.row_number - 1}"
)

PatientConsultation.objects.bulk_update(
consultations, ["patient_no"], batch_size=2000
)


class Migration(migrations.Migration):
dependencies = [
("facility", "0414_remove_bed_old_name"),
]

operations = [
migrations.RunPython(fix_duplicate_patient_numbers, migrations.RunPython.noop),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.10 on 2024-02-26 20:08

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("facility", "0415_auto_20240227_0130"),
]

operations = [
migrations.AddConstraint(
model_name="patientconsultation",
constraint=models.UniqueConstraint(
fields=("patient_no", "facility"),
name="unique_patient_no_within_facility",
),
),
]
4 changes: 4 additions & 0 deletions care/facility/models/patient_consultation.py
Expand Up @@ -287,6 +287,10 @@ class Meta:
| models.Q(referred_to__isnull=False)
| models.Q(referred_to_external__isnull=False),
),
models.UniqueConstraint(
fields=["patient_no", "facility"],
name="unique_patient_no_within_facility",
),
]

@staticmethod
Expand Down
85 changes: 85 additions & 0 deletions care/facility/tests/test_patient_consultation_api.py
Expand Up @@ -30,6 +30,7 @@ def setUpTestData(cls) -> None:
cls.doctor = cls.create_user(
"doctor", cls.district, home_facility=cls.facility, user_type=15
)
cls.patient1 = cls.create_patient(cls.district, cls.facility)

def get_default_data(self):
return {
Expand Down Expand Up @@ -529,3 +530,87 @@ def test_add_principal_edit_as_inactive_add_principal(self):
verification_status=ConditionVerificationStatus.PROVISIONAL,
)
self.assertEqual(res.status_code, status.HTTP_201_CREATED)

def test_create_consultations_with_duplicate_patient_no_within_facility(self):
patient2 = self.create_patient(self.district, self.facility)
data = self.get_default_data().copy()
data.update(
{
"patient_no": "IP1234",
"patient": patient2.external_id,
"facility": self.facility.external_id,
"created_by": self.user.external_id,
"suggestion": "A",
}
)
res = self.client.post(self.get_url(), data, format="json")
self.assertEqual(res.status_code, status.HTTP_201_CREATED)
data.update(
{
"patient_no": "IP1234",
"patient": self.patient1.external_id,
"facility": self.facility.external_id,
"created_by": self.user.external_id,
}
)
res = self.client.post(self.get_url(), data, format="json")
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)

def test_create_consultations_with_same_patient_no_in_different_facilities(self):
facility2 = self.create_facility(
self.super_user, self.district, self.local_body, name="bar"
)
patient2 = self.create_patient(self.district, facility2)
doctor2 = self.create_user(
"doctor2", self.district, home_facility=facility2, user_type=15
)
data = self.get_default_data().copy()
data.update(
{
"patient_no": "IP1234",
"patient": self.patient1.external_id,
"created_by": self.user.external_id,
"suggestion": "A",
}
)
res = self.client.post(self.get_url(), data, format="json")
self.assertEqual(res.status_code, status.HTTP_201_CREATED)
data.update(
{
"patient_no": "IP1234",
"patient": patient2.external_id,
"created_by": self.user.external_id,
"suggestion": "A",
"treating_physician": doctor2.id,
}
)
self.client.force_authenticate(user=doctor2)
res = self.client.post(self.get_url(), data, format="json")
self.assertEqual(res.status_code, status.HTTP_201_CREATED)

def test_patient_was_discharged_and_then_added_with_a_different_patient_number(
self,
):
consultation = self.create_admission_consultation(
suggestion="A",
encounter_date=make_aware(datetime.datetime(2020, 4, 1, 15, 30, 00)),
patient=self.patient1,
)
res = self.discharge(
consultation,
new_discharge_reason=NewDischargeReasonEnum.RECOVERED,
discharge_date="2020-04-02T15:30:00Z",
discharge_notes="Discharge as recovered after admission before future",
)
self.assertEqual(res.status_code, status.HTTP_200_OK)
data = self.get_default_data().copy()
data.update(
{
"patient_no": "IP1234",
"patient": self.patient1.external_id,
"created_by": self.user.external_id,
"suggestion": "A",
}
)
res = self.client.post(self.get_url(), data, format="json")
self.assertEqual(res.status_code, status.HTTP_201_CREATED)
1 change: 1 addition & 0 deletions care/utils/tests/test_utils.py
Expand Up @@ -318,6 +318,7 @@ def get_consultation_data(cls):
"course_in_facility": "",
"created_date": mock_equal,
"modified_date": mock_equal,
"patient_no": int(datetime.now().timestamp() * 1000),
}

@classmethod
Expand Down
32 changes: 16 additions & 16 deletions data/dummy/facility.json
Expand Up @@ -2070,7 +2070,7 @@
"modified_date": "2023-12-06T08:39:48.823Z",
"deleted": false,
"patient": 3,
"patient_no": "IP007",
"patient_no": "IP008",
"facility": 1,
"deprecated_diagnosis": "",
"deprecated_icd11_provisional_diagnoses": "[]",
Expand Down Expand Up @@ -2141,7 +2141,7 @@
"modified_date": "2023-12-06T08:42:30.134Z",
"deleted": false,
"patient": 4,
"patient_no": "IP007",
"patient_no": "IP009",
"facility": 1,
"deprecated_diagnosis": "",
"deprecated_icd11_provisional_diagnoses": "[]",
Expand Down Expand Up @@ -2212,7 +2212,7 @@
"modified_date": "2023-12-06T08:42:52.652Z",
"deleted": false,
"patient": 5,
"patient_no": "IP007",
"patient_no": "IP017",
"facility": 1,
"deprecated_diagnosis": "",
"deprecated_icd11_provisional_diagnoses": "[]",
Expand Down Expand Up @@ -2283,7 +2283,7 @@
"modified_date": "2023-12-06T08:43:14.972Z",
"deleted": false,
"patient": 6,
"patient_no": "IP007",
"patient_no": "IP087",
"facility": 1,
"deprecated_diagnosis": "",
"deprecated_icd11_provisional_diagnoses": "[]",
Expand Down Expand Up @@ -2354,7 +2354,7 @@
"modified_date": "2023-12-06T08:43:38.025Z",
"deleted": false,
"patient": 7,
"patient_no": "IP007",
"patient_no": "IP00527",
"facility": 1,
"deprecated_diagnosis": "",
"deprecated_icd11_provisional_diagnoses": "[]",
Expand Down Expand Up @@ -2425,7 +2425,7 @@
"modified_date": "2023-12-06T08:44:01.909Z",
"deleted": false,
"patient": 8,
"patient_no": "IP007",
"patient_no": "IP0KI07",
"facility": 1,
"deprecated_diagnosis": "",
"deprecated_icd11_provisional_diagnoses": "[]",
Expand Down Expand Up @@ -2496,7 +2496,7 @@
"modified_date": "2023-12-06T08:44:25.107Z",
"deleted": false,
"patient": 9,
"patient_no": "IP007",
"patient_no": "IP00767",
"facility": 1,
"deprecated_diagnosis": "",
"deprecated_icd11_provisional_diagnoses": "[]",
Expand Down Expand Up @@ -2567,7 +2567,7 @@
"modified_date": "2023-12-06T08:44:47.683Z",
"deleted": false,
"patient": 10,
"patient_no": "IP007",
"patient_no": "IP001237",
"facility": 1,
"deprecated_diagnosis": "",
"deprecated_icd11_provisional_diagnoses": "[]",
Expand Down Expand Up @@ -2638,7 +2638,7 @@
"modified_date": "2023-12-06T08:45:10.167Z",
"deleted": false,
"patient": 11,
"patient_no": "IP007",
"patient_no": "IP007963",
"facility": 1,
"deprecated_diagnosis": "",
"deprecated_icd11_provisional_diagnoses": "[]",
Expand Down Expand Up @@ -2709,7 +2709,7 @@
"modified_date": "2023-12-06T08:45:34.527Z",
"deleted": false,
"patient": 12,
"patient_no": "IP007",
"patient_no": "IP0001257",
"facility": 1,
"deprecated_diagnosis": "",
"deprecated_icd11_provisional_diagnoses": "[]",
Expand Down Expand Up @@ -2780,7 +2780,7 @@
"modified_date": "2023-12-06T08:45:57.145Z",
"deleted": false,
"patient": 13,
"patient_no": "IP007",
"patient_no": "IP075389007",
"facility": 1,
"deprecated_diagnosis": "",
"deprecated_icd11_provisional_diagnoses": "[]",
Expand Down Expand Up @@ -2851,7 +2851,7 @@
"modified_date": "2023-12-06T08:46:19.952Z",
"deleted": false,
"patient": 14,
"patient_no": "IP007",
"patient_no": "IP099907",
"facility": 1,
"deprecated_diagnosis": "",
"deprecated_icd11_provisional_diagnoses": "[]",
Expand Down Expand Up @@ -2922,7 +2922,7 @@
"modified_date": "2023-12-06T08:46:42.597Z",
"deleted": false,
"patient": 15,
"patient_no": "IP007",
"patient_no": "IP00700",
"facility": 1,
"deprecated_diagnosis": "",
"deprecated_icd11_provisional_diagnoses": "[]",
Expand Down Expand Up @@ -2993,7 +2993,7 @@
"modified_date": "2023-12-06T08:47:07.447Z",
"deleted": false,
"patient": 16,
"patient_no": "IP007",
"patient_no": "IP00744",
"facility": 1,
"deprecated_diagnosis": "",
"deprecated_icd11_provisional_diagnoses": "[]",
Expand Down Expand Up @@ -3064,7 +3064,7 @@
"modified_date": "2023-12-06T08:47:30.863Z",
"deleted": false,
"patient": 17,
"patient_no": "IP007",
"patient_no": "IP00117",
"facility": 1,
"deprecated_diagnosis": "",
"deprecated_icd11_provisional_diagnoses": "[]",
Expand Down Expand Up @@ -3135,7 +3135,7 @@
"modified_date": "2023-12-06T08:47:53.751Z",
"deleted": false,
"patient": 18,
"patient_no": "IP007",
"patient_no": "IP02507",
"facility": 1,
"deprecated_diagnosis": "",
"deprecated_icd11_provisional_diagnoses": "[]",
Expand Down

0 comments on commit 284c639

Please sign in to comment.