diff --git a/tmh_registry/common/utils/__init__.py b/tmh_registry/common/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tmh_registry/common/utils/functions.py b/tmh_registry/common/utils/functions.py new file mode 100644 index 0000000..73a1b1f --- /dev/null +++ b/tmh_registry/common/utils/functions.py @@ -0,0 +1,4 @@ +def get_text_choice_value_from_label(choices, label): + return [ + choice[0] for choice in choices if choice[1].upper() == label.upper() + ][0] diff --git a/tmh_registry/registry/api/serializers.py b/tmh_registry/registry/api/serializers.py index bd293d3..a66955b 100644 --- a/tmh_registry/registry/api/serializers.py +++ b/tmh_registry/registry/api/serializers.py @@ -1,14 +1,16 @@ from drf_yasg.utils import swagger_serializer_method from rest_framework.exceptions import ValidationError -from rest_framework.fields import IntegerField +from rest_framework.fields import CharField, IntegerField from rest_framework.relations import PrimaryKeyRelatedField from rest_framework.serializers import ModelSerializer, SerializerMethodField +from ...common.utils.functions import get_text_choice_value_from_label from ...users.api.serializers import MedicalPersonnelSerializer from ...users.models import MedicalPersonnel from ..models import ( Discharge, Episode, + FollowUp, Hospital, Patient, PatientHospitalMapping, @@ -50,6 +52,7 @@ class ReadPatientSerializer(ModelSerializer): age = IntegerField(allow_null=True) hospital_mappings = PatientHospitalMappingPatientSerializer(many=True) episodes = SerializerMethodField() + gender = CharField(source="get_gender_display") @swagger_serializer_method(serializer_or_field=EpisodeSerializer) def get_episodes(self, obj): @@ -97,11 +100,11 @@ class CreatePatientSerializer(ModelSerializer): hospital_id = IntegerField(write_only=True) patient_hospital_id = IntegerField(write_only=True) year_of_birth = IntegerField(allow_null=True) + gender = CharField(allow_null=True) class Meta: model = Patient fields = [ - "id", "full_name", "national_id", "age", @@ -168,6 +171,14 @@ def create(self, validated_data): {"error": "The patient needs to be registered to a hospital."} ) + validated_data["gender"] = ( + get_text_choice_value_from_label( + Patient.Gender.choices, validated_data["gender"] + ) + if validated_data["gender"] + else validated_data["gender"] + ) + validated_data.pop("age", None) new_patient = super(CreatePatientSerializer, self).create( validated_data @@ -249,6 +260,14 @@ def create(self, validated_data): class EpisodeReadSerializer(ModelSerializer): patient_hospital_mapping = PatientHospitalMappingReadSerializer() surgeons = MedicalPersonnelSerializer(many=True) + episode_type = CharField(source="get_episode_type_display") + cepod = CharField(source="get_cepod_display") + side = CharField(source="get_side_display") + occurence = CharField(source="get_occurence_display") + type = CharField(source="get_type_display") + complexity = CharField(source="get_complexity_display") + mesh_type = CharField(source="get_mesh_type_display") + anaesthetic_type = CharField(source="get_anaesthetic_type_display") class Meta: model = Episode @@ -281,6 +300,14 @@ class EpisodeWriteSerializer(ModelSerializer): surgeon_ids = PrimaryKeyRelatedField( write_only=True, many=True, queryset=MedicalPersonnel.objects.all() ) + episode_type = CharField() + cepod = CharField() + side = CharField() + occurence = CharField() + type = CharField() + complexity = CharField() + mesh_type = CharField() + anaesthetic_type = CharField() class Meta: model = Episode @@ -325,20 +352,46 @@ def create(self, validated_data): } ) - episode = Episode.objects.create( - patient_hospital_mapping=patient_hospital_mapping, - surgery_date=validated_data["surgery_date"], - episode_type=validated_data["episode_type"], - comments=validated_data["comments"], - cepod=validated_data["cepod"], - side=validated_data["side"], - occurence=validated_data["occurence"], - type=validated_data["type"], - complexity=validated_data["complexity"], - mesh_type=validated_data["mesh_type"], - anaesthetic_type=validated_data["anaesthetic_type"], - diathermy_used=validated_data["diathermy_used"], - ) + try: + episode = Episode.objects.create( + patient_hospital_mapping=patient_hospital_mapping, + surgery_date=validated_data["surgery_date"], + episode_type=get_text_choice_value_from_label( + Episode.EpisodeChoices.choices, + validated_data["episode_type"], + ), + comments=validated_data["comments"], + cepod=get_text_choice_value_from_label( + Episode.CepodChoices.choices, validated_data["cepod"] + ), + side=get_text_choice_value_from_label( + Episode.SideChoices.choices, validated_data["side"] + ), + occurence=get_text_choice_value_from_label( + Episode.OccurenceChoices.choices, + validated_data["occurence"], + ), + type=get_text_choice_value_from_label( + Episode.TypeChoices.choices, validated_data["type"] + ), + complexity=get_text_choice_value_from_label( + Episode.ComplexityChoices.choices, + validated_data["complexity"], + ), + mesh_type=get_text_choice_value_from_label( + Episode.MeshTypeChoices.choices, + validated_data["mesh_type"], + ), + anaesthetic_type=get_text_choice_value_from_label( + Episode.AnaestheticChoices.choices, + validated_data["anaesthetic_type"], + ), + diathermy_used=validated_data["diathermy_used"], + ) + except IndexError: + raise ValidationError( + {"error": "Not supported value provided for ChoiceField."} + ) if surgeons: episode.surgeons.set(surgeons) @@ -396,3 +449,88 @@ def create(self, validated_data): ) return discharge + + +class FollowUpReadSerializer(ModelSerializer): + episode = EpisodeReadSerializer() + pain_severity = CharField(source="get_pain_severity_display") + attendees = MedicalPersonnelSerializer(many=True) + + class Meta: + model = FollowUp + fields = [ + "id", + "episode", + "date", + "pain_severity", + "attendees", + "mesh_awareness", + "seroma", + "infection", + "numbness", + ] + + +class FollowUpWriteSerializer(ModelSerializer): + episode_id = PrimaryKeyRelatedField( + write_only=True, queryset=Episode.objects.exclude(discharge=None) + ) + attendee_ids = PrimaryKeyRelatedField( + write_only=True, many=True, queryset=MedicalPersonnel.objects.all() + ) + pain_severity = CharField() + + class Meta: + model = FollowUp + fields = [ + "episode_id", + "date", + "pain_severity", + "attendee_ids", + "mesh_awareness", + "seroma", + "infection", + "numbness", + ] + + def to_representation(self, instance): + serializer = FollowUpReadSerializer(instance) + return serializer.data + + def create(self, validated_data): + episode = validated_data["episode_id"] + attendees = validated_data["attendee_ids"] + + if episode.surgery_date > validated_data["date"]: + raise ValidationError( + { + "error": "Episode surgery date cannot be after Follow Up date" + } + ) + + if episode.discharge.date > validated_data["date"]: + raise ValidationError( + { + "error": "Episode Discharge date cannot be after Follow Up date" + } + ) + + pain_severity = validated_data.get("pain_severity", "") + print(f"{pain_severity=}") + follow_up = FollowUp.objects.create( + episode_id=episode.id, + date=validated_data["date"], + pain_severity=get_text_choice_value_from_label( + FollowUp.PainSeverityChoices.choices, pain_severity + ) + if pain_severity + else "", + mesh_awareness=validated_data["mesh_awareness"], + seroma=validated_data["seroma"], + infection=validated_data["infection"], + numbness=validated_data["numbness"], + ) + + follow_up.attendees.set(attendees) + + return follow_up diff --git a/tmh_registry/registry/api/viewsets.py b/tmh_registry/registry/api/viewsets.py index 5131884..2734748 100644 --- a/tmh_registry/registry/api/viewsets.py +++ b/tmh_registry/registry/api/viewsets.py @@ -15,6 +15,7 @@ from ..models import ( Discharge, Episode, + FollowUp, Hospital, Patient, PatientHospitalMapping, @@ -25,6 +26,8 @@ DischargeWriteSerializer, EpisodeReadSerializer, EpisodeWriteSerializer, + FollowUpReadSerializer, + FollowUpWriteSerializer, HospitalSerializer, PatientHospitalMappingReadSerializer, PatientHospitalMappingWriteSerializer, @@ -73,7 +76,11 @@ class Meta: @method_decorator( name="create", decorator=swagger_auto_schema( - responses={201: ReadPatientSerializer(many=True)} + operation_summary="Register a Patient", + operation_description="Use this endpoint to register a patient. A PatientHospitalMapping will be created " + "automatically for the newly created Patient and the provided Hospital.\n " + f"\nAccepted values for `gender` are `{Patient.Gender.labels}`. \n ", + responses={201: ReadPatientSerializer(many=True)}, ), ) @method_decorator( @@ -143,7 +150,21 @@ def get_serializer_class(self): @method_decorator( name="create", - decorator=swagger_auto_schema(responses={201: EpisodeReadSerializer()}), + decorator=swagger_auto_schema( + operation_summary="Register an Episode", + operation_description="Use this endpoint to register an episode. Keep in mind that you need to create a " + "`PatientHospitalMapping`(through the POST /patient-hospital-mappings/ endpoint) " + "if one does not already exist for this specific Patient/Hospital pair.\n " + f"\nAccepted values for `episode_type` are `{Episode.EpisodeChoices.labels}`. \n " + f"\nAccepted values for `cepod` are `{Episode.CepodChoices.labels}`. \n " + f"\nAccepted values for `side` are `{Episode.SideChoices.labels}`. \n " + f"\nAccepted values for `occurence` are `{Episode.OccurenceChoices.labels}`. \n " + f"\nAccepted values for `type` are `{Episode.TypeChoices.labels}`. \n " + f"\nAccepted values for `complexity` are `{Episode.ComplexityChoices.labels}`. \n " + f"\nAccepted values for `mesh_type` are `{Episode.MeshTypeChoices.labels}`. \n " + f"\nAccepted values for `anaesthetic_type` are `{Episode.AnaestheticChoices.labels}`. \n ", + responses={201: EpisodeReadSerializer()}, + ), ) class EpisodeViewset(CreateModelMixin, GenericViewSet): queryset = Episode.objects.all() @@ -159,7 +180,12 @@ def get_serializer_class(self): @method_decorator( name="create", - decorator=swagger_auto_schema(responses={201: DischargeReadSerializer()}), + decorator=swagger_auto_schema( + operation_summary="Discharge a Patient", + operation_description="Use this endpoint to discharge a patient. Only one Discharge can be registered " + "for the same Episode.", + responses={201: DischargeReadSerializer()}, + ), ) class DischargeViewset(CreateModelMixin, GenericViewSet): queryset = Discharge.objects.all() @@ -171,3 +197,26 @@ def get_serializer_class(self): return DischargeWriteSerializer raise NotImplementedError + + +@method_decorator( + name="create", + decorator=swagger_auto_schema( + operation_summary="Register a Follow Up", + operation_description="Use this endpoint to register a Follow Up. Multiple Follow Ups can be registered " + "for the same Episode.\n " + "\nThe accepted values for `pain_severity` are " + f"`{FollowUp.PainSeverityChoices.labels}`.", + responses={201: FollowUpReadSerializer()}, + ), +) +class FollowUpViewset(CreateModelMixin, GenericViewSet): + queryset = FollowUp.objects.all() + + def get_serializer_class(self): + if self.action in ["list", "retrieve"]: + return FollowUpReadSerializer + if self.action == "create": + return FollowUpWriteSerializer + + raise NotImplementedError diff --git a/tmh_registry/registry/factories.py b/tmh_registry/registry/factories.py index 405ab10..deca219 100644 --- a/tmh_registry/registry/factories.py +++ b/tmh_registry/registry/factories.py @@ -8,6 +8,7 @@ from .models import ( Discharge, Episode, + FollowUp, Hospital, Patient, PatientHospitalMapping, @@ -108,3 +109,30 @@ class Meta: date = LazyAttribute(lambda _: faker.date_object()) aware_of_mesh = LazyAttribute(lambda _: faker.boolean()) infection = LazyAttribute(lambda _: faker.boolean()) + + +class FollowUpFactory(DjangoModelFactory): + class Meta: + model = FollowUp + + episode = SubFactory(EpisodeFactory) + date = LazyAttribute(lambda _: faker.date_object()) + pain_severity = LazyAttribute( + lambda _: faker.random_element(FollowUp.PainSeverityChoices.values) + ) + mesh_awareness = LazyAttribute(lambda _: faker.boolean()) + seroma = LazyAttribute(lambda _: faker.boolean()) + infection = LazyAttribute(lambda _: faker.boolean()) + numbness = LazyAttribute(lambda _: faker.boolean()) + + @post_generation + def attendees(self, create, extracted, **kwargs): + if not create: + # Simple build, do nothing. + return + + if extracted: + # A list of groups were passed in, use them + self.attendees.add(*extracted) + else: + self.attendees.add(MedicalPersonnelFactory()) diff --git a/tmh_registry/registry/migrations/0015_auto_20220222_1043.py b/tmh_registry/registry/migrations/0015_auto_20220222_1043.py new file mode 100644 index 0000000..3f82498 --- /dev/null +++ b/tmh_registry/registry/migrations/0015_auto_20220222_1043.py @@ -0,0 +1,29 @@ +# Generated by Django 3.1.3 on 2022-02-22 10:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("registry", "0014_remove_episode_discharge_date"), + ] + + operations = [ + migrations.RenameField( + model_name="followup", + old_name="follow_up_date", + new_name="date", + ), + migrations.AddField( + model_name="followup", + name="created_at", + field=models.DateField(auto_now_add=True, default="1998-1-1"), + preserve_default=False, + ), + migrations.AddField( + model_name="followup", + name="updated_at", + field=models.DateField(auto_now=True), + ), + ] diff --git a/tmh_registry/registry/migrations/0016_auto_20220222_1415.py b/tmh_registry/registry/migrations/0016_auto_20220222_1415.py new file mode 100644 index 0000000..a430b3f --- /dev/null +++ b/tmh_registry/registry/migrations/0016_auto_20220222_1415.py @@ -0,0 +1,23 @@ +# Generated by Django 3.1.3 on 2022-02-22 14:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("registry", "0015_auto_20220222_1043"), + ] + + operations = [ + migrations.AlterField( + model_name="patient", + name="gender", + field=models.CharField( + blank=True, + choices=[("MALE", "Male"), ("FEMALE", "Female")], + max_length=32, + null=True, + ), + ), + ] diff --git a/tmh_registry/registry/models.py b/tmh_registry/registry/models.py index 74c7f9c..ed9662c 100644 --- a/tmh_registry/registry/models.py +++ b/tmh_registry/registry/models.py @@ -29,8 +29,8 @@ def __str__(self): class Patient(TimeStampMixin): class Gender(TextChoices): - MALE = ("Male", "Male") - FEMALE = ("Female", "Female") + MALE = ("MALE", "Male") + FEMALE = ("FEMALE", "Female") full_name = CharField(max_length=255) national_id = CharField(max_length=20, null=True, blank=True, unique=True) @@ -165,7 +165,7 @@ class Meta: verbose_name_plural = "Discharges" -class FollowUp(Model): +class FollowUp(TimeStampMixin): class PainSeverityChoices(TextChoices): NO_PAIN = ("NO_PAIN", "No Pain") MINIMAL = ("MINIMAL", "Minimal") @@ -174,7 +174,7 @@ class PainSeverityChoices(TextChoices): SEVERE = ("SEVERE", "Severe") episode = ForeignKey(Episode, on_delete=CASCADE) - follow_up_date = DateField() + date = DateField() pain_severity = CharField( max_length=16, choices=PainSeverityChoices.choices ) diff --git a/tmh_registry/registry/tests/api/viewsets/test_discharge.py b/tmh_registry/registry/tests/api/viewsets/test_discharges.py similarity index 90% rename from tmh_registry/registry/tests/api/viewsets/test_discharge.py rename to tmh_registry/registry/tests/api/viewsets/test_discharges.py index 0000251..f4b48bd 100644 --- a/tmh_registry/registry/tests/api/viewsets/test_discharge.py +++ b/tmh_registry/registry/tests/api/viewsets/test_discharges.py @@ -21,7 +21,7 @@ def setUp(self) -> None: self.client = APIClient() self.client.credentials(HTTP_AUTHORIZATION="Token " + self.token.key) - def test_discharge_data(self): + def get_discharge_data(self): return { "episode_id": self.episode.id, "date": "2022-02-22", @@ -30,8 +30,7 @@ def test_discharge_data(self): } def test_successful(self): - data = self.test_discharge_data() - print(f"{data['episode_id']=}") + data = self.get_discharge_data() response = self.client.post( "/api/v1/discharges/", data=data, format="json" ) @@ -44,7 +43,7 @@ def test_successful(self): self.assertEqual(response.data["infection"], data["infection"]) def test_when_episode_id_does_not_exist(self): - data = self.test_discharge_data() + data = self.get_discharge_data() data["episode_id"] = -1 response = self.client.post( "/api/v1/discharges/", data=data, format="json" @@ -54,7 +53,7 @@ def test_when_episode_id_does_not_exist(self): def test_when_episode_is_already_discharged(self): DischargeFactory(episode=self.episode, date="2022-02-22") - data = self.test_discharge_data() + data = self.get_discharge_data() response = self.client.post( "/api/v1/discharges/", data=data, format="json" ) @@ -62,7 +61,7 @@ def test_when_episode_is_already_discharged(self): self.assertEqual(HTTP_400_BAD_REQUEST, response.status_code) def test_when_episode_surgery_date_is_after_discharge_date(self): - data = self.test_discharge_data() + data = self.get_discharge_data() data["date"] = "2021-12-03" response = self.client.post( "/api/v1/discharges/", data=data, format="json" diff --git a/tmh_registry/registry/tests/api/viewsets/test_episodes.py b/tmh_registry/registry/tests/api/viewsets/test_episodes.py index c3fe0dd..7c88fde 100644 --- a/tmh_registry/registry/tests/api/viewsets/test_episodes.py +++ b/tmh_registry/registry/tests/api/viewsets/test_episodes.py @@ -6,6 +6,9 @@ from rest_framework.status import HTTP_201_CREATED, HTTP_400_BAD_REQUEST from rest_framework.test import APIClient +from tmh_registry.common.utils.functions import ( + get_text_choice_value_from_label, +) from tmh_registry.registry.factories import ( HospitalFactory, PatientFactory, @@ -34,16 +37,16 @@ def get_episode_test_data(self): "patient_id": self.patient.id, "hospital_id": self.hospital.id, "surgery_date": "2021-10-12", - "episode_type": Episode.EpisodeChoices.UMBILICAL.value, + "episode_type": Episode.EpisodeChoices.UMBILICAL.label, "surgeon_ids": [self.medical_personnel.id], "comments": "A random comment", - "cepod": Episode.CepodChoices.PLANNED.value, - "side": Episode.SideChoices.LEFT.value, - "occurence": Episode.OccurenceChoices.RECURRENT.value, - "type": Episode.TypeChoices.INDIRECT.value, - "complexity": Episode.ComplexityChoices.INCARCERATED.value, - "mesh_type": Episode.MeshTypeChoices.TNMHP.value, - "anaesthetic_type": Episode.AnaestheticChoices.SPINAL.value, + "cepod": Episode.CepodChoices.PLANNED.label, + "side": Episode.SideChoices.LEFT.label, + "occurence": Episode.OccurenceChoices.RECURRENT.label, + "type": Episode.TypeChoices.INDIRECT.label, + "complexity": Episode.ComplexityChoices.INCARCERATED.label, + "mesh_type": Episode.MeshTypeChoices.TNMHP.label, + "anaesthetic_type": Episode.AnaestheticChoices.SPINAL.label, "diathermy_used": True, } @@ -121,3 +124,55 @@ def test_create_episode_successful(self): self.assertEqual( response.data["diathermy_used"], data["diathermy_used"] ) + + # assert that values are stored in the db, not labels + episode = Episode.objects.get(id=response.data["id"]) + + self.assertEqual( + episode.episode_type, + get_text_choice_value_from_label( + Episode.EpisodeChoices.choices, data["episode_type"] + ), + ) + self.assertEqual( + episode.cepod, + get_text_choice_value_from_label( + Episode.CepodChoices.choices, data["cepod"] + ), + ) + self.assertEqual( + episode.side, + get_text_choice_value_from_label( + Episode.SideChoices.choices, data["side"] + ), + ) + self.assertEqual( + episode.occurence, + get_text_choice_value_from_label( + Episode.OccurenceChoices.choices, data["occurence"] + ), + ) + self.assertEqual( + episode.type, + get_text_choice_value_from_label( + Episode.TypeChoices.choices, data["type"] + ), + ) + self.assertEqual( + episode.complexity, + get_text_choice_value_from_label( + Episode.ComplexityChoices.choices, data["complexity"] + ), + ) + self.assertEqual( + episode.mesh_type, + get_text_choice_value_from_label( + Episode.MeshTypeChoices.choices, data["mesh_type"] + ), + ) + self.assertEqual( + episode.anaesthetic_type, + get_text_choice_value_from_label( + Episode.AnaestheticChoices.choices, data["anaesthetic_type"] + ), + ) diff --git a/tmh_registry/registry/tests/api/viewsets/test_follow_ups.py b/tmh_registry/registry/tests/api/viewsets/test_follow_ups.py new file mode 100644 index 0000000..386eda0 --- /dev/null +++ b/tmh_registry/registry/tests/api/viewsets/test_follow_ups.py @@ -0,0 +1,110 @@ +from django.test import TestCase +from rest_framework.authtoken.models import Token +from rest_framework.status import HTTP_201_CREATED, HTTP_400_BAD_REQUEST +from rest_framework.test import APIClient + +from tmh_registry.common.utils.functions import ( + get_text_choice_value_from_label, +) +from tmh_registry.registry.factories import DischargeFactory, EpisodeFactory +from tmh_registry.registry.models import FollowUp +from tmh_registry.users.factories import MedicalPersonnelFactory + + +class TestFollowUpsCreate(TestCase): + @classmethod + def setUpClass(cls) -> None: + super(TestFollowUpsCreate, cls).setUpClass() + + cls.episode = EpisodeFactory(surgery_date="2022-01-23") + cls.discharge = DischargeFactory( + episode=cls.episode, date="2022-02-19" + ) + + cls.medical_personnel = MedicalPersonnelFactory() + cls.token = Token.objects.create(user=cls.medical_personnel.user) + + def setUp(self) -> None: + self.client = APIClient() + self.client.credentials(HTTP_AUTHORIZATION="Token " + self.token.key) + + def get_follow_up_data(self): + return { + "episode_id": self.episode.id, + "date": "2022-02-22", + "attendee_ids": [self.medical_personnel.id], + "pain_severity": "Mild", + "mesh_awareness": False, + "seroma": True, + "infection": False, + "numbness": True, + } + + def test_successful(self): + data = self.get_follow_up_data() + response = self.client.post( + "/api/v1/follow-ups/", data=data, format="json" + ) + + self.assertEqual(HTTP_201_CREATED, response.status_code) + + self.assertEqual(response.data["episode"]["id"], self.episode.id) + self.assertEqual(response.data["date"], data["date"]) + self.assertEqual( + response.data["attendees"][0]["id"], data["attendee_ids"][0] + ) + self.assertEqual( + response.data["pain_severity"], + data["pain_severity"], + ) + self.assertEqual( + response.data["mesh_awareness"], data["mesh_awareness"] + ) + self.assertEqual(response.data["seroma"], data["seroma"]) + self.assertEqual(response.data["infection"], data["infection"]) + self.assertEqual(response.data["numbness"], data["numbness"]) + + # check value stored in db + follow_up = FollowUp.objects.get(id=response.data["id"]) + self.assertEqual( + follow_up.pain_severity, + get_text_choice_value_from_label( + FollowUp.PainSeverityChoices.choices, data["pain_severity"] + ), + ) + + def test_when_episode_id_does_not_exist(self): + data = self.get_follow_up_data() + data["episode_id"] = -1 + response = self.client.post( + "/api/v1/follow-ups/", data=data, format="json" + ) + + self.assertEqual(HTTP_400_BAD_REQUEST, response.status_code) + + def test_when_episode_is_not_discharged_yet(self): + data = self.get_follow_up_data() + data["episode_id"] = EpisodeFactory().id + response = self.client.post( + "/api/v1/follow-ups/", data=data, format="json" + ) + + self.assertEqual(HTTP_400_BAD_REQUEST, response.status_code) + + def test_when_episode_surgery_date_is_after_follow_up_date(self): + data = self.get_follow_up_data() + data["date"] = "2021-12-03" + response = self.client.post( + "/api/v1/follow-ups/", data=data, format="json" + ) + + self.assertEqual(HTTP_400_BAD_REQUEST, response.status_code) + + def test_when_episode_discharge_date_is_after_follow_up_date(self): + data = self.get_follow_up_data() + data["date"] = "2022-02-05" + response = self.client.post( + "/api/v1/follow-ups/", data=data, format="json" + ) + + self.assertEqual(HTTP_400_BAD_REQUEST, response.status_code) diff --git a/tmh_registry/registry/tests/api/viewsets/test_patients.py b/tmh_registry/registry/tests/api/viewsets/test_patients.py index cad1d61..3ed5aa9 100644 --- a/tmh_registry/registry/tests/api/viewsets/test_patients.py +++ b/tmh_registry/registry/tests/api/viewsets/test_patients.py @@ -12,6 +12,7 @@ ) from rest_framework.test import APIClient +from .....common.utils.functions import get_text_choice_value_from_label from .....users.factories import MedicalPersonnelFactory, UserFactory from ....factories import ( EpisodeFactory, @@ -19,7 +20,7 @@ PatientFactory, PatientHospitalMappingFactory, ) -from ....models import PatientHospitalMapping +from ....models import Patient, PatientHospitalMapping @mark.registry @@ -325,6 +326,16 @@ def test_create_patients_successful(self): ).count(), ) + # assert db value + patient = Patient.objects.get(id=response.data["id"]) + + self.assertEqual( + get_text_choice_value_from_label( + Patient.Gender.choices, data["gender"] + ), + patient.gender, + ) + def test_create_patients_only_with_mandatory_fields(self): data = self.get_patient_test_data() for key in data.keys(): diff --git a/tmh_registry/registry/urls.py b/tmh_registry/registry/urls.py index e0c4c34..67a6f44 100644 --- a/tmh_registry/registry/urls.py +++ b/tmh_registry/registry/urls.py @@ -4,6 +4,7 @@ from .api.viewsets import ( DischargeViewset, EpisodeViewset, + FollowUpViewset, HospitalViewSet, PatientHospitalMappingViewset, PatientViewSet, @@ -15,6 +16,7 @@ router.register(r"patient-hospital-mappings", PatientHospitalMappingViewset) router.register(r"episodes", EpisodeViewset) router.register(r"discharges", DischargeViewset) +router.register(r"follow-ups", FollowUpViewset) urlpatterns = [ path("", include(router.urls)), diff --git a/tmh_registry/users/api/serializers.py b/tmh_registry/users/api/serializers.py index 4e0e759..354e946 100644 --- a/tmh_registry/users/api/serializers.py +++ b/tmh_registry/users/api/serializers.py @@ -34,7 +34,7 @@ class MedicalPersonnelSerializer(ModelSerializer): class Meta: model = MedicalPersonnel - fields = ["user", "level"] + fields = ["id", "user", "level"] class SignInSerializer(Serializer):