Skip to content

Commit

Permalink
migrating feature field to array field (#2223)
Browse files Browse the repository at this point in the history
* migrating feature to array field

* updated serializer for feature field

* Added validations and tests

* Added tests

* added integer choice enum for features

* fixed migrations

* deleted merge migration and updated the numbers

* fixed dummy data in facility.json

* fixed dummy data in facility.json

* updated migration number

---------

Co-authored-by: Vignesh Hari <[email protected]>
  • Loading branch information
DraKen0009 and vigneshhari authored Aug 21, 2024
1 parent 5cada26 commit 1038f99
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 26 deletions.
17 changes: 15 additions & 2 deletions care/facility/api/serializers/facility.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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),
]
27 changes: 25 additions & 2 deletions care/facility/models/facility.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -38,6 +39,7 @@
(70, "KASP Ventilator beds"),
]

# to be removed in further PR
FEATURE_CHOICES = [
(1, "CT Scan Facility"),
(2, "Maternity Care"),
Expand All @@ -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"),
Expand Down Expand Up @@ -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
)
Expand Down Expand Up @@ -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",
Expand Down
60 changes: 58 additions & 2 deletions care/facility/tests/test_facility_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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"]
Expand Down
Loading

0 comments on commit 1038f99

Please sign in to comment.