Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encounter Symptoms #2095

Closed
wants to merge 19 commits into from
Closed
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
126 changes: 126 additions & 0 deletions care/facility/api/serializers/consultation_symptom.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
from copy import copy

from django.db import transaction
from django.utils.timezone import now
from rest_framework import serializers

from care.facility.events.handler import create_consultation_events
from care.facility.models.consultation_symptom import (
ClinicalImpressionStatus,
ConsultationSymptom,
Symptom,
)
from care.users.api.serializers.user import UserBaseMinimumSerializer


class ConsultationSymptomSerializer(serializers.ModelSerializer):
id = serializers.UUIDField(source="external_id", read_only=True)
created_by = UserBaseMinimumSerializer(read_only=True)
updated_by = UserBaseMinimumSerializer(read_only=True)

class Meta:
model = ConsultationSymptom
exclude = (
"consultation",
"external_id",
"deleted",
)
read_only_fields = (
"created_date",
"modified_date",
"is_migrated",
)

def validate_onset_date(self, value):
if value and value > now():
raise serializers.ValidationError("Onset date cannot be in the future")
return value

def validate(self, attrs):
validated_data = super().validate(attrs)
consultation = (
self.instance.consultation
if self.instance
else self.context["consultation"]
)

onset_date = (
self.instance.onset_date
if self.instance
else validated_data.get("onset_date")
)
if cure_date := validated_data.get("cure_date"):
if cure_date < onset_date:
raise serializers.ValidationError(
{"cure_date": "Cure date should be after onset date"}
)

if validated_data.get("symptom") != Symptom.OTHERS and validated_data.get(
"other_symptom"
):
raise serializers.ValidationError(
{
"other_symptom": "Other symptom should be empty when symptom is not OTHERS"
}
)

if validated_data.get("symptom") == Symptom.OTHERS and not validated_data.get(
"other_symptom"
):
raise serializers.ValidationError(
{
"other_symptom": "Other symptom should not be empty when symptom is OTHERS"
}
)

if ConsultationSymptom.objects.filter(
consultation=consultation,
symptom=validated_data.get("symptom"),
other_symptom=validated_data.get("other_symptom") or "",
cure_date__isnull=True,
clinical_impression_status=ClinicalImpressionStatus.IN_PROGRESS,
).exists():
raise serializers.ValidationError(
{"symptom": "An active symptom with the same details already exists"}
)

return validated_data

def create(self, validated_data):
validated_data["consultation"] = self.context["consultation"]
validated_data["created_by"] = self.context["request"].user

with transaction.atomic():
instance: ConsultationSymptom = super().create(validated_data)

create_consultation_events(
instance.consultation_id,
instance,
instance.created_by_id,
instance.created_date,
)

return instance

def update(self, instance, validated_data):
validated_data["updated_by"] = self.context["request"].user

with transaction.atomic():
old_instance = copy(instance)
instance = super().update(instance, validated_data)

create_consultation_events(
instance.consultation_id,
instance,
instance.updated_by_id,
instance.modified_date,
old=old_instance,
)

return instance


class ConsultationCreateSymptomSerializer(serializers.ModelSerializer):
class Meta:
model = ConsultationSymptom
fields = ("symptom", "other_symptom", "onset_date")
5 changes: 1 addition & 4 deletions care/facility/api/serializers/daily_round.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from care.facility.models.bed import Bed
from care.facility.models.daily_round import DailyRound
from care.facility.models.notification import Notification
from care.facility.models.patient_base import SYMPTOM_CHOICES, SuggestionChoices
from care.facility.models.patient_base import SuggestionChoices
from care.facility.models.patient_consultation import PatientConsultation
from care.users.api.serializers.user import UserBaseMinimumSerializer
from care.utils.notification_handler import NotificationGenerator
Expand All @@ -24,9 +24,6 @@

class DailyRoundSerializer(serializers.ModelSerializer):
id = serializers.CharField(source="external_id", read_only=True)
additional_symptoms = serializers.MultipleChoiceField(
choices=SYMPTOM_CHOICES, required=False
)
deprecated_covid_category = ChoiceField(
choices=COVID_CATEGORY_CHOICES, required=False
) # Deprecated
Expand Down
78 changes: 74 additions & 4 deletions care/facility/api/serializers/patient_consultation.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
ConsultationCreateDiagnosisSerializer,
ConsultationDiagnosisSerializer,
)
from care.facility.api.serializers.consultation_symptom import (
ConsultationCreateSymptomSerializer,
ConsultationSymptomSerializer,
)
from care.facility.api.serializers.daily_round import DailyRoundSerializer
from care.facility.api.serializers.facility import FacilityBasicInfoSerializer
from care.facility.events.handler import create_consultation_events
Expand All @@ -32,13 +36,17 @@
)
from care.facility.models.asset import AssetLocation
from care.facility.models.bed import Bed, ConsultationBed
from care.facility.models.consultation_symptom import (
ClinicalImpressionStatus,
ConsultationSymptom,
Symptom,
)
from care.facility.models.icd11_diagnosis import (
ConditionVerificationStatus,
ConsultationDiagnosis,
)
from care.facility.models.notification import Notification
from care.facility.models.patient_base import (
SYMPTOM_CHOICES,
NewDischargeReasonEnum,
RouteToFacility,
SuggestionChoices,
Expand Down Expand Up @@ -66,7 +74,6 @@ class PatientConsultationSerializer(serializers.ModelSerializer):
source="suggestion",
)

symptoms = serializers.MultipleChoiceField(choices=SYMPTOM_CHOICES)
deprecated_covid_category = ChoiceField(
choices=COVID_CATEGORY_CHOICES, required=False
)
Expand Down Expand Up @@ -151,7 +158,13 @@ class PatientConsultationSerializer(serializers.ModelSerializer):
help_text="Bulk create diagnoses for the consultation upon creation",
)
diagnoses = ConsultationDiagnosisSerializer(many=True, read_only=True)

create_symptoms = ConsultationCreateSymptomSerializer(
many=True,
write_only=True,
required=False,
help_text="Bulk create symptoms for the consultation upon creation",
)
symptoms = ConsultationSymptomSerializer(many=True, read_only=True)
medico_legal_case = serializers.BooleanField(default=False, required=False)

def get_discharge_prescription(self, consultation):
Expand Down Expand Up @@ -332,6 +345,7 @@ def create(self, validated_data):
raise ValidationError({"route_to_facility": "This field is required"})

create_diagnosis = validated_data.pop("create_diagnoses")
create_symptoms = validated_data.pop("create_symptoms")
action = -1
review_interval = -1
if "action" in validated_data:
Expand Down Expand Up @@ -407,6 +421,19 @@ def create(self, validated_data):
]
)

symptoms = ConsultationSymptom.objects.bulk_create(
ConsultationSymptom(
consultation=consultation,
symptom=obj.get("symptom"),
onset_date=obj.get("onset_date"),
cure_date=obj.get("cure_date"),
clinical_impression_status=obj.get("clinical_impression_status"),
other_symptom=obj.get("other_symptom") or "",
created_by=self.context["request"].user,
)
for obj in create_symptoms
)

if bed and consultation.suggestion == SuggestionChoices.A:
consultation_bed = ConsultationBed(
bed=bed,
Expand Down Expand Up @@ -444,7 +471,7 @@ def create(self, validated_data):

create_consultation_events(
consultation.id,
(consultation, *diagnosis),
(consultation, *diagnosis, *symptoms),
consultation.created_by.id,
consultation.created_date,
)
Expand Down Expand Up @@ -502,6 +529,46 @@ def validate_create_diagnoses(self, value):

return value

def validate_create_symptoms(self, value):
if self.instance:
raise ValidationError("Bulk create symptoms is not allowed on update")

if len(value) == 0:
raise ValidationError("Atleast one symptom is required")

counter: set[int | str] = set()
for obj in value:
item: int | str = obj["symptom"]
if obj["symptom"] == Symptom.OTHERS:
item: str = obj["other_symptom"].strip().lower()
if item in counter:
# Reject if duplicate symptoms are provided
raise ValidationError("Duplicate symptoms are not allowed")
counter.add(item)

current_time = now()
for obj in value:
if not obj.get("onset_date"):
raise ValidationError(
{"onset_date": "This field is required for all symptoms"}
)

if obj["onset_date"] > current_time:
raise ValidationError(
{"onset_date": "Onset date cannot be in the future"}
)

if cure_date := obj.get("cure_date"):
if cure_date < obj["onset_date"]:
raise ValidationError(
{"cure_date": "Cure date should be after onset date"}
)
obj["clinical_impression_status"] = ClinicalImpressionStatus.COMPLETED
else:
obj["clinical_impression_status"] = ClinicalImpressionStatus.IN_PROGRESS
sainak marked this conversation as resolved.
Show resolved Hide resolved

return value

def validate_encounter_date(self, value):
if value < MIN_ENCOUNTER_DATE:
raise ValidationError(
Expand Down Expand Up @@ -623,6 +690,9 @@ def validate(self, attrs):
if not self.instance and "create_diagnoses" not in validated:
raise ValidationError({"create_diagnoses": ["This field is required."]})

if not self.instance and "create_symptoms" not in validated:
raise ValidationError({"create_symptoms": ["This field is required."]})

return validated


Expand Down
4 changes: 2 additions & 2 deletions care/facility/api/serializers/patient_icmr.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from rest_framework import serializers

from care.facility.models import DISEASE_CHOICES, SAMPLE_TYPE_CHOICES, SYMPTOM_CHOICES
from care.facility.models import DISEASE_CHOICES, SAMPLE_TYPE_CHOICES
from care.facility.models.patient_icmr import (
PatientConsultationICMR,
PatientIcmr,
Expand Down Expand Up @@ -124,7 +124,7 @@ class Meta:

class ICMRMedicalConditionSerializer(serializers.ModelSerializer):
date_of_onset_of_symptoms = serializers.DateField()
symptoms = serializers.ListSerializer(child=ChoiceField(choices=SYMPTOM_CHOICES))
symptoms = serializers.ListSerializer(child=serializers.CharField())
hospitalization_date = serializers.DateField()
hospital_phone_number = serializers.CharField(
source="consultation.facility.phone_number"
Expand Down
59 changes: 59 additions & 0 deletions care/facility/api/viewsets/consultation_symptoms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from django.shortcuts import get_object_or_404
from django_filters import rest_framework as filters
from dry_rest_permissions.generics import DRYPermissions
from rest_framework.permissions import IsAuthenticated
from rest_framework.viewsets import ModelViewSet

from care.facility.api.serializers.consultation_symptom import (
ConsultationSymptomSerializer,
)
from care.facility.models.consultation_symptom import (
ClinicalImpressionStatus,
ConsultationSymptom,
)
from care.utils.queryset.consultation import get_consultation_queryset


class ConsultationSymptomFilter(filters.FilterSet):
is_cured = filters.BooleanFilter(method="filter_is_cured")

def filter_is_cured(self, queryset, name, value):
if value:
return queryset.filter(cure_date__isnull=False)
return queryset.filter(cure_date__isnull=True)


class ConsultationSymptomViewSet(ModelViewSet):
serializer_class = ConsultationSymptomSerializer
permission_classes = (IsAuthenticated, DRYPermissions)
queryset = ConsultationSymptom.objects.all()
filter_backends = (filters.DjangoFilterBackend,)
filterset_class = ConsultationSymptomFilter
lookup_field = "external_id"

def get_consultation_obj(self):
return get_object_or_404(
get_consultation_queryset(self.request.user).filter(
external_id=self.kwargs["consultation_external_id"]
)
)

def get_queryset(self):
consultation = self.get_consultation_obj()
return self.queryset.filter(consultation_id=consultation.id)

def get_serializer_context(self):
context = super().get_serializer_context()
context["consultation"] = self.get_consultation_obj()
return context

def perform_destroy(self, instance):
serializer = self.get_serializer(
instance,
data={
"clinical_impression_status": ClinicalImpressionStatus.ENTERED_IN_ERROR
},
partial=True,
)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
4 changes: 0 additions & 4 deletions care/facility/api/viewsets/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,6 @@ def filter_by_category(self, queryset, name, value):
last_consultation_discharge_date = filters.DateFromToRangeFilter(
field_name="last_consultation__discharge_date"
)
last_consultation_symptoms_onset_date = filters.DateFromToRangeFilter(
field_name="last_consultation__symptoms_onset_date"
)
last_consultation_admitted_bed_type_list = MultiSelectFilter(
method="filter_by_bed_type",
)
Expand Down Expand Up @@ -449,7 +446,6 @@ class PatientViewSet(
"last_vaccinated_date",
"last_consultation_encounter_date",
"last_consultation_discharge_date",
"last_consultation_symptoms_onset_date",
]
CSV_EXPORT_LIMIT = 7

Expand Down
Loading
Loading