diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f538841f5b..c8b61c98e7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ default_stages: [commit] repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.6.0 hooks: - id: no-commit-to-branch args: [--branch, develop, --branch, staging, --branch, production] @@ -23,13 +23,13 @@ repos: additional_dependencies: ["isort[pyproject]"] - repo: https://github.com/psf/black - rev: 24.4.0 + rev: 24.4.2 hooks: - id: black args: ["--config=pyproject.toml"] - repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 + rev: 7.1.0 hooks: - id: flake8 args: ["--config=.flake8"] diff --git a/LICENSE b/LICENSE index 146e799954..eddd0436bc 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Open Healthcare Network +Copyright (c) 2024 Open Healthcare Network Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/care/facility/api/serializers/asset.py b/care/facility/api/serializers/asset.py index 88ad84fa52..57526a8ba6 100644 --- a/care/facility/api/serializers/asset.py +++ b/care/facility/api/serializers/asset.py @@ -320,6 +320,32 @@ def update(self, instance: Asset, validated_data): return updated_instance +class AssetPublicSerializer(ModelSerializer): + id = UUIDField(source="external_id", read_only=True) + status = ChoiceField(choices=StatusChoices, read_only=True) + asset_type = ChoiceField(choices=AssetTypeChoices) + location_object = AssetLocationSerializer(source="current_location", read_only=True) + + class Meta: + model = Asset + fields = ( + "id", + "name", + "location_object", + "serial_number", + "warranty_details", + "warranty_amc_end_of_validity", + "asset_type", + "asset_class", + "vendor_name", + "support_name", + "support_email", + "support_phone", + "is_working", + "status", + ) + + class AssetConfigSerializer(ModelSerializer): id = UUIDField(source="external_id") type = CharField(source="asset_class") diff --git a/care/facility/api/serializers/facility.py b/care/facility/api/serializers/facility.py index c7380edb9d..dba13cd5b1 100644 --- a/care/facility/api/serializers/facility.py +++ b/care/facility/api/serializers/facility.py @@ -48,7 +48,10 @@ class FacilityBasicInfoSerializer(serializers.ModelSerializer): state_object = StateSerializer(source="state", read_only=True) facility_type = serializers.SerializerMethodField() read_cover_image_url = serializers.CharField(read_only=True) - features = serializers.MultipleChoiceField(choices=FEATURE_CHOICES) + features = serializers.ListField( + child=serializers.ChoiceField(choices=FEATURE_CHOICES), + required=False, + ) patient_count = serializers.SerializerMethodField() bed_count = serializers.SerializerMethodField() @@ -96,7 +99,10 @@ class FacilitySerializer(FacilityBasicInfoSerializer): # } read_cover_image_url = serializers.URLField(read_only=True) # location = PointField(required=False) - features = serializers.MultipleChoiceField(choices=FEATURE_CHOICES) + features = serializers.ListField( + child=serializers.ChoiceField(choices=FEATURE_CHOICES), + required=False, + ) bed_count = serializers.SerializerMethodField() class Meta: @@ -148,6 +154,13 @@ def validate_middleware_address(self, value): MiddlewareDomainAddressValidator()(value) return value + def validate_features(self, value): + if len(value) != len(set(value)): + raise serializers.ValidationError( + "Features should not contain duplicate values." + ) + return value + def create(self, validated_data): validated_data["created_by"] = self.context["request"].user return super().create(validated_data) diff --git a/care/facility/api/viewsets/asset.py b/care/facility/api/viewsets/asset.py index 45b976f2f9..42323de545 100644 --- a/care/facility/api/viewsets/asset.py +++ b/care/facility/api/viewsets/asset.py @@ -35,6 +35,7 @@ from care.facility.api.serializers.asset import ( AssetConfigSerializer, AssetLocationSerializer, + AssetPublicSerializer, AssetSerializer, AssetServiceSerializer, AssetTransactionSerializer, @@ -187,7 +188,7 @@ def filter_is_permanent(self, queryset, _, value): class AssetPublicViewSet(GenericViewSet): queryset = Asset.objects.all() - serializer_class = AssetSerializer + serializer_class = AssetPublicSerializer lookup_field = "external_id" def retrieve(self, request, *args, **kwargs): @@ -205,7 +206,7 @@ def retrieve(self, request, *args, **kwargs): class AssetPublicQRViewSet(GenericViewSet): queryset = Asset.objects.all() - serializer_class = AssetSerializer + serializer_class = AssetPublicSerializer lookup_field = "qr_code_id" def retrieve(self, request, *args, **kwargs): diff --git a/care/facility/api/viewsets/icd.py b/care/facility/api/viewsets/icd.py index da28af1bdf..d836de3b5f 100644 --- a/care/facility/api/viewsets/icd.py +++ b/care/facility/api/viewsets/icd.py @@ -28,7 +28,6 @@ def list(self, request): query = [ ICD11.has_code == 1, - ICD11.chapter != "null", # noqa: E711 ] if q := request.query_params.get("query"): query.append(ICD11.vec % query_builder(q)) diff --git a/care/facility/management/commands/load_event_types.py b/care/facility/management/commands/load_event_types.py index 4aecbfc6fc..fc9c231372 100644 --- a/care/facility/management/commands/load_event_types.py +++ b/care/facility/management/commands/load_event_types.py @@ -153,7 +153,7 @@ class Command(BaseCommand): "fields": ( "bilateral_air_entry", "etco2", - "ventilator_fi02", + "ventilator_fio2", "ventilator_interface", "ventilator_mean_airway_pressure", "ventilator_mode", diff --git a/care/facility/management/commands/load_icd11_diagnoses_data.py b/care/facility/management/commands/load_icd11_diagnoses_data.py index faa8ba6df9..ef47d81105 100644 --- a/care/facility/management/commands/load_icd11_diagnoses_data.py +++ b/care/facility/management/commands/load_icd11_diagnoses_data.py @@ -87,9 +87,9 @@ class Command(BaseCommand): "21 Symptoms, signs or clinical findings, not elsewhere classified": "Others", "22 Injury, poisoning or certain other consequences of external causes": "Injury, Poisoning", "23 External causes of morbidity or mortality": "External Causes of Injury", - "24 Factors influencing health status or contact with health services": None, + "24 Factors influencing health status or contact with health services": "Reason for contact with Health Services", "25 Codes for special purposes": "Codes for special purposes", - "26 Supplementary Chapter Traditional Medicine Conditions - Module I": None, + "26 Supplementary Chapter Traditional Medicine Conditions - Module I": "Supplementary chapter", "V Supplementary section for functioning assessment": "Functioning assessment", } diff --git a/care/facility/migrations/0448_rename_features_facility_old_features.py b/care/facility/migrations/0448_rename_features_facility_old_features.py new file mode 100644 index 0000000000..77f3053b83 --- /dev/null +++ b/care/facility/migrations/0448_rename_features_facility_old_features.py @@ -0,0 +1,56 @@ +# Generated by Django 4.2.10 on 2024-06-03 06:24 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +def convert_features_to_array(apps, schema_editor): + Facility = apps.get_model("facility", "Facility") + + facilities_to_update = Facility.objects.filter(old_features__isnull=False) + + updated_facilities = [] + + for facility in facilities_to_update: + try: + facility.features = list(facility.old_features) + updated_facilities.append(facility) + except ValueError: + print(f"facility '{facility.name}' has invalid facility features") + + if updated_facilities: + Facility.objects.bulk_update(updated_facilities, ["features"]) + + +class Migration(migrations.Migration): + dependencies = [ + ("facility", "0447_patientconsultationevent_taken_at"), + ] + + operations = [ + migrations.RenameField( + model_name="facility", + old_name="features", + new_name="old_features", + ), + migrations.AddField( + model_name="facility", + name="features", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.SmallIntegerField( + choices=[ + (1, "CT Scan Facility"), + (2, "Maternity Care"), + (3, "X-Ray Facility"), + (4, "Neonatal Care"), + (5, "Operation Theater"), + (6, "Blood Bank"), + ] + ), + blank=True, + null=True, + size=None, + ), + ), + migrations.RunPython(convert_features_to_array), + ] diff --git a/care/facility/migrations/0448_rename_ventilator_fi02_dailyround_ventilator_fio2.py b/care/facility/migrations/0448_rename_ventilator_fi02_dailyround_ventilator_fio2.py new file mode 100644 index 0000000000..f63c9898f9 --- /dev/null +++ b/care/facility/migrations/0448_rename_ventilator_fi02_dailyround_ventilator_fio2.py @@ -0,0 +1,80 @@ +# Generated by Django 4.2.10 on 2024-08-13 10:36 + +from django.core.paginator import Paginator +from django.db import migrations + + +class Migration(migrations.Migration): + def forward_rename_dailyround_events(apps, schema_editor): + EventType = apps.get_model("facility", "EventType") + PatientConsultationEvent = apps.get_model( + "facility", "PatientConsultationEvent" + ) + + event_type = EventType.objects.filter(name="RESPIRATORY_SUPPORT") + if not event_type.exists(): + return + event_type_id = event_type.first().id + + paginator = Paginator( + PatientConsultationEvent.objects.filter( + object_model="DailyRound", + event_type_id=event_type_id, + value__has_key="ventilator_fi02", + ).order_by("id"), + 1000, + ) + + for page_number in paginator.page_range: + bulk = [] + for instance in paginator.page(page_number).object_list: + instance.value["ventilator_fio2"] = instance.value.pop( + "ventilator_fi02" + ) + bulk.append(instance) + PatientConsultationEvent.objects.bulk_update(bulk, ["value"]) + + def reverse_rename_dailyround_events(apps, schema_editor): + EventType = apps.get_model("facility", "EventType") + PatientConsultationEvent = apps.get_model( + "facility", "PatientConsultationEvent" + ) + + event_type = EventType.objects.filter(name="RESPIRATORY_SUPPORT") + if not event_type.exists(): + return + event_type_id = event_type.first().id + + paginator = Paginator( + PatientConsultationEvent.objects.filter( + object_model="DailyRound", + event_type_id=event_type_id, + value__has_key="ventilator_fio2", + ).order_by("id"), + 1000, + ) + + for page_number in paginator.page_range: + bulk = [] + for instance in paginator.page(page_number).object_list: + instance.value["ventilator_fi02"] = instance.value.pop( + "ventilator_fio2" + ) + bulk.append(instance) + PatientConsultationEvent.objects.bulk_update(bulk, ["value"]) + + dependencies = [ + ("facility", "0447_patientconsultationevent_taken_at"), + ] + + operations = [ + migrations.RenameField( + model_name="dailyround", + old_name="ventilator_fi02", + new_name="ventilator_fio2", + ), + migrations.RunPython( + forward_rename_dailyround_events, + reverse_code=reverse_rename_dailyround_events, + ), + ] diff --git a/care/facility/migrations/0449_merge_20240822_1343.py b/care/facility/migrations/0449_merge_20240822_1343.py new file mode 100644 index 0000000000..6486cb7483 --- /dev/null +++ b/care/facility/migrations/0449_merge_20240822_1343.py @@ -0,0 +1,12 @@ +# Generated by Django 4.2.10 on 2024-08-22 08:13 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("facility", "0448_rename_features_facility_old_features"), + ("facility", "0448_rename_ventilator_fi02_dailyround_ventilator_fio2"), + ] + + operations = [] diff --git a/care/facility/models/daily_round.py b/care/facility/models/daily_round.py index 30c18b91c4..9f12dc25a3 100644 --- a/care/facility/models/daily_round.py +++ b/care/facility/models/daily_round.py @@ -320,7 +320,7 @@ class InsulinIntakeFrequencyType(enum.Enum): null=True, validators=[MinValueValidator(0), MaxValueValidator(70)], ) - ventilator_fi02 = models.IntegerField( + ventilator_fio2 = models.IntegerField( default=None, null=True, validators=[MinValueValidator(21), MaxValueValidator(100)], diff --git a/care/facility/models/facility.py b/care/facility/models/facility.py index f5a0013540..6d2f25ac3e 100644 --- a/care/facility/models/facility.py +++ b/care/facility/models/facility.py @@ -1,5 +1,6 @@ from django.conf import settings from django.contrib.auth import get_user_model +from django.contrib.postgres.fields import ArrayField from django.core.validators import MinValueValidator from django.db import models from multiselectfield import MultiSelectField @@ -38,6 +39,7 @@ (70, "KASP Ventilator beds"), ] +# to be removed in further PR FEATURE_CHOICES = [ (1, "CT Scan Facility"), (2, "Maternity Care"), @@ -47,9 +49,20 @@ (6, "Blood Bank"), ] + +class FacilityFeature(models.IntegerChoices): + CT_SCAN_FACILITY = 1, "CT Scan Facility" + MATERNITY_CARE = 2, "Maternity Care" + X_RAY_FACILITY = 3, "X-Ray Facility" + NEONATAL_CARE = 4, "Neonatal Care" + OPERATION_THEATER = 5, "Operation Theater" + BLOOD_BANK = 6, "Blood Bank" + + ROOM_TYPES.extend(BASE_ROOM_TYPES) REVERSE_ROOM_TYPES = reverse_choices(ROOM_TYPES) +REVERSE_FEATURE_CHOICES = reverse_choices(FEATURE_CHOICES) FACILITY_TYPES = [ (1, "Educational Inst"), @@ -160,13 +173,17 @@ class Facility(FacilityBaseModel, FacilityPermissionMixin): verified = models.BooleanField(default=False) facility_type = models.IntegerField(choices=FACILITY_TYPES) kasp_empanelled = models.BooleanField(default=False, blank=False, null=False) - features = MultiSelectField( + features = ArrayField( + models.SmallIntegerField(choices=FacilityFeature.choices), + blank=True, + null=True, + ) + old_features = MultiSelectField( choices=FEATURE_CHOICES, null=True, blank=True, max_length=get_max_length(FEATURE_CHOICES, None), ) - longitude = models.DecimalField( max_digits=22, decimal_places=16, null=True, blank=True ) @@ -243,6 +260,12 @@ def save(self, *args, **kwargs) -> None: facility=self, user=self.created_by, created_by=self.created_by ) + @property + def get_features_display(self): + if not self.features: + return [] + return [FacilityFeature(f).label for f in self.features] + CSV_MAPPING = { "name": "Facility Name", "facility_type": "Facility Type", diff --git a/care/facility/tests/test_facility_api.py b/care/facility/tests/test_facility_api.py index 60cdb08c36..0f6fef256d 100644 --- a/care/facility/tests/test_facility_api.py +++ b/care/facility/tests/test_facility_api.py @@ -23,7 +23,9 @@ def test_listing(self): def test_create(self): dist_admin = self.create_user("dist_admin", self.district, user_type=30) - sample_data = { + self.client.force_authenticate(user=dist_admin) + + sample_data_with_empty_feature_list = { "name": "Hospital X", "district": self.district.pk, "state": self.state.pk, @@ -33,7 +35,61 @@ def test_create(self): "pincode": 390024, "features": [], } - self.client.force_authenticate(user=dist_admin) + response = self.client.post( + "/api/v1/facility/", sample_data_with_empty_feature_list + ) + self.assertIs(response.status_code, status.HTTP_201_CREATED) + + sample_data_with_invalid_choice = { + "name": "Hospital X", + "district": self.district.pk, + "state": self.state.pk, + "local_body": self.local_body.pk, + "facility_type": "Educational Inst", + "address": "Nearby", + "pincode": 390024, + "features": [1020, 2, 4, 5], + } + response = self.client.post( + "/api/v1/facility/", sample_data_with_invalid_choice + ) + + self.assertIs(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.data["features"][0][0].code, "invalid_choice") + self.assertEqual( + response.data["features"][0][0], '"1020" is not a valid choice.' + ) + + sample_data_with_duplicate_choices = { + "name": "Hospital X", + "district": self.district.pk, + "state": self.state.pk, + "local_body": self.local_body.pk, + "facility_type": "Educational Inst", + "address": "Nearby", + "pincode": 390024, + "features": [1, 1], + } + response = self.client.post( + "/api/v1/facility/", sample_data_with_duplicate_choices + ) + + self.assertIs(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual( + response.data["features"][0], + "Features should not contain duplicate values.", + ) + + sample_data = { + "name": "Hospital X", + "district": self.district.pk, + "state": self.state.pk, + "local_body": self.local_body.pk, + "facility_type": "Educational Inst", + "address": "Nearby", + "pincode": 390024, + "features": [1, 2], + } response = self.client.post("/api/v1/facility/", sample_data) self.assertIs(response.status_code, status.HTTP_201_CREATED) fac_id = response.data["id"] diff --git a/care/hcx/utils/fhir.py b/care/hcx/utils/fhir.py index 6b13693fce..46ee5326c6 100644 --- a/care/hcx/utils/fhir.py +++ b/care/hcx/utils/fhir.py @@ -1017,8 +1017,8 @@ def process_coverage_elibility_check_response(self, response): coverageeligibilityresponse.CoverageEligibilityResponse( **list( filter( - lambda entry: type(entry.resource) - == coverageeligibilityresponse.CoverageEligibilityResponse, + lambda entry: entry.resource + is coverageeligibilityresponse.CoverageEligibilityResponse, coverage_eligibility_check_bundle.entry, ) )[0].resource.dict() @@ -1027,7 +1027,7 @@ def process_coverage_elibility_check_response(self, response): coverage_request = coverage.Coverage( **list( filter( - lambda entry: type(entry.resource) == coverage.Coverage, + lambda entry: entry.resource is coverage.Coverage, coverage_eligibility_check_bundle.entry, ) )[0].resource.dict() @@ -1057,7 +1057,7 @@ def process_claim_response(self, response): claim_response = claimresponse.ClaimResponse( **list( filter( - lambda entry: type(entry.resource) == claimresponse.ClaimResponse, + lambda entry: entry.resource is claimresponse.ClaimResponse, claim_bundle.entry, ) )[0].resource.dict() diff --git a/care/users/api/viewsets/users.py b/care/users/api/viewsets/users.py index d581b170c0..f67ce6ac42 100644 --- a/care/users/api/viewsets/users.py +++ b/care/users/api/viewsets/users.py @@ -57,9 +57,13 @@ class UserFilterSet(filters.FilterSet): ) last_login = filters.DateFromToRangeFilter(field_name="last_login") district_id = filters.NumberFilter(field_name="district_id", lookup_expr="exact") - home_facility = filters.UUIDFilter( - field_name="home_facility__external_id", lookup_expr="exact" - ) + home_facility = filters.CharFilter(method="filter_home_facility") + + def filter_home_facility(self, queryset, name, value): + if value == "NONE": + return queryset.filter(home_facility__isnull=True) + return queryset.filter(home_facility__external_id=value) + last_active_days = filters.CharFilter(method="last_active_after") def get_user_type( diff --git a/care/users/tests/test_api.py b/care/users/tests/test_api.py index a91a891a74..ef58f25e7c 100644 --- a/care/users/tests/test_api.py +++ b/care/users/tests/test_api.py @@ -211,6 +211,9 @@ def setUpTestData(cls) -> None: 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.facility_2 = cls.create_facility( + cls.super_user, cls.district, cls.local_body + ) cls.user_1 = cls.create_user("staff1", cls.district, home_facility=cls.facility) @@ -218,6 +221,12 @@ def setUpTestData(cls) -> None: cls.user_3 = cls.create_user("staff3", cls.district, home_facility=cls.facility) + cls.user_4 = cls.create_user( + "staff4", cls.district, home_facility=cls.facility_2 + ) + + cls.user_5 = cls.create_user("doctor", cls.district) + def setUp(self): self.client.force_authenticate(self.super_user) self.user_1.last_login = timezone.now() - timedelta(hours=1) @@ -248,7 +257,37 @@ def test_last_active_filter(self): response = self.client.get("/api/v1/users/?last_active_days=never") self.assertEqual(response.status_code, status.HTTP_200_OK) res_data_json = response.json() - self.assertEqual(res_data_json["count"], 1) + self.assertEqual(res_data_json["count"], 3) self.assertIn( self.user_3.username, {r["username"] for r in res_data_json["results"]} ) + + def test_home_facility_filter(self): + """Test home facility filter""" + response = self.client.get("/api/v1/users/?home_facility=NOT_A_VALID_UUID") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + response = self.client.get( + f"/api/v1/users/?home_facility={self.facility.external_id}" + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + res_data_json = response.json() + self.assertEqual(res_data_json["count"], 3) + self.assertIn( + self.user_1.username, {r["username"] for r in res_data_json["results"]} + ) + + response = self.client.get( + f"/api/v1/users/?home_facility={self.facility_2.external_id}" + ) + res_data_json = response.json() + self.assertEqual(res_data_json["count"], 1) + self.assertIn( + self.user_4.username, {r["username"] for r in res_data_json["results"]} + ) + + response = self.client.get("/api/v1/users/?home_facility=NONE") + res_data_json = response.json() + self.assertEqual(res_data_json["count"], 1) + self.assertIn( + self.user_5.username, {r["username"] for r in res_data_json["results"]} + ) diff --git a/data/dummy/facility.json b/data/dummy/facility.json index 7b7485df8c..45f4a12b71 100644 --- a/data/dummy/facility.json +++ b/data/dummy/facility.json @@ -12,7 +12,7 @@ "verified": false, "facility_type": 2, "kasp_empanelled": false, - "features": "1,2,3,4,5,6", + "features": [1,2,3,4,5,6], "longitude": null, "latitude": null, "pincode": 670000, @@ -49,7 +49,7 @@ "verified": false, "facility_type": 1300, "kasp_empanelled": false, - "features": "1,6", + "features": [1,6], "longitude": null, "latitude": null, "pincode": 670112, @@ -86,7 +86,7 @@ "verified": false, "facility_type": 1500, "kasp_empanelled": false, - "features": "1,4,6", + "features": [1,4,6], "longitude": "78.6757364624373000", "latitude": "21.4009146842158660", "pincode": 670000, @@ -123,7 +123,7 @@ "verified": false, "facility_type": 1510, "kasp_empanelled": false, - "features": "1,3,5", + "features": [1,3,5], "longitude": "75.2139014820876600", "latitude": "18.2774285038890340", "pincode": 670000, @@ -160,7 +160,7 @@ "verified": false, "facility_type": 2, "kasp_empanelled": false, - "features": "", + "features": [], "longitude": null, "latitude": null, "pincode": 682001, @@ -197,7 +197,7 @@ "verified": false, "facility_type": 2, "kasp_empanelled": false, - "features": "", + "features": [], "longitude": null, "latitude": null, "pincode": 682001, @@ -234,7 +234,7 @@ "verified": false, "facility_type": 2, "kasp_empanelled": false, - "features": "", + "features": [], "longitude": null, "latitude": null, "pincode": 682001, @@ -271,7 +271,7 @@ "verified": false, "facility_type": 2, "kasp_empanelled": false, - "features": "", + "features": [], "longitude": null, "latitude": null, "pincode": 682001, @@ -308,7 +308,7 @@ "verified": false, "facility_type": 2, "kasp_empanelled": false, - "features": "", + "features": [], "longitude": null, "latitude": null, "pincode": 682001, @@ -345,7 +345,7 @@ "verified": false, "facility_type": 2, "kasp_empanelled": false, - "features": "", + "features": [], "longitude": null, "latitude": null, "pincode": 682001, @@ -382,7 +382,7 @@ "verified": false, "facility_type": 2, "kasp_empanelled": false, - "features": "", + "features": [], "longitude": null, "latitude": null, "pincode": 682001, @@ -419,7 +419,7 @@ "verified": false, "facility_type": 2, "kasp_empanelled": false, - "features": "", + "features": [], "longitude": null, "latitude": null, "pincode": 682001, @@ -456,7 +456,7 @@ "verified": false, "facility_type": 2, "kasp_empanelled": false, - "features": "", + "features": [], "longitude": null, "latitude": null, "pincode": 682001, @@ -493,7 +493,7 @@ "verified": false, "facility_type": 2, "kasp_empanelled": false, - "features": "", + "features": [], "longitude": null, "latitude": null, "pincode": 682001, @@ -530,7 +530,7 @@ "verified": false, "facility_type": 2, "kasp_empanelled": false, - "features": "", + "features": [], "longitude": null, "latitude": null, "pincode": 682001, @@ -567,7 +567,7 @@ "verified": false, "facility_type": 2, "kasp_empanelled": false, - "features": "", + "features": [], "longitude": null, "latitude": null, "pincode": 682001, @@ -604,7 +604,7 @@ "verified": false, "facility_type": 2, "kasp_empanelled": false, - "features": "", + "features": [], "longitude": null, "latitude": null, "pincode": 682001, @@ -641,7 +641,7 @@ "verified": false, "facility_type": 2, "kasp_empanelled": false, - "features": "", + "features": [], "longitude": null, "latitude": null, "pincode": 682001, @@ -678,7 +678,7 @@ "verified": false, "facility_type": 2, "kasp_empanelled": false, - "features": "", + "features": [], "longitude": null, "latitude": null, "pincode": 682001, @@ -715,7 +715,7 @@ "verified": false, "facility_type": 2, "kasp_empanelled": false, - "features": "", + "features": [], "longitude": null, "latitude": null, "pincode": 682001,