diff --git a/care/facility/api/serializers/patient.py b/care/facility/api/serializers/patient.py index e94fa0d25c..f526db1b73 100644 --- a/care/facility/api/serializers/patient.py +++ b/care/facility/api/serializers/patient.py @@ -251,6 +251,16 @@ def validate_countries_travelled(self, value): value = [value] return value + def validate_date_of_birth(self, value): + if value and value > now().date(): + raise serializers.ValidationError("Enter a valid DOB such that age > 0") + return value + + def validate_year_of_birth(self, value): + if value and value > now().year: + raise serializers.ValidationError("Enter a valid year of birth") + return value + def validate(self, attrs): validated = super().validate(attrs) if not self.partial and not ( diff --git a/care/facility/api/viewsets/ambulance.py b/care/facility/api/viewsets/ambulance.py index f67e3457a4..2f435067d5 100644 --- a/care/facility/api/viewsets/ambulance.py +++ b/care/facility/api/viewsets/ambulance.py @@ -65,7 +65,7 @@ def get_serializer_class(self): @extend_schema(tags=["ambulance"]) @action(methods=["POST"], detail=True) - def add_driver(self, request): + def add_driver(self, request, *args, **kwargs): ambulance = self.get_object() serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) @@ -78,7 +78,7 @@ def add_driver(self, request): @extend_schema(tags=["ambulance"]) @action(methods=["DELETE"], detail=True) - def remove_driver(self, request): + def remove_driver(self, request, *args, **kwargs): ambulance = self.get_object() serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) diff --git a/care/facility/api/viewsets/patient.py b/care/facility/api/viewsets/patient.py index 0a18f48909..e7c5d618af 100644 --- a/care/facility/api/viewsets/patient.py +++ b/care/facility/api/viewsets/patient.py @@ -77,6 +77,7 @@ DISEASE_STATUS_DICT, NewDischargeReasonEnum, ) +from care.facility.models.patient_consultation import PatientConsultation from care.users.models import User from care.utils.cache.cache_allowed_facilities import get_accessible_facilities from care.utils.filters.choicefilter import CareChoiceFilter @@ -300,8 +301,11 @@ def filter_queryset(self, request, queryset, view): allowed_facilities = get_accessible_facilities(request.user) q_filters = Q(facility__id__in=allowed_facilities) if view.action == "retrieve": - q_filters |= Q(consultations__facility__id__in=allowed_facilities) - queryset = queryset.distinct("id") + q_filters |= Q( + id__in=PatientConsultation.objects.filter( + facility__id__in=allowed_facilities + ).values("patient_id") + ) q_filters |= Q(last_consultation__assigned_to=request.user) q_filters |= Q(assigned_to=request.user) queryset = queryset.filter(q_filters) @@ -449,14 +453,28 @@ class PatientViewSet( CSV_EXPORT_LIMIT = 7 def get_queryset(self): - # filter_query = self.request.query_params.get("disease_status") - queryset = super().get_queryset() - # if filter_query: - # disease_status = filter_query if filter_query.isdigit() else DiseaseStatusEnum[filter_query].value - # return queryset.filter(disease_status=disease_status) - - # if self.action == "list": - # queryset = queryset.filter(is_active=self.request.GET.get("is_active", True)) + queryset = super().get_queryset().order_by("modified_date") + + if self.action == "list": + queryset = queryset.annotate( + no_consultation_filed=Case( + When( + Q(last_consultation__isnull=True) + | ~Q(last_consultation__facility__id=F("facility__id")) + | ( + Q(last_consultation__discharge_date__isnull=False) + & Q(is_active=True) + ), + then=True, + ), + default=False, + output_field=models.BooleanField(), + ) + ).order_by( + "-no_consultation_filed", + "modified_date", + ) + return queryset def get_serializer_class(self): @@ -592,7 +610,11 @@ class FacilityDischargedPatientViewSet(GenericViewSet, mixins.ListModelMixin): permission_classes = (IsAuthenticated, DRYPermissions) lookup_field = "external_id" serializer_class = PatientListSerializer - filter_backends = (filters.DjangoFilterBackend,) + filter_backends = ( + filters.DjangoFilterBackend, + rest_framework_filters.OrderingFilter, + PatientCustomOrderingFilter, + ) filterset_class = FacilityDischargedPatientFilterSet queryset = PatientRegistration.objects.select_related( "local_body", @@ -611,12 +633,21 @@ class FacilityDischargedPatientViewSet(GenericViewSet, mixins.ListModelMixin): "created_by", ) + ordering_fields = [ + "id", + "name", + "created_date", + "modified_date", + ] + def get_queryset(self) -> QuerySet: qs = super().get_queryset() return qs.filter( - Q(consultations__facility__external_id=self.kwargs["facility_external_id"]) - & Q(consultations__discharge_date__isnull=False) - ).distinct() + id__in=PatientConsultation.objects.filter( + discharge_date__isnull=False, + facility__external_id=self.kwargs["facility_external_id"], + ).values_list("patient_id") + ) class FacilityPatientStatsHistoryFilterSet(filters.FilterSet): diff --git a/care/facility/migrations/0428_alter_patientmetainfo_occupation.py b/care/facility/migrations/0428_alter_patientmetainfo_occupation.py new file mode 100644 index 0000000000..e6a5e069cb --- /dev/null +++ b/care/facility/migrations/0428_alter_patientmetainfo_occupation.py @@ -0,0 +1,54 @@ +# Generated by Django 4.2.10 on 2024-04-17 04:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("facility", "0427_dailyround_is_parsed_by_ocr"), + ] + + operations = [ + migrations.AlterField( + model_name="patientmetainfo", + name="occupation", + field=models.IntegerField( + blank=True, + choices=[ + (1, "STUDENT"), + (2, "BUSINESSMAN"), + (3, "HEALTH_CARE_WORKER"), + (4, "HEALTH_CARE_LAB_WORKER"), + (5, "ANIMAL_HANDLER"), + (6, "OTHERS"), + (7, "HEALTHCARE_PRACTITIONER"), + (8, "PARADEMICS"), + (9, "BUSINESS_RELATED"), + (10, "ENGINEER"), + (11, "TEACHER"), + (12, "OTHER_PROFESSIONAL_OCCUPATIONS"), + (13, "OFFICE_ADMINISTRATIVE"), + (14, "CHEF"), + (15, "PROTECTIVE_SERVICE"), + (16, "HOSPITALITY"), + (17, "CUSTODIAL"), + (18, "CUSTOMER_SERVICE"), + (19, "SALES_SUPERVISOR"), + (20, "RETAIL_SALES_WORKER"), + (21, "INSURANCE_SALES_AGENT"), + (22, "SALES_REPRESENTATIVE"), + (23, "REAL_ESTATE"), + (24, "CONSTRUCTION_EXTRACTION"), + (25, "AGRI_NATURAL"), + (26, "PRODUCTION_OCCUPATION"), + (27, "PILOT_FLIGHT"), + (28, "VEHICLE_DRIVER"), + (29, "MILITARY"), + (30, "HOMEMAKER"), + (31, "UNKNOWN"), + (32, "NOT_APPLICABLE"), + ], + null=True, + ), + ), + ] diff --git a/care/facility/models/patient.py b/care/facility/models/patient.py index d40733515e..bfefeb4b75 100644 --- a/care/facility/models/patient.py +++ b/care/facility/models/patient.py @@ -571,6 +571,32 @@ class OccupationEnum(enum.Enum): HEALTH_CARE_LAB_WORKER = 4 ANIMAL_HANDLER = 5 OTHERS = 6 + HEALTHCARE_PRACTITIONER = 7 + PARADEMICS = 8 + BUSINESS_RELATED = 9 + ENGINEER = 10 + TEACHER = 11 + OTHER_PROFESSIONAL_OCCUPATIONS = 12 + OFFICE_ADMINISTRATIVE = 13 + CHEF = 14 + PROTECTIVE_SERVICE = 15 + HOSPITALITY = 16 + CUSTODIAL = 17 + CUSTOMER_SERVICE = 18 + SALES_SUPERVISOR = 19 + RETAIL_SALES_WORKER = 20 + INSURANCE_SALES_AGENT = 21 + SALES_REPRESENTATIVE = 22 + REAL_ESTATE = 23 + CONSTRUCTION_EXTRACTION = 24 + AGRI_NATURAL = 25 + PRODUCTION_OCCUPATION = 26 + PILOT_FLIGHT = 27 + VEHICLE_DRIVER = 28 + MILITARY = 29 + HOMEMAKER = 30 + UNKNOWN = 31 + NOT_APPLICABLE = 32 OccupationChoices = [(item.value, item.name) for item in OccupationEnum] diff --git a/care/facility/models/tests/test_patient.py b/care/facility/models/tests/test_patient.py index d00b3e8d93..ab403e61a3 100644 --- a/care/facility/models/tests/test_patient.py +++ b/care/facility/models/tests/test_patient.py @@ -1,3 +1,7 @@ +from datetime import timedelta + +from django.utils.timezone import now +from rest_framework import status from rest_framework.test import APITestCase from care.facility.models import DiseaseStatusEnum @@ -23,3 +27,41 @@ def test_disease_state_recovery_is_aliased_to_recovered(self): patient.refresh_from_db() self.assertEqual(patient.disease_status, DiseaseStatusEnum.RECOVERED.value) + + def test_date_of_birth_validation(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": now().date() + timedelta(days=365), + "year_of_birth": None, + "disease_status": "NEGATIVE", + "emergency_phone_number": "+919000000666", + "is_vaccinated": "false", + "number_of_doses": 0, + "phone_number": "+919000044343", + } + 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("date_of_birth", response.data) + + def test_year_of_birth_validation(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 + 1, + "disease_status": "NEGATIVE", + "emergency_phone_number": "+919000000666", + "is_vaccinated": "false", + "number_of_doses": 0, + "phone_number": "+919000044343", + } + 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("year_of_birth", response.data) diff --git a/care/facility/tests/test_ambulance_api.py b/care/facility/tests/test_ambulance_api.py new file mode 100644 index 0000000000..c8c962c3f6 --- /dev/null +++ b/care/facility/tests/test_ambulance_api.py @@ -0,0 +1,218 @@ +""" +Test module for Ambulance API +""" + +from rest_framework.test import APITestCase + +from care.facility.models.ambulance import Ambulance +from care.utils.tests.test_utils import TestUtils + + +class AmbulanceViewSetTest(TestUtils, APITestCase): + """ + Test class for Ambulance + """ + + @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.user = cls.create_user( + "user", district=cls.district, local_body=cls.local_body + ) + cls.facility = cls.create_facility(cls.super_user, cls.district, cls.local_body) + cls.patient = cls.create_patient( + cls.district, cls.facility, local_body=cls.local_body + ) + cls.ambulance = cls.create_ambulance(cls.district, cls.user) + + def get_base_url(self) -> str: + return "/api/v1/ambulance" + + def get_url(self, entry_id=None, action=None): + """ + Constructs the url for ambulance api + """ + base_url = f"{self.get_base_url()}/" + if entry_id is not None: + base_url += f"{entry_id}/" + if action is not None: + base_url += f"{action}/" + return base_url + + def get_detail_representation(self, obj=None) -> dict: + return { + "vehicle_number": obj.vehicle_number, + "ambulance_type": obj.ambulance_type, + "owner_name": obj.owner_name, + "owner_phone_number": obj.owner_phone_number, + "owner_is_smart_phone": obj.owner_is_smart_phone, + "deleted": obj.deleted, + "has_oxygen": obj.has_oxygen, + "has_ventilator": obj.has_ventilator, + "has_suction_machine": obj.has_suction_machine, + "has_defibrillator": obj.has_defibrillator, + "insurance_valid_till_year": obj.insurance_valid_till_year, + "has_free_service": obj.has_free_service, + "primary_district": obj.primary_district.id, + "primary_district_object": { + "id": obj.primary_district.id, + "name": obj.primary_district.name, + "state": obj.primary_district.state.id, + }, + "secondary_district": obj.secondary_district, + "third_district": obj.third_district, + "secondary_district_object": None, + "third_district_object": None, + } + + def get_list_representation(self, obj=None) -> dict: + return { + "drivers": list(obj.drivers), + **self.get_detail_representation(obj), + } + + def get_create_representation(self) -> dict: + """ + Returns a representation of a ambulance create request body + """ + return { + "vehicle_number": "WW73O2195", + "owner_name": "string", + "owner_phone_number": "+918800900466", + "owner_is_smart_phone": True, + "has_oxygen": True, + "has_ventilator": True, + "has_suction_machine": True, + "has_defibrillator": True, + "insurance_valid_till_year": 2020, + "ambulance_type": 1, + "primary_district": self.district.id, + } + + def test_create_ambulance(self): + """ + Test to create ambulance + """ + + # Test with invalid data + res = self.client.post( + self.get_url(action="create"), data=self.get_create_representation() + ) + self.assertEqual(res.status_code, 400) + self.assertEqual(res.json()["drivers"][0], "This field is required.") + + data = { + "drivers": [ + { + "name": "string", + "phone_number": "+919013526849", + "is_smart_phone": True, + } + ], + } + data.update(self.get_create_representation()) + res = self.client.post(self.get_url(action="create"), data=data, format="json") + self.assertEqual(res.status_code, 400) + self.assertEqual( + res.json()["non_field_errors"][0], + "The ambulance must provide a price or be marked as free", + ) + + # Test with valid data + data.update({"price_per_km": 100}) + res = self.client.post(self.get_url(action="create"), data=data, format="json") + self.assertEqual(res.status_code, 201) + self.assertTrue( + Ambulance.objects.filter(vehicle_number=data["vehicle_number"]).exists() + ) + + def test_list_ambulance(self): + """ + Test to list ambulance + """ + res = self.client.get(self.get_url()) + self.assertEqual(res.status_code, 200) + self.assertEqual(res.json()["count"], 1) + self.assertDictContainsSubset( + self.get_list_representation(self.ambulance), res.json()["results"][0] + ) + + def test_retrieve_ambulance(self): + """ + Test to retrieve ambulance + """ + res = self.client.get(f"/api/v1/ambulance/{self.ambulance.id}/") + self.assertEqual(res.status_code, 200) + self.assertDictContainsSubset( + self.get_detail_representation(self.ambulance), res.json() + ) + + def test_update_ambulance(self): + """ + Test to update ambulance + """ + + res = self.client.patch( + self.get_url(entry_id=self.ambulance.id), + data={"vehicle_number": "WW73O2200", "has_free_service": True}, + ) + self.assertEqual(res.status_code, 200) + self.ambulance.refresh_from_db() + self.assertEqual(self.ambulance.vehicle_number, "WW73O2200") + + def test_delete_ambulance(self): + """ + Test to delete ambulance + """ + res = self.client.delete(self.get_url(entry_id=self.ambulance.id)) + self.assertEqual(res.status_code, 204) + self.ambulance.refresh_from_db() + self.assertTrue(self.ambulance.deleted) + + def test_add_driver(self): + """ + Test to add driver + """ + + res = self.client.post( + self.get_url(entry_id=self.ambulance.id, action="add_driver"), + data={ + "name": "string", + "phone_number": "+919013526800", + "is_smart_phone": True, + }, + ) + + self.assertEqual(res.status_code, 201) + self.assertTrue( + self.ambulance.drivers.filter(phone_number="+919013526800").exists() + ) + + def test_remove_driver(self): + """ + Test to remove driver + """ + + res = self.client.post( + self.get_url(entry_id=self.ambulance.id, action="add_driver"), + data={ + "name": "string", + "phone_number": "+919013526800", + "is_smart_phone": True, + }, + ) + + driver_id = res.json()["id"] + + res = self.client.delete( + self.get_url( + entry_id=self.ambulance.id, + action="remove_driver", + ), + data={"driver_id": driver_id}, + ) + self.assertEqual(res.status_code, 204) + self.assertFalse(self.ambulance.drivers.exists()) diff --git a/care/facility/tests/test_patient_and_consultation_access.py b/care/facility/tests/test_patient_and_consultation_access.py index 7235602695..b3decb5d63 100644 --- a/care/facility/tests/test_patient_and_consultation_access.py +++ b/care/facility/tests/test_patient_and_consultation_access.py @@ -51,6 +51,7 @@ def setUpTestData(cls) -> None: user_type=15, ) cls.patient = cls.create_patient(cls.district, cls.remote_facility) + cls.patient1 = cls.create_patient(cls.district, cls.remote_facility) def list_patients(self, **kwargs): return self.client.get("/api/v1/patient/", data=kwargs) @@ -78,6 +79,40 @@ def discharge(self, consultation, **kwargs): format="json", ) + def test_discharge_patient_ordering_filter(self): + consultation1 = self.create_consultation( + self.patient, + self.home_facility, + suggestion="A", + encounter_date=make_aware(datetime.datetime(2024, 1, 3)), + ) + consultation2 = self.create_consultation( + self.patient1, + self.home_facility, + suggestion="A", + encounter_date=make_aware(datetime.datetime(2024, 1, 1)), + ) + self.discharge(consultation1, discharge_date="2024-01-04T00:00:00Z") + self.discharge(consultation2, discharge_date="2024-01-02T00:00:00Z") + + # order by reverse modified date + patients_order = [self.patient1, self.patient] + response = self.client.get( + f"/api/v1/facility/{self.home_facility.external_id}/discharged_patients/?ordering=-modified_date", + ) + response = response.json()["results"] + for i in range(len(response)): + self.assertEqual(str(patients_order[i].external_id), response[i]["id"]) + + # order by modified date + patients_order = patients_order[::-1] + response = self.client.get( + f"/api/v1/facility/{self.home_facility.external_id}/discharged_patients/?ordering=modified_date", + ) + response = response.json()["results"] + for i in range(len(response)): + self.assertEqual(str(patients_order[i].external_id), response[i]["id"]) + def test_patient_consultation_access(self): # In this test, a patient is admitted to a remote facility and then later admitted to a home facility. diff --git a/care/facility/tests/test_places_api.py b/care/facility/tests/test_places_api.py new file mode 100644 index 0000000000..b05c717243 --- /dev/null +++ b/care/facility/tests/test_places_api.py @@ -0,0 +1,194 @@ +from rest_framework import status +from rest_framework.test import APITestCase + +from care.users.models import District, LocalBody, State, Ward +from care.utils.tests.test_utils import TestUtils + + +class DistrictViewSetTestCase(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("staff", cls.district, home_facility=cls.facility) + cls.ward = cls.create_ward(cls.local_body) + + def test_list_district(self): + state2 = self.create_state(name="TEST_STATE_2") + self.create_district(state2, name="TEST_DISTRICT_2") + + response = self.client.get("/api/v1/district/") + self.assertEqual(response.status_code, status.HTTP_200_OK) + + response = self.client.get("/api/v1/district/?district_name=TEST_DISTRICT_2") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["results"][0]["name"], "TEST_DISTRICT_2") + + response = self.client.get(f"/api/v1/district/?state={state2.id}") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["results"][0]["name"], "TEST_DISTRICT_2") + + response = self.client.get(f"/api/v1/district/?state_name={self.state.name}") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["results"][0]["name"], self.district.name) + + def test_retrieve_district(self): + response = self.client.get(f"/api/v1/district/{self.district.id}/") + district_obj = District.objects.get(pk=self.district.id) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data.get("id"), district_obj.id) + self.assertEqual(response.data.get("name"), district_obj.name) + + def test_list_district_all_local_body(self): + response = self.client.get( + f"/api/v1/district/{self.district.id}/get_all_local_body/" + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data[0]["name"], self.local_body.name) + self.assertEqual(response.data[0]["wards"][0]["name"], self.ward.name) + + def test_list_district_local_body(self): + response = self.client.get( + f"/api/v1/district/{self.district.id}/local_bodies/", + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data[0]["name"], self.local_body.name) + + +class LocalBodyViewSetTestCase(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("staff", cls.district, home_facility=cls.facility) + + def test_list_local_body(self): + state2 = self.create_state(name="TEST_STATE_2") + district2 = self.create_district(state2) + self.create_local_body(district2, name="LOCAL_BODY_2") + + response = self.client.get("/api/v1/local_body/") + self.assertEqual(response.status_code, status.HTTP_200_OK) + + response = self.client.get( + f"/api/v1/local_body/?local_body_name={self.local_body.name}" + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["results"][0]["name"], self.local_body.name) + + response = self.client.get(f"/api/v1/local_body/?state={state2.id}") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["results"][0]["name"], "LOCAL_BODY_2") + + response = self.client.get(f"/api/v1/local_body/?state_name={self.state.name}") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["results"][0]["name"], self.local_body.name) + + response = self.client.get(f"/api/v1/local_body/?district={self.district.id}") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["results"][0]["name"], self.local_body.name) + + response = self.client.get(f"/api/v1/local_body/?district2={district2.name}") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["results"][0]["name"], "LOCAL_BODY_2") + + def test_retrieve_local_body(self): + response = self.client.get(f"/api/v1/local_body/{self.local_body.id}/") + local_body_obj = LocalBody.objects.get(pk=self.local_body.id) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data.get("id"), local_body_obj.id) + self.assertEqual(response.data.get("name"), local_body_obj.name) + + +class StateViewSetTestCase(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("staff", cls.district, home_facility=cls.facility) + + def test_list_state(self): + response = self.client.get("/api/v1/state/") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["count"], len(State.objects.all())) + + def test_retrieve_state(self): + response = self.client.get(f"/api/v1/state/{self.state.id}/") + state_obj = State.objects.get(pk=self.state.id) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data.get("id"), state_obj.id) + self.assertEqual(response.data.get("name"), state_obj.name) + + def test_list_state_districts(self): + response = self.client.get(f"/api/v1/state/{self.state.id}/districts/") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data[0]["name"], self.district.name) + + +class WardViewSetTestCase(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("staff", cls.district, home_facility=cls.facility) + cls.ward = cls.create_ward(cls.local_body) + + def test_list_ward(self): + state2 = self.create_state(name="TEST_STATE_2") + district2 = self.create_district(state2) + local_body2 = self.create_local_body(district2) + self.create_ward(local_body2, name="WARD2") + + # Endpoints to filter with state id and state name are throwing error + + response = self.client.get("/api/v1/ward/") + self.assertEqual(response.status_code, status.HTTP_200_OK) + + response = self.client.get("/api/v1/ward/?ward_name=WARD2") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["results"][0]["name"], "WARD2") + + response = self.client.get(f"/api/v1/ward/?district={district2.id}") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["results"][0]["name"], "WARD2") + + response = self.client.get(f"/api/v1/ward/?district_name={self.district.name}") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["results"][0]["name"], self.ward.name) + + response = self.client.get(f"/api/v1/ward/?local_body={self.local_body.id}") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["results"][0]["name"], self.ward.name) + self.assertEqual(response.data["results"][0]["local_body"], self.local_body.id) + + response = self.client.get(f"/api/v1/ward/?local_body_name={local_body2.name}") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["results"][0]["name"], "WARD2") + + response = self.client.get(f"/api/v1/ward/?state_name={state2.name}") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["results"][0]["name"], "WARD2") + + response = self.client.get(f"/api/v1/ward/?state={self.state.id}") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["results"][0]["name"], self.ward.name) + + def test_retrieve_ward(self): + response = self.client.get(f"/api/v1/ward/{self.ward.id}/") + ward_obj = Ward.objects.get(pk=self.ward.id) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data.get("id"), ward_obj.id) + self.assertEqual(response.data.get("name"), ward_obj.name) diff --git a/care/users/api/viewsets/lsg.py b/care/users/api/viewsets/lsg.py index 2b45ccbb35..a899b387cf 100644 --- a/care/users/api/viewsets/lsg.py +++ b/care/users/api/viewsets/lsg.py @@ -127,9 +127,9 @@ class LocalBodyViewSet( class WardFilterSet(filters.FilterSet): - state = filters.NumberFilter(field_name="district__state_id") + state = filters.NumberFilter(field_name="local_body__district__state_id") state_name = filters.CharFilter( - field_name="district__state__name", lookup_expr="icontains" + field_name="local_body__district__state__name", lookup_expr="icontains" ) district = filters.NumberFilter(field_name="local_body__district_id") district_name = filters.CharFilter( diff --git a/care/users/management/commands/load_skill_data.py b/care/users/management/commands/load_skill_data.py index 4d476f793f..3325ba448e 100644 --- a/care/users/management/commands/load_skill_data.py +++ b/care/users/management/commands/load_skill_data.py @@ -17,6 +17,7 @@ def handle(self, *args, **options): "Anesthesiologist", "Cardiac Surgeon", "Cardiologist", + "Dentist", "Dermatologist", "Diabetologist", "Emergency Medicine Physician", @@ -25,8 +26,12 @@ def handle(self, *args, **options): "Gastroenterologist", "General Medicine", "General Surgeon", + "Geriatrician", "Hematologist", + "Immunologist", + "Infectious Disease Specialist", "Intensivist", + "MBBS doctor", "Medical Officer", "Nephrologist", "Neuro Surgeon", @@ -35,12 +40,14 @@ def handle(self, *args, **options): "Oncologist", "Oncology Surgeon", "Ophthalmologist", + "Oral and Maxillofacial Surgeon", "Orthopedic", "Orthopedic Surgeon", "Otolaryngologist (ENT)", "Pediatrician", "Palliative care Physician", "Pathologist", + "Pediatric Surgeon", "Physician", "Plastic Surgeon", "Psychiatrist", @@ -48,7 +55,9 @@ def handle(self, *args, **options): "Radio technician", "Radiologist", "Rheumatologist", + "Sports Medicine Specialist", "Thoraco-Vascular Surgeon", + "Transfusion Medicine Specialist", "Urologist", ] diff --git a/care/users/migrations/0016_upgrade_user_skills.py b/care/users/migrations/0016_upgrade_user_skills.py new file mode 100644 index 0000000000..1354c7e5fc --- /dev/null +++ b/care/users/migrations/0016_upgrade_user_skills.py @@ -0,0 +1,31 @@ +# Generated by Django 4.2.10 on 2024-04-18 05:42 + +from django.db import migrations + + +def add_skills(apps, schema_editor): + Skill = apps.get_model("users", "Skill") + if Skill.objects.exists(): + skills = [ + "Dentist", + "Geriatrician", + "Immunologist", + "Infectious Disease Specialist", + "MBBS doctor", + "Oral and Maxillofacial Surgeon", + "Pediatric Surgeon", + "Sports Medicine Specialist", + "Transfusion Medicine Specialist", + ] + for skill in skills: + Skill.objects.get_or_create(name=skill) + + +class Migration(migrations.Migration): + dependencies = [ + ("users", "0015_age_to_dateofbirth"), + ] + + operations = [ + migrations.RunPython(add_skills), + ] diff --git a/care/utils/tests/test_utils.py b/care/utils/tests/test_utils.py index a6c2307312..acb286f043 100644 --- a/care/utils/tests/test_utils.py +++ b/care/utils/tests/test_utils.py @@ -13,6 +13,7 @@ CATEGORY_CHOICES, DISEASE_CHOICES_MAP, SYMPTOM_CHOICES, + Ambulance, Disease, DiseaseStatusEnum, Facility, @@ -95,12 +96,16 @@ def get_base_url(self) -> str: raise NotImplementedError() @classmethod - def create_state(cls) -> State: - return State.objects.create(name=f"State{now().timestamp()}") + def create_state(cls, **kwargs) -> State: + data = {"name": f"State{now().timestamp()}"} + data.update(kwargs) + return State.objects.create(**data) @classmethod - def create_district(cls, state: State) -> District: - return District.objects.create(state=state, name=f"District{now().timestamp()}") + def create_district(cls, state: State, **kwargs) -> District: + data = {"state": state, "name": f"District{now().timestamp()}"} + data.update(**kwargs) + return District.objects.create(**data) @classmethod def create_local_body(cls, district: District, **kwargs) -> LocalBody: @@ -174,11 +179,14 @@ def create_user( return user @classmethod - def create_ward(cls, local_body) -> Ward: - ward = Ward.objects.create( - name=f"Ward{now().timestamp()}", local_body=local_body, number=1 - ) - return ward + def create_ward(cls, local_body, **kwargs) -> Ward: + data = { + "name": f"Ward{now().timestamp()}", + "local_body": local_body, + "number": 1, + } + data.update(kwargs) + return Ward.objects.create(**data) @classmethod def create_super_user(cls, *args, **kwargs) -> User: @@ -457,6 +465,29 @@ def clone_object(cls, obj, save=True): new_obj.save() return new_obj + @classmethod + def get_ambulance_data(cls, district, user) -> dict: + return { + "vehicle_number": "KL01AB1234", + "owner_name": "Foo", + "owner_phone_number": "9998887776", + "primary_district": district, + "has_oxygen": True, + "has_ventilator": True, + "has_suction_machine": True, + "has_defibrillator": True, + "insurance_valid_till_year": 2021, + "price_per_km": 10, + "has_free_service": False, + "created_by": user, + } + + @classmethod + def create_ambulance(cls, district: District, user: User, **kwargs) -> Ambulance: + data = cls.get_ambulance_data(district, user) + data.update(**kwargs) + return Ambulance.objects.create(**data) + def get_list_representation(self, obj) -> dict: """ Returns the dict representation of the obj in list API