Skip to content

Commit

Permalink
Merge pull request #2255 from coronasafe/consent-filters
Browse files Browse the repository at this point in the history
Return files with consent lists and consent filters
  • Loading branch information
vigneshhari authored Jul 13, 2024
2 parents 24d1842 + 5b74301 commit c9caff8
Show file tree
Hide file tree
Showing 6 changed files with 415 additions and 13 deletions.
20 changes: 20 additions & 0 deletions care/facility/api/serializers/patient_consultation.py
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,7 @@ 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)
files = serializers.SerializerMethodField()

class Meta:
model = PatientConsent
Expand All @@ -869,6 +870,7 @@ class Meta:
"id",
"type",
"patient_code_status",
"files",
"archived",
"archived_by",
"archived_date",
Expand All @@ -878,13 +880,31 @@ class Meta:

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

def get_files(self, obj):
from care.facility.api.serializers.file_upload import (
FileUploadListSerializer,
check_permissions,
)

user = self.context["request"].user
file_type = FileUpload.FileType.CONSENT_RECORD
if check_permissions(file_type, obj.external_id, user, "read"):
return FileUploadListSerializer(
FileUpload.objects.filter(
associating_id=obj.external_id, file_type=file_type
),
many=True,
).data
return None

def validate_patient_code_status(self, value):
if value == PatientCodeStatusType.NOT_SPECIFIED:
raise ValidationError(
Expand Down
26 changes: 26 additions & 0 deletions care/facility/api/viewsets/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,32 @@ def filter_by_diagnoses(self, queryset, name, value):
)
return queryset.filter(filter_q)

last_consultation__consent_types = MultiSelectFilter(
method="filter_by_has_consents"
)

def filter_by_has_consents(self, queryset, name, value: str):

if not value:
return queryset

values = value.split(",")

filter_q = Q()

if "None" in values:
filter_q |= ~Q(
last_consultation__has_consents__len__gt=0,
)
values.remove("None")

if values:
filter_q |= Q(
last_consultation__has_consents__overlap=values,
)

return queryset.filter(filter_q)


class PatientDRYFilter(DRYPermissionFiltersBase):
def filter_queryset(self, request, queryset, view):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Generated by Django 4.2.10 on 2024-07-04 16:20

import uuid

import django.contrib.postgres.fields
import django.db.models.deletion
from django.db import migrations, models
from django.db.models import Subquery


class Migration(migrations.Migration):

def migrate_has_consents(apps, schema_editor):
FileUpload = apps.get_model("facility", "FileUpload")
PatientConsent = apps.get_model("facility", "PatientConsent")

consents = PatientConsent.objects.filter(archived=False)
for consent in consents:
consultation = consent.consultation
consent_types = (
PatientConsent.objects.filter(consultation=consultation, archived=False)
.annotate(
str_external_id=models.functions.Cast(
"external_id", models.CharField()
)
)
.annotate(
has_files=models.Exists(
FileUpload.objects.filter(
associating_id=models.OuterRef("str_external_id"),
file_type=7,
is_archived=False,
)
)
)
.filter(has_files=True)
.distinct("type")
.values_list("type", flat=True)
)
consultation.has_consents = list(consent_types)
consultation.save()

dependencies = [
("facility", "0443_remove_patientconsultation_consent_records_and_more"),
]

operations = [
migrations.AddField(
model_name="patientconsultation",
name="has_consents",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.IntegerField(
choices=[
(1, "Consent for Admission"),
(2, "Patient Code Status"),
(3, "Consent for Procedure"),
(4, "High Risk Consent"),
(5, "Others"),
]
),
default=list,
size=None,
),
),
migrations.AlterField(
model_name="patientconsent",
name="consultation",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="consents",
to="facility.patientconsultation",
),
),
migrations.RunPython(
migrate_has_consents, reverse_code=migrations.RunPython.noop
),
]
41 changes: 41 additions & 0 deletions care/facility/models/file_upload.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import time
import uuid
from uuid import uuid4

import boto3
Expand Down Expand Up @@ -163,5 +164,45 @@ class FileType(models.IntegerChoices):
FileTypeChoices = [(x.value, x.name) for x in FileType]
FileCategoryChoices = [(x.value, x.name) for x in BaseFileUpload.FileCategory]

def save(self, *args, **kwargs):
from care.facility.models import PatientConsent

if self.file_type == self.FileType.CONSENT_RECORD:
new_consent = False
if not self.pk and not self.is_archived:
new_consent = True
consent = PatientConsent.objects.filter(
external_id=uuid.UUID(self.associating_id), archived=False
).first()
consultation = consent.consultation
consent_types = (
PatientConsent.objects.filter(consultation=consultation, archived=False)
.annotate(
str_external_id=models.functions.Cast(
"external_id", models.CharField()
)
)
.annotate(
has_files=(
models.Exists(
FileUpload.objects.filter(
associating_id=models.OuterRef("str_external_id"),
file_type=self.FileType.CONSENT_RECORD,
is_archived=False,
).exclude(pk=self.pk if self.is_archived else None)
)
if not new_consent
else models.Value(True)
)
)
.filter(has_files=True)
.distinct("type")
.values_list("type", flat=True)
)
consultation.has_consents = list(consent_types)
consultation.save()

return super().save(*args, **kwargs)

def __str__(self):
return f"{self.FileTypeChoices[self.file_type][1]} - {self.name}{' (Archived)' if self.is_archived else ''}"
25 changes: 16 additions & 9 deletions care/facility/models/patient_consultation.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@
from care.utils.models.base import BaseModel


class ConsentType(models.IntegerChoices):
CONSENT_FOR_ADMISSION = 1, "Consent for Admission"
PATIENT_CODE_STATUS = 2, "Patient Code Status"
CONSENT_FOR_PROCEDURE = 3, "Consent for Procedure"
HIGH_RISK_CONSENT = 4, "High Risk Consent"
OTHERS = 5, "Others"


class PatientConsultation(PatientBaseModel, ConsultationRelatedPermissionMixin):
SUGGESTION_CHOICES = [
(SuggestionChoices.HI, "HOME ISOLATION"),
Expand Down Expand Up @@ -248,6 +256,11 @@ class PatientConsultation(PatientBaseModel, ConsultationRelatedPermissionMixin):
prn_prescription = JSONField(default=dict)
discharge_advice = JSONField(default=dict)

has_consents = ArrayField(
models.IntegerField(choices=ConsentType.choices),
default=list,
)

def get_related_consultation(self):
return self

Expand Down Expand Up @@ -359,14 +372,6 @@ def has_object_generate_discharge_summary_permission(self, request):
return self.has_object_read_permission(request)


class ConsentType(models.IntegerChoices):
CONSENT_FOR_ADMISSION = 1, "Consent for Admission"
PATIENT_CODE_STATUS = 2, "Patient Code Status"
CONSENT_FOR_PROCEDURE = 3, "Consent for Procedure"
HIGH_RISK_CONSENT = 4, "High Risk Consent"
OTHERS = 5, "Others"


class PatientCodeStatusType(models.IntegerChoices):
NOT_SPECIFIED = 0, "Not Specified"
DNH = 1, "Do Not Hospitalize"
Expand All @@ -387,7 +392,9 @@ class ConsultationClinician(models.Model):


class PatientConsent(BaseModel, ConsultationRelatedPermissionMixin):
consultation = models.ForeignKey(PatientConsultation, on_delete=models.CASCADE)
consultation = models.ForeignKey(
PatientConsultation, on_delete=models.CASCADE, related_name="consents"
)
type = models.IntegerField(choices=ConsentType.choices)
patient_code_status = models.IntegerField(
choices=PatientCodeStatusType.choices, null=True, blank=True
Expand Down
Loading

0 comments on commit c9caff8

Please sign in to comment.