Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
4 changes: 4 additions & 0 deletions tmh_registry/common/utils/functions.py
Original file line number Diff line number Diff line change
@@ -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]
170 changes: 154 additions & 16 deletions tmh_registry/registry/api/serializers.py
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
55 changes: 52 additions & 3 deletions tmh_registry/registry/api/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from ..models import (
Discharge,
Episode,
FollowUp,
Hospital,
Patient,
PatientHospitalMapping,
Expand All @@ -25,6 +26,8 @@
DischargeWriteSerializer,
EpisodeReadSerializer,
EpisodeWriteSerializer,
FollowUpReadSerializer,
FollowUpWriteSerializer,
HospitalSerializer,
PatientHospitalMappingReadSerializer,
PatientHospitalMappingWriteSerializer,
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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()
Expand 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()
Expand 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
28 changes: 28 additions & 0 deletions tmh_registry/registry/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from .models import (
Discharge,
Episode,
FollowUp,
Hospital,
Patient,
PatientHospitalMapping,
Expand Down Expand Up @@ -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())
Loading