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

Add Consent Model #2209

Merged
merged 23 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from 15 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
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
98 changes: 97 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 @@ -864,3 +870,93 @@ 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):
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, self_id=None):
consents = PatientConsent.objects.filter(
consultation=consultation, type=type
).exclude(id=self_id)

consents.update(
archived=True,
archived_by=self.context["request"].user,
archived_date=timezone.now(),
)
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=timezone.now(),
archive_reason="Consent Archived",
archived_by=self.context["request"].user,
)

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

def update(self, instance, validated_data):
with transaction.atomic():
self.clear_existing_records(
consultation=instance.consultation,
type=instance.type,
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
)
73 changes: 72 additions & 1 deletion care/facility/api/viewsets/patient_consultation.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,7 +23,10 @@
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,
Expand Down Expand Up @@ -287,3 +291,70 @@ 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(self, consultation_id):
consultation = PatientConsultation.objects.filter(external_id=consultation_id)

consultation.prefetch_related(
"assigned_to",
Prefetch(
"assigned_to__skills",
queryset=Skill.objects.filter(userskill__deleted=False),
),
"current_bed",
"current_bed__bed",
"current_bed__assets",
"current_bed__assets__current_location",
)

if self.request.user.is_superuser:
return consultation.first()
elif self.request.user.user_type >= User.TYPE_VALUE_MAP["StateLabAdmin"]:
return self.queryset.filter(
patient__facility__state=self.request.user.state
)
elif self.request.user.user_type >= User.TYPE_VALUE_MAP["DistrictLabAdmin"]:
return self.queryset.filter(
patient__facility__district=self.request.user.district
)
allowed_facilities = get_accessible_facilities(self.request.user)
# A user should be able to see all the consultations of a patient if the patient is active in an accessible facility
applied_filters = Q(
Q(patient__is_active=True) & Q(patient__facility__id__in=allowed_facilities)
)
# A user should be able to see all consultations part of their home facility
applied_filters |= Q(facility=self.request.user.home_facility)
return consultation.filter(applied_filters).first()

def get_queryset(self):
consultation_id = self.kwargs.get("consultation_external_id", None)
shivankacker marked this conversation as resolved.
Show resolved Hide resolved
if not consultation_id:
raise NotFound({"detail": "Consultation not found"})
return self.queryset.filter(consultation=self.get_consultation(consultation_id))

def perform_create(self, serializer):
consultation_id = self.kwargs.get("consultation_external_id", None)
if not consultation_id:
raise NotFound({"detail": "Consultation not found"})
consultation = self.get_consultation(consultation_id)
serializer.save(consultation=consultation, created_by=self.request.user)
Loading
Loading