From 35b1d48b2aa535ed1745487ea05f5161498b9ee7 Mon Sep 17 00:00:00 2001 From: Suyash Singh Date: Sat, 15 Jul 2023 01:43:07 +0530 Subject: [PATCH 1/6] List and Detail serializer to daily rounds --- care/facility/api/serializers/daily_round.py | 342 +++++++++++++++++++ care/facility/api/viewsets/daily_round.py | 11 +- 2 files changed, 352 insertions(+), 1 deletion(-) diff --git a/care/facility/api/serializers/daily_round.py b/care/facility/api/serializers/daily_round.py index 340962d86d..28f1d7dff4 100644 --- a/care/facility/api/serializers/daily_round.py +++ b/care/facility/api/serializers/daily_round.py @@ -13,6 +13,7 @@ CATEGORY_CHOICES, COVID_CATEGORY_CHOICES, PatientRegistration, + User, ) from care.facility.models.bed import Bed from care.facility.models.daily_round import DailyRound @@ -328,3 +329,344 @@ def validate(self, obj): validated["bed_id"] = bed_object.id return validated + + +class UserDailyRoundsMinimumSerializer(serializers.ModelSerializer): + id = serializers.CharField(source="external_id", read_only=True) + user_type = ChoiceField(choices=User.TYPE_CHOICES, read_only=True) + + class Meta: + model = User + fields = ("id", "first_name", "last_name", "user_type") + + +class DailyRoundsListSerializer(serializers.ModelSerializer): + id = serializers.CharField(source="external_id", read_only=True) + patient_category = ChoiceField(choices=CATEGORY_CHOICES, required=False) + last_edited_by = UserDailyRoundsMinimumSerializer(read_only=True) + created_by = UserDailyRoundsMinimumSerializer(read_only=True) + + class Meta: + model = DailyRound + fields = ( + "id", + "temperature", + "temperature_measured_at", + "bp", + "resp", + "spo2", + "ventilator_spo2", + "pulse", + "created_date", + "rounds_type", + "patient_category", + "physical_examination_info", + "other_details", + "last_edited_by", + "created_by", + "created_by_telemedicine", + "last_updated_by_telemedicine", + ) + + +class DailyRoundsDetailSerializer(DailyRoundsListSerializer): + additional_symptoms = serializers.MultipleChoiceField( + choices=SYMPTOM_CHOICES, required=False + ) + deprecated_covid_category = ChoiceField( + choices=COVID_CATEGORY_CHOICES, required=False + ) # Deprecated + current_health = ChoiceField(choices=CURRENT_HEALTH_CHOICES, required=False) + + action = ChoiceField( + choices=PatientRegistration.ActionChoices, write_only=True, required=False + ) + review_interval = serializers.IntegerField( + source="consultation__review_interval", required=False + ) + + taken_at = serializers.DateTimeField(required=True) + + rounds_type = ChoiceField(choices=DailyRound.RoundsTypeChoice, required=True) + + # Critical Care Components + + consciousness_level = ChoiceField( + choices=DailyRound.ConsciousnessChoice, required=False + ) + left_pupil_light_reaction = ChoiceField( + choices=DailyRound.PupilReactionChoice, required=False + ) + right_pupil_light_reaction = ChoiceField( + choices=DailyRound.PupilReactionChoice, required=False + ) + limb_response_upper_extremity_right = ChoiceField( + choices=DailyRound.LimbResponseChoice, required=False + ) + limb_response_upper_extremity_left = ChoiceField( + choices=DailyRound.LimbResponseChoice, required=False + ) + limb_response_lower_extremity_left = ChoiceField( + choices=DailyRound.LimbResponseChoice, required=False + ) + limb_response_lower_extremity_right = ChoiceField( + choices=DailyRound.LimbResponseChoice, required=False + ) + rhythm = ChoiceField(choices=DailyRound.RythmnChoice, required=False) + ventilator_interface = ChoiceField( + choices=DailyRound.VentilatorInterfaceChoice, required=False + ) + ventilator_mode = ChoiceField( + choices=DailyRound.VentilatorModeChoice, required=False + ) + ventilator_oxygen_modality = ChoiceField( + choices=DailyRound.VentilatorOxygenModalityChoice, required=False + ) + insulin_intake_frequency = ChoiceField( + choices=DailyRound.InsulinIntakeFrequencyChoice, required=False + ) + + clone_last = serializers.BooleanField( + write_only=True, default=False, required=False + ) + + last_edited_by = UserBaseMinimumSerializer(read_only=True) + created_by = UserBaseMinimumSerializer(read_only=True) + + # bed_object = BedSerializer(read_only=True) + + class Meta: + model = DailyRound + read_only_fields = ( + "last_updated_by_telemedicine", + "created_by_telemedicine", + "glasgow_total_calculated", + "total_intake_calculated", + "total_output_calculated", + ) + exclude = ("deleted",) + + def update(self, instance, validated_data): + instance.last_edited_by = self.context["request"].user + + if instance.consultation.discharge_date: + raise ValidationError( + {"consultation": ["Discharged Consultation data cannot be updated"]} + ) + + if ( + "action" in validated_data + or "consultation__review_interval" in validated_data + ): + patient = instance.consultation.patient + + if "action" in validated_data: + action = validated_data.pop("action") + patient.action = action + + if "consultation__review_interval" in validated_data: + review_interval = validated_data.pop("consultation__review_interval") + instance.consultation.review_interval = review_interval + instance.consultation.save(update_fields=["review_interval"]) + if review_interval >= 0: + patient.review_time = localtime(now()) + timedelta( + minutes=review_interval + ) + else: + patient.review_time = None + patient.save() + + validated_data["last_updated_by_telemedicine"] = False + if self.context["request"].user == instance.consultation.assigned_to: + validated_data["last_updated_by_telemedicine"] = True + instance.consultation.save(update_fields=["last_updated_by_telemedicine"]) + + NotificationGenerator( + event=Notification.Event.PATIENT_CONSULTATION_UPDATE_UPDATED, + caused_by=self.context["request"].user, + caused_object=instance, + facility=instance.consultation.patient.facility, + ).generate() + + return super().update(instance, validated_data) + + def update_last_daily_round(self, daily_round_obj): + consultation = daily_round_obj.consultation + consultation.last_daily_round = daily_round_obj + consultation.save() + + NotificationGenerator( + event=Notification.Event.PATIENT_CONSULTATION_UPDATE_CREATED, + caused_by=self.context["request"].user, + caused_object=daily_round_obj, + facility=daily_round_obj.consultation.patient.facility, + ).generate() + + def create(self, validated_data): + # Authorisation Checks + + # Skip check for asset user + if self.context["request"].user.asset_id is None: + allowed_facilities = get_home_facility_queryset( + self.context["request"].user + ) + if not allowed_facilities.filter( + id=self.validated_data["consultation"].facility.id + ).exists(): + raise ValidationError( + { + "facility": "Daily Round creates are only allowed in home facility" + } + ) + + # Authorisation Checks End + + with transaction.atomic(): + if "clone_last" in validated_data: + should_clone = validated_data.pop("clone_last") + if should_clone: + consultation = get_object_or_404( + get_consultation_queryset(self.context["request"].user).filter( + id=validated_data["consultation"].id + ) + ) + last_objects = DailyRound.objects.filter( + consultation=consultation + ).order_by("-created_date") + if not last_objects.exists(): + raise ValidationError( + {"daily_round": "No Daily Round record available to copy"} + ) + + if "rounds_type" not in validated_data: + raise ValidationError( + {"daily_round": "Rounds type is required to clone"} + ) + + rounds_type = validated_data.get("rounds_type") + if rounds_type == DailyRound.RoundsType.NORMAL.value: + fields_to_clone = [ + "consultation_id", + "patient_category", + "taken_at", + "additional_symptoms", + "other_symptoms", + "physical_examination_info", + "other_details", + "recommend_discharge", + "bp", + "pulse", + "resp", + "temperature", + "rhythm", + "rhythm_detail", + "ventilator_spo2", + ] + cloned_daily_round_obj = DailyRound() + for field in fields_to_clone: + value = getattr(last_objects[0], field) + setattr(cloned_daily_round_obj, field, value) + else: + cloned_daily_round_obj = last_objects[0] + + cloned_daily_round_obj.pk = None + cloned_daily_round_obj.rounds_type = rounds_type + cloned_daily_round_obj.created_by = self.context["request"].user + cloned_daily_round_obj.last_edited_by = self.context["request"].user + cloned_daily_round_obj.created_date = timezone.now() + cloned_daily_round_obj.modified_date = timezone.now() + cloned_daily_round_obj.external_id = uuid4() + cloned_daily_round_obj.save() + self.update_last_daily_round(cloned_daily_round_obj) + return self.update(cloned_daily_round_obj, validated_data) + + if ( + "action" in validated_data + or "consultation__review_interval" in validated_data + ): + patient = validated_data["consultation"].patient + + if "action" in validated_data: + action = validated_data.pop("action") + patient.action = action + + if "consultation__review_interval" in validated_data: + review_interval = validated_data.pop( + "consultation__review_interval" + ) + if review_interval >= 0: + validated_data["consultation"].review_interval = review_interval + patient.review_time = localtime(now()) + timedelta( + minutes=review_interval + ) + else: + patient.review_time = None + patient.save() + + validated_data["created_by_telemedicine"] = False + validated_data["last_updated_by_telemedicine"] = False + + if ( + self.context["request"].user + == validated_data["consultation"].assigned_to + ): + validated_data["created_by_telemedicine"] = True + validated_data["last_updated_by_telemedicine"] = True + + daily_round_obj: DailyRound = super().create(validated_data) + daily_round_obj.created_by = self.context["request"].user + daily_round_obj.last_edited_by = self.context["request"].user + daily_round_obj.consultation.last_updated_by_telemedicine = validated_data[ + "last_updated_by_telemedicine" + ] + daily_round_obj.consultation.save( + update_fields=["last_updated_by_telemedicine", "review_interval"] + ) + daily_round_obj.save( + update_fields=[ + "created_by", + "last_edited_by", + ] + ) + + if daily_round_obj.rounds_type != DailyRound.RoundsType.AUTOMATED.value: + self.update_last_daily_round(daily_round_obj) + return daily_round_obj + + def validate(self, obj): + validated = super().validate(obj) + + if validated["consultation"].discharge_date: + raise ValidationError( + {"consultation": ["Discharged Consultation data cannot be updated"]} + ) + + if "action" in validated: + if validated["action"] == PatientRegistration.ActionEnum.REVIEW: + if "consultation__review_interval" not in validated: + raise ValidationError( + { + "review_interval": [ + "This field is required as the patient has been requested Review." + ] + } + ) + if validated["consultation__review_interval"] <= 0: + raise ValidationError( + { + "review_interval": [ + "This field value is must be greater than 0." + ] + } + ) + + if "bed" in validated: + external_id = validated.pop("bed")["external_id"] + if external_id: + # TODO add authorisation checks + bed_object = Bed.objects.filter(external_id=external_id).first() + if not bed_object: + raise ValidationError({"bed": ["Object not found."]}) + validated["bed_id"] = bed_object.id + + return validated diff --git a/care/facility/api/viewsets/daily_round.py b/care/facility/api/viewsets/daily_round.py index 387c5489a3..f000d9a760 100644 --- a/care/facility/api/viewsets/daily_round.py +++ b/care/facility/api/viewsets/daily_round.py @@ -9,7 +9,11 @@ from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet -from care.facility.api.serializers.daily_round import DailyRoundSerializer +from care.facility.api.serializers.daily_round import ( + DailyRoundsDetailSerializer, + DailyRoundSerializer, + DailyRoundsListSerializer, +) from care.facility.api.viewsets.mixins.access import AssetUserAccessMixin from care.facility.models.daily_round import DailyRound from care.facility.models.patient_consultation import PatientConsultation @@ -129,3 +133,8 @@ def analyse(self, request, **kwargs): "page_size": self.PAGE_SIZE, } return Response(final_data) + + def get_serializer_class(self): + if self.action == "list": + return DailyRoundsListSerializer + return DailyRoundsDetailSerializer From cbac5f8860c3b365d90ee4e55bdee6c0062207f3 Mon Sep 17 00:00:00 2001 From: Suyash Singh Date: Sat, 15 Jul 2023 01:44:19 +0530 Subject: [PATCH 2/6] Remove reference of DailyRoundSerializer --- care/facility/api/serializers/daily_round.py | 1 + care/facility/api/viewsets/daily_round.py | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/care/facility/api/serializers/daily_round.py b/care/facility/api/serializers/daily_round.py index 28f1d7dff4..6a7518b56b 100644 --- a/care/facility/api/serializers/daily_round.py +++ b/care/facility/api/serializers/daily_round.py @@ -27,6 +27,7 @@ class DailyRoundSerializer(serializers.ModelSerializer): + # Remove when issue #5492 is fixed id = serializers.CharField(source="external_id", read_only=True) additional_symptoms = serializers.MultipleChoiceField( choices=SYMPTOM_CHOICES, required=False diff --git a/care/facility/api/viewsets/daily_round.py b/care/facility/api/viewsets/daily_round.py index f000d9a760..5235c0e2d2 100644 --- a/care/facility/api/viewsets/daily_round.py +++ b/care/facility/api/viewsets/daily_round.py @@ -11,7 +11,6 @@ from care.facility.api.serializers.daily_round import ( DailyRoundsDetailSerializer, - DailyRoundSerializer, DailyRoundsListSerializer, ) from care.facility.api.viewsets.mixins.access import AssetUserAccessMixin @@ -44,7 +43,7 @@ class DailyRoundsViewSet( mixins.UpdateModelMixin, GenericViewSet, ): - serializer_class = DailyRoundSerializer + serializer_class = DailyRoundsDetailSerializer permission_classes = ( IsAuthenticated, DRYPermissions, From 68b69f1b9f95aad4d6217f503791da24093a4a1e Mon Sep 17 00:00:00 2001 From: Suyash Singh Date: Sat, 15 Jul 2023 02:37:23 +0530 Subject: [PATCH 3/6] "Add tests for detail and list serializer" --- .../tests/test_patient_daily_rounds_api.py | 235 +++++++++++++++++- care/utils/tests/test_base.py | 16 ++ 2 files changed, 246 insertions(+), 5 deletions(-) diff --git a/care/facility/tests/test_patient_daily_rounds_api.py b/care/facility/tests/test_patient_daily_rounds_api.py index 4b675274c6..d1dad8319c 100644 --- a/care/facility/tests/test_patient_daily_rounds_api.py +++ b/care/facility/tests/test_patient_daily_rounds_api.py @@ -1,13 +1,238 @@ +from enum import Enum + from rest_framework import status +from rest_framework.test import APIRequestFactory, APITestCase +from rest_framework_simplejwt.tokens import RefreshToken +from care.facility.tests.mixins import TestClassMixin from care.utils.tests.test_base import TestBase -class TestDailyRoundApi(TestBase): - def get_url(self, external_consultation_id=None): - return f"/api/v1/consultation/{external_consultation_id}/daily_rounds/analyse/" +class ExpectedDailyRoundListData(Enum): + ID = "id" + TEMPERATURE = "temperature" + TEMPERATURE_MEASURED_AT = "temperature_measured_at" + BP = "bp" + RESP = "resp" + SPO2 = "spo2" + VENTILATOR_SPO2 = "ventilator_spo2" + PULSE = "pulse" + CREATED_DATE = "created_date" + ROUNDS_TYPE = "rounds_type" + PATIENT_CATEGORY = "patient_category" + PHYSICAL_EXAMINATION_INFO = "physical_examination_info" + OTHER_DETAILS = "other_details" + LAST_EDITED_BY = "last_edited_by" + CREATED_BY = "created_by" + CREATED_BY_TELEMEDICINE = "created_by_telemedicine" + LAST_UPDATED_BY_TELEMEDICINE = "last_updated_by_telemedicine" + + +class ExpectedDailyRoundCreatedByKeys(Enum): + ID = "id" + FIRST_NAME = "first_name" + LAST_NAME = "last_name" + USER_TYPE = "user_type" + + +class ExpectedDailyRoundLastEditedByKeys(Enum): + ID = "id" + FIRST_NAME = "first_name" + LAST_NAME = "last_name" + USER_TYPE = "user_type" + + +class ExpectedDailyRoundRetrieveData(Enum): + ID = "id" + PATIENT_CATEGORY = "patient_category" + LAST_EDITED_BY = "last_edited_by" + CREATED_BY = "created_by" + ADDITIONAL_SYMPTOMS = "additional_symptoms" + DEPRECATED_COVID_CATEGORY = "deprecated_covid_category" + CURRENT_HEALTH = "current_health" + TAKEN_AT = "taken_at" + ROUNDS_TYPE = "rounds_type" + CONSCIOUSNESS_LEVEL = "consciousness_level" + LEFT_PUPIL_LIGHT_REACTION = "left_pupil_light_reaction" + RIGHT_PUPIL_LIGHT_REACTION = "right_pupil_light_reaction" + LIMB_RESPONSE_UPPER_EXTREMITY_RIGHT = "limb_response_upper_extremity_right" + LIMB_RESPONSE_UPPER_EXTREMITY_LEFT = "limb_response_upper_extremity_left" + LIMB_RESPONSE_LOWER_EXTREMITY_LEFT = "limb_response_lower_extremity_left" + LIMB_RESPONSE_LOWER_EXTREMITY_RIGHT = "limb_response_lower_extremity_right" + RHYTHM = "rhythm" + VENTILATOR_INTERFACE = "ventilator_interface" + VENTILATOR_MODE = "ventilator_mode" + VENTILATOR_OXYGEN_MODALITY = "ventilator_oxygen_modality" + INSULIN_INTAKE_FREQUENCY = "insulin_intake_frequency" + EXTERNAL_ID = "external_id" + CREATED_DATE = "created_date" + MODIFIED_DATE = "modified_date" + TEMPERATURE = "temperature" + SPO2 = "spo2" + TEMPERATURE_MEASURED_AT = "temperature_measured_at" + PHYSICAL_EXAMINATION_INFO = "physical_examination_info" + OTHER_SYMPTOMS = "other_symptoms" + RECOMMEND_DISCHARGE = "recommend_discharge" + OTHER_DETAILS = "other_details" + MEDICATION_GIVEN = "medication_given" + LAST_UPDATED_BY_TELEMEDICINE = "last_updated_by_telemedicine" + CREATED_BY_TELEMEDICINE = "created_by_telemedicine" + CONSCIOUSNESS_LEVEL_DETAIL = "consciousness_level_detail" + IN_PRONE_POSITION = "in_prone_position" + LEFT_PUPIL_SIZE = "left_pupil_size" + LEFT_PUPIL_SIZE_DETAIL = "left_pupil_size_detail" + LEFT_PUPIL_LIGHT_REACTION_DETAIL = "left_pupil_light_reaction_detail" + RIGHT_PUPIL_SIZE = "right_pupil_size" + RIGHT_PUPIL_SIZE_DETAIL = "right_pupil_size_detail" + RIGHT_PUPIL_LIGHT_REACTION_DETAIL = "right_pupil_light_reaction_detail" + GLASGOW_EYE_OPEN = "glasgow_eye_open" + GLASGOW_VERBAL_RESPONSE = "glasgow_verbal_response" + GLASGOW_MOTOR_RESPONSE = "glasgow_motor_response" + GLASGOW_TOTAL_CALCULATED = "glasgow_total_calculated" + BP = "bp" + PULSE = "pulse" + RESP = "resp" + RHYTHM_DETAIL = "rhythm_detail" + VENTILATOR_PEEP = "ventilator_peep" + VENTILATOR_PIP = "ventilator_pip" + VENTILATOR_MEAN_AIRWAY_PRESSURE = "ventilator_mean_airway_pressure" + VENTILATOR_RESP_RATE = "ventilator_resp_rate" + VENTILATOR_PRESSURE_SUPPORT = "ventilator_pressure_support" + VENTILATOR_TIDAL_VOLUME = "ventilator_tidal_volume" + VENTILATOR_OXYGEN_MODALITY_OXYGEN_RATE = "ventilator_oxygen_modality_oxygen_rate" + VENTILATOR_OXYGEN_MODALITY_FLOW_RATE = "ventilator_oxygen_modality_flow_rate" + VENTILATOR_FI02 = "ventilator_fi02" + VENTILATOR_SPO2 = "ventilator_spo2" + ETCO2 = "etco2" + BILATERAL_AIR_ENTRY = "bilateral_air_entry" + PAIN = "pain" + PAIN_SCALE_ENHANCED = "pain_scale_enhanced" + PH = "ph" + PCO2 = "pco2" + PO2 = "po2" + HCO3 = "hco3" + BASE_EXCESS = "base_excess" + LACTATE = "lactate" + SODIUM = "sodium" + POTASSIUM = "potassium" + BLOOD_SUGAR_LEVEL = "blood_sugar_level" + INSULIN_INTAKE_DOSE = "insulin_intake_dose" + INFUSIONS = "infusions" + IV_FLUIDS = "iv_fluids" + FEEDS = "feeds" + TOTAL_INTAKE_CALCULATED = "total_intake_calculated" + OUTPUT = "output" + TOTAL_OUTPUT_CALCULATED = "total_output_calculated" + DIALYSIS_FLUID_BALANCE = "dialysis_fluid_balance" + DIALYSIS_NET_BALANCE = "dialysis_net_balance" + PRESSURE_SORE = "pressure_sore" + NURSING = "nursing" + MEDICINE_ADMINISTRATION = "medicine_administration" + META = "meta" + CONSULTATION = "consultation" + + +class ExpectedCreatedByRetrieveKeys(Enum): + ID = "id" + FIRST_NAME = "first_name" + USERNAME = "username" + EMAIL = "email" + LAST_NAME = "last_name" + USER_TYPE = "user_type" + LAST_LOGIN = "last_login" + HOME_FACILITY = "home_facility" + + +class ExpectedLastEditedByRetrieveKeys(Enum): + ID = "id" + FIRST_NAME = "first_name" + USERNAME = "username" + EMAIL = "email" + LAST_NAME = "last_name" + USER_TYPE = "user_type" + LAST_LOGIN = "last_login" + HOME_FACILITY = "home_facility" + + +class TestDailyRoundApi(TestBase, TestClassMixin, APITestCase): + analyse_url = "/api/v1/consultation/{}/daily_rounds/analyse/" + daily_rounds_url = "/api/v1/consultation/{}/daily_rounds/" + + def setUp(self): + self.factory = APIRequestFactory() + + self.consultation = self.create_consultation() + + # create daily round + self.daily_round = self.create_daily_round(consultation=self.consultation) + + refresh_token = RefreshToken.for_user(self.user) + self.client.credentials( + HTTP_AUTHORIZATION=f"Bearer {refresh_token.access_token}" + ) def test_external_consultation_does_not_exists_returns_404(self): - sample_uuid = "e4a3d84a-d678-4992-9287-114f029046d8" - response = self.client.get(self.get_url(sample_uuid)) + external_consultation_id = "e4a3d84a-d678-4992-9287-114f029046d8" + response = self.client.get(self.analyse_url.format(external_consultation_id)) self.assertEquals(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_daily_rounds_list(self): + response = self.client.get( + self.daily_rounds_url.format(self.consultation.external_id) + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + data = response.json() + + results = data["results"] + self.assertIsInstance(results, list) + + for result in results: + self.assertCountEqual( + result.keys(), + [item.value for item in ExpectedDailyRoundListData], + ) + + created_by_content = result["created_by"] + + if created_by_content is not None: + self.assertCountEqual( + created_by_content.keys(), + [item.value for item in ExpectedDailyRoundCreatedByKeys], + ) + + last_edited_by = result["last_edited_by"] + + if last_edited_by is not None: + self.assertCountEqual( + last_edited_by.keys(), + [item.value for item in ExpectedDailyRoundLastEditedByKeys], + ) + + def test_daily_rounds_retrieve(self): + response = self.client.get( + self.daily_rounds_url.format(self.consultation.external_id) + + str(self.daily_round.external_id) + + "/" + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + data = response.json() + self.assertCountEqual( + data.keys(), + [item.value for item in ExpectedDailyRoundRetrieveData], + ) + + created_by_content = data["created_by"] + + if created_by_content is not None: + self.assertCountEqual( + created_by_content.keys(), + [item.value for item in ExpectedCreatedByRetrieveKeys], + ) + + last_edited_by = data["last_edited_by"] + + if last_edited_by is not None: + self.assertCountEqual( + last_edited_by.keys(), + [item.value for item in ExpectedLastEditedByRetrieveKeys], + ) diff --git a/care/utils/tests/test_base.py b/care/utils/tests/test_base.py index 3ed212c1b2..85c6cae3b2 100644 --- a/care/utils/tests/test_base.py +++ b/care/utils/tests/test_base.py @@ -14,6 +14,7 @@ COVID_CATEGORY_CHOICES, DISEASE_CHOICES_MAP, SYMPTOM_CHOICES, + DailyRound, Disease, DiseaseStatusEnum, Facility, @@ -448,3 +449,18 @@ def create_patient_note( } data.update(kwargs) return PatientNotes.objects.create(**data) + + def create_daily_round(self, consultation=None): + data = { + "consultation": consultation or self.consultation, + "temperature": 98.6, + "spo2": 98, + "temperature_measured_at": make_aware( + datetime.datetime(2020, 4, 7, 15, 30) + ), + "physical_examination_info": "physical_examination_info", + "additional_symptoms": [SYMPTOM_CHOICES[0][0], SYMPTOM_CHOICES[1][0]], + "other_symptoms": "No other symptoms", + "created_by": self.user, + } + return DailyRound.objects.create(**data) From cad87d49705b46d0f626515459b7a39c3eabb992 Mon Sep 17 00:00:00 2001 From: Suyash Singh Date: Fri, 28 Jul 2023 12:11:49 +0530 Subject: [PATCH 4/6] fix test cases --- .../tests/test_patient_daily_rounds_api.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/care/facility/tests/test_patient_daily_rounds_api.py b/care/facility/tests/test_patient_daily_rounds_api.py index d1dad8319c..afcd6d04a7 100644 --- a/care/facility/tests/test_patient_daily_rounds_api.py +++ b/care/facility/tests/test_patient_daily_rounds_api.py @@ -1,10 +1,9 @@ from enum import Enum from rest_framework import status -from rest_framework.test import APIRequestFactory, APITestCase +from rest_framework.test import APIRequestFactory from rest_framework_simplejwt.tokens import RefreshToken -from care.facility.tests.mixins import TestClassMixin from care.utils.tests.test_base import TestBase @@ -140,7 +139,6 @@ class ExpectedCreatedByRetrieveKeys(Enum): LAST_NAME = "last_name" USER_TYPE = "user_type" LAST_LOGIN = "last_login" - HOME_FACILITY = "home_facility" class ExpectedLastEditedByRetrieveKeys(Enum): @@ -154,18 +152,21 @@ class ExpectedLastEditedByRetrieveKeys(Enum): HOME_FACILITY = "home_facility" -class TestDailyRoundApi(TestBase, TestClassMixin, APITestCase): +class TestDailyRoundApi(TestBase): analyse_url = "/api/v1/consultation/{}/daily_rounds/analyse/" daily_rounds_url = "/api/v1/consultation/{}/daily_rounds/" - def setUp(self): - self.factory = APIRequestFactory() + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.factory = APIRequestFactory() - self.consultation = self.create_consultation() + cls.consultation = cls.create_consultation() # create daily round - self.daily_round = self.create_daily_round(consultation=self.consultation) + cls.daily_round = cls.create_daily_round(consultation=cls.consultation) + def setUp(self): refresh_token = RefreshToken.for_user(self.user) self.client.credentials( HTTP_AUTHORIZATION=f"Bearer {refresh_token.access_token}" From af6782e911dbbe5ae82b2e24b16af90a9731bd46 Mon Sep 17 00:00:00 2001 From: Suyash Singh Date: Fri, 28 Jul 2023 15:54:54 +0530 Subject: [PATCH 5/6] Fix test case by converting to classmethods --- care/utils/tests/test_base.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/care/utils/tests/test_base.py b/care/utils/tests/test_base.py index 85c6cae3b2..35a8889754 100644 --- a/care/utils/tests/test_base.py +++ b/care/utils/tests/test_base.py @@ -439,20 +439,22 @@ def create_consultation( data.update(kwargs) return PatientConsultation.objects.create(**data) + @classmethod def create_patient_note( - self, patient=None, facility=None, note="Patient is doing find", **kwargs + cls, patient=None, facility=None, note="Patient is doing find", **kwargs ): data = { - "patient": patient or self.patient, - "facility": facility or self.facility, + "patient": patient or cls.patient, + "facility": facility or cls.facility, "note": note, } data.update(kwargs) return PatientNotes.objects.create(**data) - def create_daily_round(self, consultation=None): + @classmethod + def create_daily_round(cls, consultation=None): data = { - "consultation": consultation or self.consultation, + "consultation": consultation or cls.consultation, "temperature": 98.6, "spo2": 98, "temperature_measured_at": make_aware( @@ -461,6 +463,6 @@ def create_daily_round(self, consultation=None): "physical_examination_info": "physical_examination_info", "additional_symptoms": [SYMPTOM_CHOICES[0][0], SYMPTOM_CHOICES[1][0]], "other_symptoms": "No other symptoms", - "created_by": self.user, + "created_by": cls.user, } return DailyRound.objects.create(**data) From 4fd81aa69bda7c8c8794caa5eb994a3ddf905d7e Mon Sep 17 00:00:00 2001 From: Suyash Singh Date: Wed, 6 Sep 2023 00:33:10 +0530 Subject: [PATCH 6/6] Fix: replace with userbaseminimumserializer --- care/facility/api/serializers/daily_round.py | 14 ++----------- .../tests/test_patient_daily_rounds_api.py | 21 +++++-------------- 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/care/facility/api/serializers/daily_round.py b/care/facility/api/serializers/daily_round.py index 6a7518b56b..820fede8db 100644 --- a/care/facility/api/serializers/daily_round.py +++ b/care/facility/api/serializers/daily_round.py @@ -13,7 +13,6 @@ CATEGORY_CHOICES, COVID_CATEGORY_CHOICES, PatientRegistration, - User, ) from care.facility.models.bed import Bed from care.facility.models.daily_round import DailyRound @@ -332,20 +331,11 @@ def validate(self, obj): return validated -class UserDailyRoundsMinimumSerializer(serializers.ModelSerializer): - id = serializers.CharField(source="external_id", read_only=True) - user_type = ChoiceField(choices=User.TYPE_CHOICES, read_only=True) - - class Meta: - model = User - fields = ("id", "first_name", "last_name", "user_type") - - class DailyRoundsListSerializer(serializers.ModelSerializer): id = serializers.CharField(source="external_id", read_only=True) patient_category = ChoiceField(choices=CATEGORY_CHOICES, required=False) - last_edited_by = UserDailyRoundsMinimumSerializer(read_only=True) - created_by = UserDailyRoundsMinimumSerializer(read_only=True) + last_edited_by = UserBaseMinimumSerializer(read_only=True) + created_by = UserBaseMinimumSerializer(read_only=True) class Meta: model = DailyRound diff --git a/care/facility/tests/test_patient_daily_rounds_api.py b/care/facility/tests/test_patient_daily_rounds_api.py index afcd6d04a7..51cd9cbf55 100644 --- a/care/facility/tests/test_patient_daily_rounds_api.py +++ b/care/facility/tests/test_patient_daily_rounds_api.py @@ -131,7 +131,7 @@ class ExpectedDailyRoundRetrieveData(Enum): CONSULTATION = "consultation" -class ExpectedCreatedByRetrieveKeys(Enum): +class UserBaseMinimumKeys(Enum): ID = "id" FIRST_NAME = "first_name" USERNAME = "username" @@ -141,17 +141,6 @@ class ExpectedCreatedByRetrieveKeys(Enum): LAST_LOGIN = "last_login" -class ExpectedLastEditedByRetrieveKeys(Enum): - ID = "id" - FIRST_NAME = "first_name" - USERNAME = "username" - EMAIL = "email" - LAST_NAME = "last_name" - USER_TYPE = "user_type" - LAST_LOGIN = "last_login" - HOME_FACILITY = "home_facility" - - class TestDailyRoundApi(TestBase): analyse_url = "/api/v1/consultation/{}/daily_rounds/analyse/" daily_rounds_url = "/api/v1/consultation/{}/daily_rounds/" @@ -198,7 +187,7 @@ def test_daily_rounds_list(self): if created_by_content is not None: self.assertCountEqual( created_by_content.keys(), - [item.value for item in ExpectedDailyRoundCreatedByKeys], + [item.value for item in UserBaseMinimumKeys], ) last_edited_by = result["last_edited_by"] @@ -206,7 +195,7 @@ def test_daily_rounds_list(self): if last_edited_by is not None: self.assertCountEqual( last_edited_by.keys(), - [item.value for item in ExpectedDailyRoundLastEditedByKeys], + [item.value for item in UserBaseMinimumKeys], ) def test_daily_rounds_retrieve(self): @@ -227,7 +216,7 @@ def test_daily_rounds_retrieve(self): if created_by_content is not None: self.assertCountEqual( created_by_content.keys(), - [item.value for item in ExpectedCreatedByRetrieveKeys], + [item.value for item in UserBaseMinimumKeys], ) last_edited_by = data["last_edited_by"] @@ -235,5 +224,5 @@ def test_daily_rounds_retrieve(self): if last_edited_by is not None: self.assertCountEqual( last_edited_by.keys(), - [item.value for item in ExpectedLastEditedByRetrieveKeys], + [item.value for item in UserBaseMinimumKeys], )