Skip to content

Commit

Permalink
Add Consent Model (#2209)
Browse files Browse the repository at this point in the history
* added migrations

* updates

* updates

* v-comparision

* fixing files

* complete?

* added tests

* fixed test

* made changes

* updated migrations and used bulk updates

* Update care/facility/api/serializers/patient_consultation.py

* fixed

* fixed permissions and router

* fixes

* cleanup queryset

* rebase migrations

* allow only home facility users to create or update consent

* fixes

* add is_migrated field

* fix permission

* remove types from migrations

---------

Co-authored-by: Aakash Singh <mail@singhaakash.dev>
  • Loading branch information
shivankacker and sainak authored Jun 6, 2024
1 parent 369b9df commit 1e962ec
Show file tree
Hide file tree
Showing 12 changed files with 693 additions and 40 deletions.
8 changes: 8 additions & 0 deletions care/facility/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
from care.facility.models.ambulance import Ambulance, AmbulanceDriver
from care.facility.models.asset import Asset
from care.facility.models.bed import AssetBed, Bed
from care.facility.models.file_upload import FileUpload
from care.facility.models.patient_consultation import (
PatientConsent,
PatientConsultation,
)
from care.facility.models.patient_sample import PatientSample

from .models import (
Expand Down Expand Up @@ -209,3 +214,6 @@ class FacilityUserAdmin(DjangoQLSearchMixin, admin.ModelAdmin, ExportCsvMixin):
admin.site.register(AssetBed)
admin.site.register(Asset)
admin.site.register(Bed)
admin.site.register(PatientConsent)
admin.site.register(FileUpload)
admin.site.register(PatientConsultation)
12 changes: 8 additions & 4 deletions care/facility/api/serializers/file_upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
from care.facility.models.facility import Facility
from care.facility.models.file_upload import FileUpload
from care.facility.models.patient import PatientRegistration
from care.facility.models.patient_consultation import PatientConsultation
from care.facility.models.patient_consultation import (
PatientConsent,
PatientConsultation,
)
from care.facility.models.patient_sample import PatientSample
from care.users.api.serializers.user import UserBaseMinimumSerializer
from care.users.models import User
Expand Down Expand Up @@ -53,9 +56,9 @@ def check_permissions(file_type, associating_id, user, action="create"):
raise Exception("No Permission")
return consultation.id
elif file_type == FileUpload.FileType.CONSENT_RECORD.value:
consultation = PatientConsultation.objects.get(
consent_records__contains=[{"id": associating_id}]
)
consultation = PatientConsent.objects.get(
external_id=associating_id
).consultation
if consultation.discharge_date and not action == "read":
raise serializers.ValidationError(
{
Expand Down Expand Up @@ -173,6 +176,7 @@ class Meta:
fields = (
"id",
"name",
"associating_id",
"uploaded_by",
"archived_by",
"archived_datetime",
Expand Down
113 changes: 112 additions & 1 deletion care/facility/api/serializers/patient_consultation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from django.conf import settings
from django.db import transaction
from django.utils import timezone
from django.utils.timezone import localtime, make_aware, now
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
Expand Down Expand Up @@ -41,6 +42,7 @@
EncounterSymptom,
Symptom,
)
from care.facility.models.file_upload import FileUpload
from care.facility.models.icd11_diagnosis import (
ConditionVerificationStatus,
ConsultationDiagnosis,
Expand All @@ -51,7 +53,11 @@
RouteToFacility,
SuggestionChoices,
)
from care.facility.models.patient_consultation import PatientConsultation
from care.facility.models.patient_consultation import (
ConsentType,
PatientConsent,
PatientConsultation,
)
from care.users.api.serializers.user import (
UserAssignedSerializer,
UserBaseMinimumSerializer,
Expand Down Expand Up @@ -848,3 +854,108 @@ def validate(self, attrs):
class Meta:
model = PatientConsultation
fields = ("email",)


class PatientConsentSerializer(serializers.ModelSerializer):
id = serializers.CharField(source="external_id", read_only=True)
created_by = UserBaseMinimumSerializer(read_only=True)
archived_by = UserBaseMinimumSerializer(read_only=True)

class Meta:
model = PatientConsent

fields = (
"id",
"type",
"patient_code_status",
"archived",
"archived_by",
"archived_date",
"created_by",
"created_date",
)

read_only_fields = (
"id",
"created_by",
"created_date",
"archived",
"archived_by",
"archived_date",
)

def validate(self, attrs):
user = self.context["request"].user
if (
user.user_type < User.TYPE_VALUE_MAP["DistrictAdmin"]
and self.context["consultation"].facility_id != user.home_facility_id
):
raise ValidationError(
"Only Home Facility Staff can create consent for a Consultation"
)

if attrs.get("type") == ConsentType.PATIENT_CODE_STATUS and not attrs.get(
"patient_code_status"
):
raise ValidationError(
{
"patient_code_status": [
"This field is required for Patient Code Status Consent"
]
}
)

if attrs.get("type") != ConsentType.PATIENT_CODE_STATUS and attrs.get(
"patient_code_status"
):
raise ValidationError(
{
"patient_code_status": [
"This field is not required for this type of Consent"
]
}
)
return attrs

def clear_existing_records(self, consultation, type, user, self_id=None):
consents = PatientConsent.objects.filter(
consultation=consultation, type=type
).exclude(id=self_id)

archived_date = timezone.now()
consents.update(
archived=True,
archived_by=user,
archived_date=archived_date,
)
FileUpload.objects.filter(
associating_id__in=list(consents.values_list("external_id", flat=True)),
file_type=FileUpload.FileType.CONSENT_RECORD,
is_archived=False,
).update(
is_archived=True,
archived_datetime=archived_date,
archive_reason="Consent Archived",
archived_by=user,
)

def create(self, validated_data):
with transaction.atomic():
self.clear_existing_records(
consultation=self.context["consultation"],
type=validated_data["type"],
user=self.context["request"].user,
)
validated_data["consultation"] = self.context["consultation"]
validated_data["created_by"] = self.context["request"].user
return super().create(validated_data)

def update(self, instance, validated_data):
with transaction.atomic():
self.clear_existing_records(
consultation=instance.consultation,
type=instance.type,
user=self.context["request"].user,
self_id=instance.id,
)
return super().update(instance, validated_data)
16 changes: 11 additions & 5 deletions care/facility/api/viewsets/file_upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,19 @@ def get_queryset(self):
{"associating_id": "associating_id missing in request params"}
)
file_type = self.request.GET["file_type"]
associating_id = self.request.GET["associating_id"]
associating_ids = self.request.GET["associating_id"].split(",")
if file_type not in FileUpload.FileType.__members__:
raise ValidationError({"file_type": "invalid file type"})
file_type = FileUpload.FileType[file_type].value
associating_internal_id = check_permissions(
file_type, associating_id, self.request.user, "read"
)

associating_internal_ids = []

for associating_id in associating_ids:
associating_internal_id = check_permissions(
file_type, associating_id, self.request.user, "read"
)
associating_internal_ids.append(associating_internal_id)

return self.queryset.filter(
file_type=file_type, associating_id=associating_internal_id
file_type=file_type, associating_id__in=associating_internal_ids
)
44 changes: 42 additions & 2 deletions care/facility/api/viewsets/patient_consultation.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.db.models import Prefetch
from django.db.models.query_utils import Q
from django.shortcuts import render
from django.shortcuts import get_object_or_404, render
from django_filters import rest_framework as filters
from drf_spectacular.utils import extend_schema
from dry_rest_permissions.generics import DRYPermissions
Expand All @@ -14,6 +14,7 @@
from care.facility.api.serializers.file_upload import FileUploadRetrieveSerializer
from care.facility.api.serializers.patient_consultation import (
EmailDischargeSummarySerializer,
PatientConsentSerializer,
PatientConsultationDischargeSerializer,
PatientConsultationIDSerializer,
PatientConsultationSerializer,
Expand All @@ -22,14 +23,18 @@
from care.facility.models.bed import AssetBed, ConsultationBed
from care.facility.models.file_upload import FileUpload
from care.facility.models.mixins.permissions.asset import IsAssetUser
from care.facility.models.patient_consultation import PatientConsultation
from care.facility.models.patient_consultation import (
PatientConsent,
PatientConsultation,
)
from care.facility.tasks.discharge_summary import (
email_discharge_summary_task,
generate_discharge_summary_task,
)
from care.facility.utils.reports import discharge_summary
from care.users.models import Skill, User
from care.utils.cache.cache_allowed_facilities import get_accessible_facilities
from care.utils.queryset.consultation import get_consultation_queryset


class PatientConsultationFilter(filters.FilterSet):
Expand Down Expand Up @@ -287,3 +292,38 @@ def dev_preview_discharge_summary(request, consultation_id):
raise NotFound({"detail": "Consultation not found"})
data = discharge_summary.get_discharge_summary_data(consultation)
return render(request, "reports/patient_discharge_summary_pdf.html", data)


class PatientConsentViewSet(
AssetUserAccessMixin,
mixins.CreateModelMixin,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
GenericViewSet,
):
lookup_field = "external_id"
serializer_class = PatientConsentSerializer
permission_classes = (
IsAuthenticated,
DRYPermissions,
)
queryset = PatientConsent.objects.all().select_related("consultation")
filter_backends = (filters.DjangoFilterBackend,)

filterset_fields = ("archived",)

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):
return self.queryset.filter(consultation=self.get_consultation_obj())

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

0 comments on commit 1e962ec

Please sign in to comment.