From 2ca0c3c04cd7bdefc3db2a1a2250cdc99a2ac3b6 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Thu, 30 May 2024 12:19:05 +0530 Subject: [PATCH 01/21] added migrations --- care/facility/admin.py | 2 + .../api/serializers/patient_consultation.py | 44 +++++- .../api/viewsets/patient_consultation.py | 24 ++- ...ntconsultation_consent_records_and_more.py | 140 ++++++++++++++++++ care/facility/models/patient_consultation.py | 49 +++++- 5 files changed, 251 insertions(+), 8 deletions(-) create mode 100644 care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py diff --git a/care/facility/admin.py b/care/facility/admin.py index db0ac6a173..31b95bee65 100644 --- a/care/facility/admin.py +++ b/care/facility/admin.py @@ -6,6 +6,7 @@ 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.patient_consultation import PatientConsent from care.facility.models.patient_sample import PatientSample from care.facility.models.patient_tele_consultation import PatientTeleConsultation @@ -211,3 +212,4 @@ class FacilityUserAdmin(DjangoQLSearchMixin, admin.ModelAdmin, ExportCsvMixin): admin.site.register(AssetBed) admin.site.register(Asset) admin.site.register(Bed) +admin.site.register(PatientConsent) diff --git a/care/facility/api/serializers/patient_consultation.py b/care/facility/api/serializers/patient_consultation.py index e08a80ea78..bc977667c7 100644 --- a/care/facility/api/serializers/patient_consultation.py +++ b/care/facility/api/serializers/patient_consultation.py @@ -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 @@ -51,7 +52,10 @@ RouteToFacility, SuggestionChoices, ) -from care.facility.models.patient_consultation import PatientConsultation +from care.facility.models.patient_consultation import ( + PatientConsent, + PatientConsultation, +) from care.users.api.serializers.user import ( UserAssignedSerializer, UserBaseMinimumSerializer, @@ -864,3 +868,41 @@ 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) + + class Meta: + model = PatientConsent + + fields = ( + "id", + "type", + "patient_code_status", + "archived", + "created_by", + "created_date", + ) + + read_only_fields = ( + "id", + "created_by", + "created_date", + ) + + def update(self, instance, validated_data): + # check if the consent has been archived, if so, cascade archive all files + if instance.archived: + for file in instance.files.all(): + file.is_archived = True + file.archive_reason = instance.archived_reason + file.archive_datetime = timezone.now() + file.save() + + return super().update(instance, validated_data) + + def create(self, validated_data): + validated_data["created_by"] = self.context["request"].user + return super().create(validated_data) diff --git a/care/facility/api/viewsets/patient_consultation.py b/care/facility/api/viewsets/patient_consultation.py index 4a31f6354e..463dd240bd 100644 --- a/care/facility/api/viewsets/patient_consultation.py +++ b/care/facility/api/viewsets/patient_consultation.py @@ -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, @@ -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, @@ -287,3 +291,21 @@ 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,) diff --git a/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py b/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py new file mode 100644 index 0000000000..6c68ee08df --- /dev/null +++ b/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py @@ -0,0 +1,140 @@ +# Generated by Django 4.2.10 on 2024-05-30 06:33 + +import uuid + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + def migrate_consents(apps, schema_editor): + PatientConsultation = apps.get_model("facility", "PatientConsultation") + PatientConsent = apps.get_model("facility", "PatientConsent") + FileUpload = apps.get_model("facility", "FileUpload") + consultations = PatientConsultation.objects.filter( + consent_records__isnull=False + ) + for consultation in consultations: + for consent in consultation.consent_records: + new_consent = PatientConsent.objects.create( + consultation=consultation, + type=consent["type"], + patient_code_status=consent["patient_code_status"], + created_by=consultation.created_by, + archived=consent["deleted"], + ) + + old_id = consent.get("id") + + files = FileUpload.objects.filter( + associating_id=old_id, file_type=FileUpload.FileType.CONSENT_RECORD + ) + + files.bulk_update(consent, {"associating_id": new_consent.external_id}) + + new_consent.files.set(files) + + def reverse_migrate(apps, schema_editor): + PatientConsent = apps.get_model("facility", "PatientConsent") + for consent in PatientConsent.objects.all(): + consultation = consent.consultation + consultation.consent_records.append( + { + "type": consent.type, + "patient_code_status": consent.patient_code_status, + "deleted": consent.archived, + "id": consent.external_id, + } + ) + consultation.save() + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("facility", "0440_merge_20240528_1613"), + ] + + operations = [ + migrations.CreateModel( + name="PatientConsent", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "external_id", + models.UUIDField(db_index=True, default=uuid.uuid4, unique=True), + ), + ( + "created_date", + models.DateTimeField(auto_now_add=True, db_index=True, null=True), + ), + ( + "modified_date", + models.DateTimeField(auto_now=True, db_index=True, null=True), + ), + ("deleted", models.BooleanField(db_index=True, default=False)), + ( + "type", + models.IntegerField( + choices=[ + (1, "Consent For Admission"), + (2, "Patient Code Status"), + (3, "Consent For Procedure"), + (4, "High Risk Consent"), + (5, "Others"), + ] + ), + ), + ( + "patient_code_status", + models.IntegerField( + blank=True, + choices=[ + (1, "Dnh"), + (2, "Dnr"), + (3, "Comfort Care"), + (4, "Active Treatment"), + ], + null=True, + ), + ), + ("archived", models.BooleanField(default=False)), + ( + "consultation", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="facility.patientconsultation", + ), + ), + ( + "created_by", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="created_consents", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "files", + models.ManyToManyField( + related_name="consents", to="facility.fileupload" + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.RunPython(migrate_consents, reverse_code=reverse_migrate), + migrations.RemoveField( + model_name="patientconsultation", + name="consent_records", + ), + ] diff --git a/care/facility/models/patient_consultation.py b/care/facility/models/patient_consultation.py index 1e787d7885..3c2b83f170 100644 --- a/care/facility/models/patient_consultation.py +++ b/care/facility/models/patient_consultation.py @@ -11,7 +11,7 @@ COVID_CATEGORY_CHOICES, PatientBaseModel, ) -from care.facility.models.json_schema.consultation import CONSENT_RECORDS +from care.facility.models.file_upload import FileUpload from care.facility.models.mixins.permissions.patient import ( ConsultationRelatedPermissionMixin, ) @@ -26,7 +26,7 @@ reverse_choices, ) from care.users.models import User -from care.utils.models.validators import JSONFieldSchemaValidator +from care.utils.models.base import BaseModel class PatientConsultation(PatientBaseModel, ConsultationRelatedPermissionMixin): @@ -248,10 +248,6 @@ class PatientConsultation(PatientBaseModel, ConsultationRelatedPermissionMixin): prn_prescription = JSONField(default=dict) discharge_advice = JSONField(default=dict) - consent_records = JSONField( - default=list, validators=[JSONFieldSchemaValidator(CONSENT_RECORDS)] - ) - def get_related_consultation(self): return self @@ -368,6 +364,21 @@ def has_object_generate_discharge_summary_permission(self, request): return self.has_object_read_permission(request) +class ConsentType(models.IntegerChoices): + CONSENT_FOR_ADMISSION = (1,) + PATIENT_CODE_STATUS = (2,) + CONSENT_FOR_PROCEDURE = (3,) + HIGH_RISK_CONSENT = (4,) + OTHERS = 5 + + +class PatientCodeStatusType(models.IntegerChoices): + DNH = (1,) + DNR = (2,) + COMFORT_CARE = (3,) + ACTIVE_TREATMENT = 4 + + class ConsultationClinician(models.Model): consultation = models.ForeignKey( PatientConsultation, @@ -377,3 +388,29 @@ class ConsultationClinician(models.Model): User, on_delete=models.PROTECT, ) + + +class PatientConsent(BaseModel): + consultation = models.ForeignKey(PatientConsultation, on_delete=models.CASCADE) + type = models.IntegerField(choices=ConsentType.choices) + patient_code_status = models.IntegerField( + choices=PatientCodeStatusType.choices, null=True, blank=True + ) + archived = models.BooleanField(default=False) + files = models.ManyToManyField(FileUpload, related_name="consents") + created_by = models.ForeignKey( + User, on_delete=models.PROTECT, related_name="created_consents" + ) + + def save(self, *args, **kwargs): + # archive existing patient code status consent + if self.pk is None and self.type == ConsentType.PATIENT_CODE_STATUS: + existing_pcs = PatientConsent.objects.filter( + consultation=self.consultation, + type=ConsentType.PATIENT_CODE_STATUS, + archived=False, + ) + if existing_pcs.exists(): + existing_pcs.update(archived=True) + + super().save(*args, **kwargs) From 41fd601d2c47903becdc90096705cb19e1f900a4 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Thu, 30 May 2024 12:42:54 +0530 Subject: [PATCH 02/21] updates --- care/facility/admin.py | 2 + ...ntconsultation_consent_records_and_more.py | 47 +++++++++++++++---- care/facility/models/file_upload.py | 3 ++ care/facility/models/patient_consultation.py | 21 +++++---- 4 files changed, 55 insertions(+), 18 deletions(-) diff --git a/care/facility/admin.py b/care/facility/admin.py index 31b95bee65..3d474b167c 100644 --- a/care/facility/admin.py +++ b/care/facility/admin.py @@ -6,6 +6,7 @@ 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 from care.facility.models.patient_sample import PatientSample from care.facility.models.patient_tele_consultation import PatientTeleConsultation @@ -213,3 +214,4 @@ class FacilityUserAdmin(DjangoQLSearchMixin, admin.ModelAdmin, ExportCsvMixin): admin.site.register(Asset) admin.site.register(Bed) admin.site.register(PatientConsent) +admin.site.register(FileUpload) diff --git a/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py b/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py index 6c68ee08df..b3ad92b32a 100644 --- a/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py +++ b/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py @@ -5,13 +5,26 @@ import django.db.models.deletion from django.conf import settings from django.db import migrations, models +from django.utils import timezone + +from care.facility.models.file_upload import FileUpload as FileUploadModel +from care.facility.models.patient_consultation import ( + PatientConsent as PatientConsentModel, +) +from care.facility.models.patient_consultation import ( + PatientConsultation as PatientConsultationModel, +) class Migration(migrations.Migration): def migrate_consents(apps, schema_editor): - PatientConsultation = apps.get_model("facility", "PatientConsultation") - PatientConsent = apps.get_model("facility", "PatientConsent") - FileUpload = apps.get_model("facility", "FileUpload") + PatientConsultation: PatientConsultationModel = apps.get_model( + "facility", "PatientConsultation" + ) + PatientConsent: PatientConsentModel = apps.get_model( + "facility", "PatientConsent" + ) + FileUpload: FileUploadModel = apps.get_model("facility", "FileUpload") consultations = PatientConsultation.objects.filter( consent_records__isnull=False ) @@ -20,23 +33,39 @@ def migrate_consents(apps, schema_editor): new_consent = PatientConsent.objects.create( consultation=consultation, type=consent["type"], - patient_code_status=consent["patient_code_status"], + patient_code_status=consent.get("patient_code_status", None), created_by=consultation.created_by, - archived=consent["deleted"], + archived=consent.get("deleted", False), ) old_id = consent.get("id") files = FileUpload.objects.filter( - associating_id=old_id, file_type=FileUpload.FileType.CONSENT_RECORD + associating_id=old_id, + file_type=FileUploadModel.FileType.CONSENT_RECORD, ) - files.bulk_update(consent, {"associating_id": new_consent.external_id}) + kwargs = { + "associating_id": new_consent.external_id, + } + + if consent.get("deleted", False): + kwargs = { + **kwargs, + "is_archived": True, + "archived_datetime": timezone.now(), + "archive_reason": "Consent Record Archived", + "archived_by": consultation.created_by, + } + + files.update(**kwargs) new_consent.files.set(files) def reverse_migrate(apps, schema_editor): - PatientConsent = apps.get_model("facility", "PatientConsent") + PatientConsent: PatientConsentModel = apps.get_model( + "facility", "PatientConsent" + ) for consent in PatientConsent.objects.all(): consultation = consent.consultation consultation.consent_records.append( @@ -44,7 +73,7 @@ def reverse_migrate(apps, schema_editor): "type": consent.type, "patient_code_status": consent.patient_code_status, "deleted": consent.archived, - "id": consent.external_id, + "id": str(consent.external_id), } ) consultation.save() diff --git a/care/facility/models/file_upload.py b/care/facility/models/file_upload.py index 51bce92d96..c09f3814c3 100644 --- a/care/facility/models/file_upload.py +++ b/care/facility/models/file_upload.py @@ -162,3 +162,6 @@ class FileType(models.IntegerChoices): # TODO: switch to Choices.choices FileTypeChoices = [(x.value, x.name) for x in FileType] FileCategoryChoices = [(x.value, x.name) for x in BaseFileUpload.FileCategory] + + def __str__(self): + return f"{self.FileTypeChoices[self.file_type][1]} - {self.name}" diff --git a/care/facility/models/patient_consultation.py b/care/facility/models/patient_consultation.py index 3c2b83f170..bd6e86de52 100644 --- a/care/facility/models/patient_consultation.py +++ b/care/facility/models/patient_consultation.py @@ -365,18 +365,18 @@ def has_object_generate_discharge_summary_permission(self, request): class ConsentType(models.IntegerChoices): - CONSENT_FOR_ADMISSION = (1,) - PATIENT_CODE_STATUS = (2,) - CONSENT_FOR_PROCEDURE = (3,) - HIGH_RISK_CONSENT = (4,) - OTHERS = 5 + 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): - DNH = (1,) - DNR = (2,) - COMFORT_CARE = (3,) - ACTIVE_TREATMENT = 4 + DNH = 1, "Do Not Hospitalize" + DNR = 2, "Do Not Resuscitate" + COMFORT_CARE = 3, "Comfort Care Only" + ACTIVE_TREATMENT = 4, "Active Treatment" class ConsultationClinician(models.Model): @@ -402,6 +402,9 @@ class PatientConsent(BaseModel): User, on_delete=models.PROTECT, related_name="created_consents" ) + def __str__(self) -> str: + return f"{self.consultation.patient.name} - {ConsentType(self.type).label}" + def save(self, *args, **kwargs): # archive existing patient code status consent if self.pk is None and self.type == ConsentType.PATIENT_CODE_STATUS: From 000e941d57706d0d46c6b56e54fd8cd9aba9a16d Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Thu, 30 May 2024 12:48:26 +0530 Subject: [PATCH 03/21] updates --- ...ove_patientconsultation_consent_records_and_more.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py b/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py index b3ad92b32a..24fda30886 100644 --- a/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py +++ b/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py @@ -68,12 +68,20 @@ def reverse_migrate(apps, schema_editor): ) for consent in PatientConsent.objects.all(): consultation = consent.consultation + + kwargs = {} + + if consent.patient_code_status: + kwargs = { + "patient_code_status": consent.patient_code_status, + } + consultation.consent_records.append( { "type": consent.type, - "patient_code_status": consent.patient_code_status, "deleted": consent.archived, "id": str(consent.external_id), + **kwargs, } ) consultation.save() From 351d1de74dc2d4f41b8a5a9174b2320c2d0e298f Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Thu, 30 May 2024 12:49:39 +0530 Subject: [PATCH 04/21] v-comparision --- .../0441_remove_patientconsultation_consent_records_and_more.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py b/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py index 24fda30886..03b685153e 100644 --- a/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py +++ b/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py @@ -71,7 +71,7 @@ def reverse_migrate(apps, schema_editor): kwargs = {} - if consent.patient_code_status: + if type(consent.patient_code_status) == int: kwargs = { "patient_code_status": consent.patient_code_status, } From eb7c358e1ef4408818c5b8e0c3751207c7b8cdc0 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Thu, 30 May 2024 13:04:50 +0530 Subject: [PATCH 05/21] fixing files --- care/facility/admin.py | 6 +++++- ...1_remove_patientconsultation_consent_records_and_more.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/care/facility/admin.py b/care/facility/admin.py index 3d474b167c..75d7c91aca 100644 --- a/care/facility/admin.py +++ b/care/facility/admin.py @@ -7,7 +7,10 @@ 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 +from care.facility.models.patient_consultation import ( + PatientConsent, + PatientConsultation, +) from care.facility.models.patient_sample import PatientSample from care.facility.models.patient_tele_consultation import PatientTeleConsultation @@ -215,3 +218,4 @@ class FacilityUserAdmin(DjangoQLSearchMixin, admin.ModelAdmin, ExportCsvMixin): admin.site.register(Bed) admin.site.register(PatientConsent) admin.site.register(FileUpload) +admin.site.register(PatientConsultation) diff --git a/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py b/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py index 03b685153e..6cca0a333e 100644 --- a/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py +++ b/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py @@ -60,7 +60,7 @@ def migrate_consents(apps, schema_editor): files.update(**kwargs) - new_consent.files.set(files) + new_consent.files.add(*files) def reverse_migrate(apps, schema_editor): PatientConsent: PatientConsentModel = apps.get_model( From 0724813bcdcec8ab4fc30b9bfc0e0d24e9e430e4 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Fri, 31 May 2024 00:35:59 +0530 Subject: [PATCH 06/21] complete? --- care/facility/api/serializers/file_upload.py | 12 ++- .../api/serializers/patient_consultation.py | 39 ++++++--- care/facility/api/viewsets/file_upload.py | 16 ++-- .../api/viewsets/patient_consultation.py | 11 +++ ...ntconsultation_consent_records_and_more.py | 47 +++++++---- care/facility/models/file_upload.py | 2 +- care/facility/models/patient_consultation.py | 82 ++++++++++++++++--- config/api_router.py | 6 +- 8 files changed, 164 insertions(+), 51 deletions(-) diff --git a/care/facility/api/serializers/file_upload.py b/care/facility/api/serializers/file_upload.py index e991cf045a..399d7862ce 100644 --- a/care/facility/api/serializers/file_upload.py +++ b/care/facility/api/serializers/file_upload.py @@ -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 @@ -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( { @@ -173,6 +176,7 @@ class Meta: fields = ( "id", "name", + "associating_id", "uploaded_by", "archived_by", "archived_datetime", diff --git a/care/facility/api/serializers/patient_consultation.py b/care/facility/api/serializers/patient_consultation.py index bc977667c7..216e22c8a4 100644 --- a/care/facility/api/serializers/patient_consultation.py +++ b/care/facility/api/serializers/patient_consultation.py @@ -873,6 +873,7 @@ class Meta: 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 @@ -882,6 +883,8 @@ class Meta: "type", "patient_code_status", "archived", + "archived_by", + "archived_date", "created_by", "created_date", ) @@ -890,19 +893,31 @@ class Meta: "id", "created_by", "created_date", + "archived", + "archived_by", + "archived_date", ) - def update(self, instance, validated_data): - # check if the consent has been archived, if so, cascade archive all files - if instance.archived: - for file in instance.files.all(): - file.is_archived = True - file.archive_reason = instance.archived_reason - file.archive_datetime = timezone.now() - file.save() + def clear_existing_records(self, consultation, type, self_id=None): + consents = PatientConsent.objects.filter( + consultation=consultation, type=type + ).exclude(id=self_id) + + # looping because .update does not call model save method + for consent in consents: + consent.archived = True + consent.archived_by = self.context["request"].user + consent.archived_date = timezone.now() + consent.save() - return super().update(instance, validated_data) + def create(self, validated_data): + self.clear_existing_records( + consultation=validated_data["consultation"], type=validated_data["type"] + ) + return super().create(validated_data) - def create(self, validated_data): - validated_data["created_by"] = self.context["request"].user - return super().create(validated_data) + def update(self, instance, validated_data): + self.clear_existing_records( + consultation=instance.consultation, type=instance.type, self_id=instance.id + ) + return super().update(instance, validated_data) diff --git a/care/facility/api/viewsets/file_upload.py b/care/facility/api/viewsets/file_upload.py index 666eac03c4..2f9ad882c5 100644 --- a/care/facility/api/viewsets/file_upload.py +++ b/care/facility/api/viewsets/file_upload.py @@ -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 ) diff --git a/care/facility/api/viewsets/patient_consultation.py b/care/facility/api/viewsets/patient_consultation.py index 463dd240bd..6f7fdc61b1 100644 --- a/care/facility/api/viewsets/patient_consultation.py +++ b/care/facility/api/viewsets/patient_consultation.py @@ -309,3 +309,14 @@ class PatientConsentViewSet( ) queryset = PatientConsent.objects.all().select_related("consultation") filter_backends = (filters.DjangoFilterBackend,) + + filterset_fields = ("archived",) + + def get_queryset(self): + consultation_id = self.kwargs.get("consultation_external_id", None) + return self.queryset.filter(consultation__external_id=consultation_id) + + def perform_create(self, serializer): + consultation_id = self.kwargs.get("consultation_external_id", None) + consultation = PatientConsultation.objects.get(external_id=consultation_id) + serializer.save(consultation=consultation, created_by=self.request.user) diff --git a/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py b/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py index 6cca0a333e..bdaddb3b51 100644 --- a/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py +++ b/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.10 on 2024-05-30 06:33 +# Generated by Django 4.2.10 on 2024-05-30 16:35 import uuid @@ -7,6 +7,7 @@ from django.db import migrations, models from django.utils import timezone +import care.facility.models.mixins.permissions.patient from care.facility.models.file_upload import FileUpload as FileUploadModel from care.facility.models.patient_consultation import ( PatientConsent as PatientConsentModel, @@ -60,8 +61,6 @@ def migrate_consents(apps, schema_editor): files.update(**kwargs) - new_consent.files.add(*files) - def reverse_migrate(apps, schema_editor): PatientConsent: PatientConsentModel = apps.get_model( "facility", "PatientConsent" @@ -121,9 +120,9 @@ def reverse_migrate(apps, schema_editor): "type", models.IntegerField( choices=[ - (1, "Consent For Admission"), + (1, "Consent for Admission"), (2, "Patient Code Status"), - (3, "Consent For Procedure"), + (3, "Consent for Procedure"), (4, "High Risk Consent"), (5, "Others"), ] @@ -134,15 +133,26 @@ def reverse_migrate(apps, schema_editor): models.IntegerField( blank=True, choices=[ - (1, "Dnh"), - (2, "Dnr"), - (3, "Comfort Care"), + (1, "Do Not Hospitalize"), + (2, "Do Not Resuscitate"), + (3, "Comfort Care Only"), (4, "Active Treatment"), ], null=True, ), ), ("archived", models.BooleanField(default=False)), + ("archived_date", models.DateTimeField(blank=True, null=True)), + ( + "archived_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="archived_consents", + to=settings.AUTH_USER_MODEL, + ), + ), ( "consultation", models.ForeignKey( @@ -158,16 +168,19 @@ def reverse_migrate(apps, schema_editor): to=settings.AUTH_USER_MODEL, ), ), - ( - "files", - models.ManyToManyField( - related_name="consents", to="facility.fileupload" - ), - ), ], - options={ - "abstract": False, - }, + bases=( + models.Model, + care.facility.models.mixins.permissions.patient.ConsultationRelatedPermissionMixin, + ), + ), + migrations.AddConstraint( + model_name="patientconsent", + constraint=models.UniqueConstraint( + condition=models.Q(("archived", False)), + fields=("consultation", "type"), + name="unique_consultation_consent", + ), ), migrations.RunPython(migrate_consents, reverse_code=reverse_migrate), migrations.RemoveField( diff --git a/care/facility/models/file_upload.py b/care/facility/models/file_upload.py index c09f3814c3..5ac205f82c 100644 --- a/care/facility/models/file_upload.py +++ b/care/facility/models/file_upload.py @@ -164,4 +164,4 @@ class FileType(models.IntegerChoices): FileCategoryChoices = [(x.value, x.name) for x in BaseFileUpload.FileCategory] def __str__(self): - return f"{self.FileTypeChoices[self.file_type][1]} - {self.name}" + return f"{self.FileTypeChoices[self.file_type][1]} - {self.name}{' (Archived)' if self.is_archived else ''}" diff --git a/care/facility/models/patient_consultation.py b/care/facility/models/patient_consultation.py index bd6e86de52..c5398e0a8f 100644 --- a/care/facility/models/patient_consultation.py +++ b/care/facility/models/patient_consultation.py @@ -390,30 +390,90 @@ class ConsultationClinician(models.Model): ) -class PatientConsent(BaseModel): +class PatientConsent(BaseModel, ConsultationRelatedPermissionMixin): consultation = models.ForeignKey(PatientConsultation, on_delete=models.CASCADE) type = models.IntegerField(choices=ConsentType.choices) patient_code_status = models.IntegerField( choices=PatientCodeStatusType.choices, null=True, blank=True ) archived = models.BooleanField(default=False) - files = models.ManyToManyField(FileUpload, related_name="consents") + archived_by = models.ForeignKey( + User, + on_delete=models.PROTECT, + null=True, + blank=True, + related_name="archived_consents", + ) + archived_date = models.DateTimeField(null=True, blank=True) created_by = models.ForeignKey( User, on_delete=models.PROTECT, related_name="created_consents" ) + class Meta: + constraints = [ + models.UniqueConstraint( + fields=["consultation", "type"], + name="unique_consultation_consent", + condition=models.Q(archived=False), + ) + ] + def __str__(self) -> str: - return f"{self.consultation.patient.name} - {ConsentType(self.type).label}" + return f"{self.consultation.patient.name} - {ConsentType(self.type).label}{' (Archived)' if self.archived else ''}" def save(self, *args, **kwargs): - # archive existing patient code status consent - if self.pk is None and self.type == ConsentType.PATIENT_CODE_STATUS: - existing_pcs = PatientConsent.objects.filter( - consultation=self.consultation, - type=ConsentType.PATIENT_CODE_STATUS, - archived=False, + if self.archived: + files = FileUpload.objects.filter( + associating_id=self.external_id, + is_archived=False, + ) + files.update( + is_archived=True, + archived_datetime=timezone.now(), + archive_reason="Consent Archived", + archived_by=self.archived_by, ) - if existing_pcs.exists(): - existing_pcs.update(archived=True) super().save(*args, **kwargs) + + @staticmethod + def has_write_permission(request): + return request.user.is_superuser or ( + request.user.verified + and ConsultationRelatedPermissionMixin.has_write_permission(request) + ) + + def has_object_read_permission(self, request): + if not super().has_object_read_permission(request): + return False + return ( + request.user.is_superuser + or ( + self.patient.facility + and request.user in self.consultation.patient.facility.users.all() + ) + or ( + self.consultation.assigned_to == request.user + or request.user == self.consultation.patient.assigned_to + ) + or ( + request.user.user_type >= User.TYPE_VALUE_MAP["DistrictLabAdmin"] + and ( + self.consultation.patient.facility + and request.user.district + == self.consultation.patient.facility.district + ) + ) + or ( + request.user.user_type >= User.TYPE_VALUE_MAP["StateLabAdmin"] + and ( + self.consultation.patient.facility + and request.user.state == self.consultation.patient.facility.state + ) + ) + ) + + def has_object_update_permission(self, request): + return super().has_object_update_permission( + request + ) and self.has_object_read_permission(request) diff --git a/config/api_router.py b/config/api_router.py index ab8787fcc2..746c7cd4ac 100644 --- a/config/api_router.py +++ b/config/api_router.py @@ -59,7 +59,10 @@ PatientSearchViewSet, PatientViewSet, ) -from care.facility.api.viewsets.patient_consultation import PatientConsultationViewSet +from care.facility.api.viewsets.patient_consultation import ( + PatientConsentViewSet, + PatientConsultationViewSet, +) from care.facility.api.viewsets.patient_external_test import PatientExternalTestViewSet from care.facility.api.viewsets.patient_investigation import ( InvestigationGroupViewset, @@ -234,6 +237,7 @@ consultation_nested_router.register( r"prescription_administration", MedicineAdministrationViewSet ) +consultation_nested_router.register(r"consents", PatientConsentViewSet) consultation_nested_router.register(r"events", PatientConsultationEventViewSet) router.register("event_types", EventTypeViewSet) From c25dce7e3f9d2ebe768fd1dc6e7383fc2c7edf74 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Fri, 31 May 2024 06:48:40 +0530 Subject: [PATCH 07/21] added tests --- .../api/serializers/patient_consultation.py | 25 +++ ...ntconsultation_consent_records_and_more.py | 20 +++ care/facility/models/patient_consultation.py | 14 +- care/facility/tests/test_file_upload.py | 26 +-- .../tests/test_patient_consents_api.py | 155 ++++++++++++++++++ care/utils/tests/test_utils.py | 19 +++ 6 files changed, 236 insertions(+), 23 deletions(-) create mode 100644 care/facility/tests/test_patient_consents_api.py diff --git a/care/facility/api/serializers/patient_consultation.py b/care/facility/api/serializers/patient_consultation.py index 216e22c8a4..671d6dd09c 100644 --- a/care/facility/api/serializers/patient_consultation.py +++ b/care/facility/api/serializers/patient_consultation.py @@ -53,6 +53,7 @@ SuggestionChoices, ) from care.facility.models.patient_consultation import ( + ConsentType, PatientConsent, PatientConsultation, ) @@ -898,6 +899,30 @@ class Meta: "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 diff --git a/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py b/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py index bdaddb3b51..ac092302d1 100644 --- a/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py +++ b/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py @@ -182,6 +182,26 @@ def reverse_migrate(apps, schema_editor): name="unique_consultation_consent", ), ), + migrations.AddConstraint( + model_name="patientconsent", + constraint=models.CheckConstraint( + check=models.Q( + models.Q(("type", 2), _negated=True), + ("patient_code_status__isnull", False), + _connector="OR", + ), + name="patient_code_status_required", + ), + ), + migrations.AddConstraint( + model_name="patientconsent", + constraint=models.CheckConstraint( + check=models.Q( + ("type", 2), ("patient_code_status__isnull", True), _connector="OR" + ), + name="patient_code_status_not_required", + ), + ), migrations.RunPython(migrate_consents, reverse_code=reverse_migrate), migrations.RemoveField( model_name="patientconsultation", diff --git a/care/facility/models/patient_consultation.py b/care/facility/models/patient_consultation.py index c5398e0a8f..e768740d61 100644 --- a/care/facility/models/patient_consultation.py +++ b/care/facility/models/patient_consultation.py @@ -415,7 +415,17 @@ class Meta: fields=["consultation", "type"], name="unique_consultation_consent", condition=models.Q(archived=False), - ) + ), + models.CheckConstraint( + name="patient_code_status_required", + check=~models.Q(type=ConsentType.PATIENT_CODE_STATUS) + | models.Q(patient_code_status__isnull=False), + ), + models.CheckConstraint( + name="patient_code_status_not_required", + check=models.Q(type=ConsentType.PATIENT_CODE_STATUS) + | models.Q(patient_code_status__isnull=True), + ), ] def __str__(self) -> str: @@ -449,7 +459,7 @@ def has_object_read_permission(self, request): return ( request.user.is_superuser or ( - self.patient.facility + self.consultation.patient.facility and request.user in self.consultation.patient.facility.users.all() ) or ( diff --git a/care/facility/tests/test_file_upload.py b/care/facility/tests/test_file_upload.py index 45a65e1f79..d8eddd1c52 100644 --- a/care/facility/tests/test_file_upload.py +++ b/care/facility/tests/test_file_upload.py @@ -1,5 +1,3 @@ -import json - from rest_framework import status from rest_framework.test import APITestCase @@ -64,31 +62,16 @@ def setUpTestData(cls) -> None: cls.district, cls.facility, local_body=cls.local_body ) cls.consultation = cls.create_consultation(cls.patient, cls.facility) + cls.consent = cls.create_patient_consent(cls.consultation) def test_consent_file_upload(self): - response = self.client.patch( - f"/api/v1/consultation/{self.consultation.external_id}/", - { - "consent_records": json.dumps( - [ - { - "id": "consent-12345", - "type": 2, - "patient_code_status": 1, - } - ] - ) - }, - ) - self.assertEqual(response.status_code, status.HTTP_200_OK) - upload_response = self.client.post( "/api/v1/files/", { "original_name": "test.pdf", "file_type": "CONSENT_RECORD", "name": "Test File", - "associating_id": "consent-12345", + "associating_id": self.consent.external_id, "file_category": "UNSPECIFIED", "mime_type": "application/pdf", }, @@ -97,11 +80,12 @@ def test_consent_file_upload(self): self.assertEqual(upload_response.status_code, status.HTTP_201_CREATED) self.assertEqual( - FileUpload.objects.filter(associating_id="consent-12345").count(), 1 + FileUpload.objects.filter(associating_id=self.consent.external_id).count(), + 1, ) all_files = self.client.get( - "/api/v1/files/?associating_id=consent-12345&file_type=CONSENT_RECORD&is_archived=false" + f"/api/v1/files/?associating_id={self.consent.external_id}&file_type=CONSENT_RECORD&is_archived=false" ) self.assertEqual(all_files.status_code, status.HTTP_200_OK) diff --git a/care/facility/tests/test_patient_consents_api.py b/care/facility/tests/test_patient_consents_api.py new file mode 100644 index 0000000000..e59c958779 --- /dev/null +++ b/care/facility/tests/test_patient_consents_api.py @@ -0,0 +1,155 @@ +from rest_framework.test import APITestCase + +from care.facility.models.patient_consultation import ConsentType, PatientCodeStatusType +from care.utils.tests.test_utils import TestUtils + + +class TestPatientConsent(TestUtils, APITestCase): + @classmethod + def setUpTestData(cls) -> None: + cls.state = cls.create_state() + cls.district = cls.create_district(cls.state) + cls.local_body = cls.create_local_body(cls.district) + cls.super_user = cls.create_super_user("su", cls.district) + cls.facility = cls.create_facility(cls.super_user, cls.district, cls.local_body) + cls.location = cls.create_asset_location(cls.facility) + cls.user = cls.create_user("staff1", cls.district, home_facility=cls.facility) + cls.doctor = cls.create_user( + "doctor", cls.district, home_facility=cls.facility, user_type=15 + ) + cls.patient1 = cls.create_patient(cls.district, cls.facility) + cls.consultation = cls.create_consultation( + cls.patient1, cls.facility, cls.doctor + ) + cls.patient2 = cls.create_patient(cls.district, cls.facility) + cls.consultation2 = cls.create_consultation( + cls.patient2, cls.facility, cls.doctor + ) + + def test_create_consent(self): + response = self.client.post( + f"/api/v1/consultation/{self.consultation.external_id}/consents/", + { + "type": ConsentType.CONSENT_FOR_ADMISSION, + }, + ) + self.assertEqual(response.status_code, 201) + self.assertEqual(response.data["type"], ConsentType.CONSENT_FOR_ADMISSION) + + def test_list_consent(self): + response = self.client.get( + f"/api/v1/consultation/{self.consultation.external_id}/consents/" + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.data.get("results")), 0) + + self.client.post( + f"/api/v1/consultation/{self.consultation.external_id}/consents/", + { + "type": ConsentType.CONSENT_FOR_ADMISSION, + }, + ) + response = self.client.get( + f"/api/v1/consultation/{self.consultation.external_id}/consents/" + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.data.get("results")), 1) + + def test_retrieve_consent(self): + response = self.client.post( + f"/api/v1/consultation/{self.consultation.external_id}/consents/", + { + "type": ConsentType.CONSENT_FOR_ADMISSION, + }, + ) + self.assertEqual(response.status_code, 201) + response = self.client.get( + f"/api/v1/consultation/{self.consultation.external_id}/consents/{response.data['id']}/" + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data["type"], ConsentType.CONSENT_FOR_ADMISSION) + + def test_update_consent(self): + response = self.client.post( + f"/api/v1/consultation/{self.consultation.external_id}/consents/", + { + "type": ConsentType.CONSENT_FOR_ADMISSION, + }, + ) + self.assertEqual(response.status_code, 201) + response = self.client.patch( + f"/api/v1/consultation/{self.consultation.external_id}/consents/{response.data['id']}/", + { + "type": ConsentType.CONSENT_FOR_PROCEDURE, + }, + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data["type"], ConsentType.CONSENT_FOR_PROCEDURE) + + def test_auto_archive_consents(self): + response_1 = self.client.post( + f"/api/v1/consultation/{self.consultation.external_id}/consents/", + { + "type": ConsentType.PATIENT_CODE_STATUS, + "patient_code_status": PatientCodeStatusType.ACTIVE_TREATMENT, + }, + ) + self.assertEqual(response_1.status_code, 201) + + upload_response = self.client.post( + "/api/v1/files/", + { + "original_name": "test.pdf", + "file_type": "CONSENT_RECORD", + "name": "Test File", + "associating_id": response_1.data["id"], + "file_category": "UNSPECIFIED", + "mime_type": "application/pdf", + }, + ) + + self.assertEqual(upload_response.status_code, 201) + + response_2 = self.client.post( + f"/api/v1/consultation/{self.consultation.external_id}/consents/", + { + "type": ConsentType.PATIENT_CODE_STATUS, + "patient_code_status": PatientCodeStatusType.COMFORT_CARE, + }, + ) + + self.assertEqual(response_2.status_code, 201) + + response = self.client.get( + f"/api/v1/consultation/{self.consultation.external_id}/consents/{response_1.data['id']}/" + ) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.data["archived"], True) + + files = self.client.get( + f"/api/v1/files/?associating_id={response_1.data['id']}&file_type=CONSENT_RECORD&is_archived=false" + ) + + self.assertEqual(files.status_code, 200) + self.assertEqual(files.data["count"], 0) + + def test_patient_code_status_constraint(self): + response = self.client.post( + f"/api/v1/consultation/{self.consultation.external_id}/consents/", + { + "type": ConsentType.PATIENT_CODE_STATUS, + }, + ) + + self.assertEqual(response.status_code, 400) + + response = self.client.post( + f"/api/v1/consultation/{self.consultation.external_id}/consents/", + { + "type": ConsentType.CONSENT_FOR_ADMISSION, + "patient_code_status": PatientCodeStatusType.ACTIVE_TREATMENT, + }, + ) + + self.assertEqual(response.status_code, 400) diff --git a/care/utils/tests/test_utils.py b/care/utils/tests/test_utils.py index 20fbfee7d2..a167f78135 100644 --- a/care/utils/tests/test_utils.py +++ b/care/utils/tests/test_utils.py @@ -32,6 +32,11 @@ ICD11Diagnosis, ) from care.facility.models.patient import RationCardCategory +from care.facility.models.patient_consultation import ( + ConsentType, + PatientCodeStatusType, + PatientConsent, +) from care.users.models import District, State @@ -448,6 +453,20 @@ def create_consultation_diagnosis( data.update(kwargs) return ConsultationDiagnosis.objects.create(**data) + @classmethod + def create_patient_consent( + cls, + consultation: PatientConsultation, + **kwargs, + ): + data = { + "consultation": consultation, + "type": ConsentType.PATIENT_CODE_STATUS, + "patient_code_status": PatientCodeStatusType.COMFORT_CARE, + } + data.update(kwargs) + return PatientConsent.objects.create(**data) + @classmethod def clone_object(cls, obj, save=True): new_obj = obj._meta.model.objects.get(pk=obj.id) From af8ae904c0324860b4250db2d89c8b0adb9cf214 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Fri, 31 May 2024 20:51:10 +0530 Subject: [PATCH 08/21] fixed test --- care/facility/tests/test_file_upload.py | 2 +- care/utils/tests/test_utils.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/care/facility/tests/test_file_upload.py b/care/facility/tests/test_file_upload.py index d8eddd1c52..bfbe12c372 100644 --- a/care/facility/tests/test_file_upload.py +++ b/care/facility/tests/test_file_upload.py @@ -62,7 +62,7 @@ def setUpTestData(cls) -> None: cls.district, cls.facility, local_body=cls.local_body ) cls.consultation = cls.create_consultation(cls.patient, cls.facility) - cls.consent = cls.create_patient_consent(cls.consultation) + cls.consent = cls.create_patient_consent(cls.consultation, created_by=cls.user) def test_consent_file_upload(self): upload_response = self.client.post( diff --git a/care/utils/tests/test_utils.py b/care/utils/tests/test_utils.py index a167f78135..8670e91d75 100644 --- a/care/utils/tests/test_utils.py +++ b/care/utils/tests/test_utils.py @@ -463,6 +463,7 @@ def create_patient_consent( "consultation": consultation, "type": ConsentType.PATIENT_CODE_STATUS, "patient_code_status": PatientCodeStatusType.COMFORT_CARE, + "created_by": consultation.created_by, } data.update(kwargs) return PatientConsent.objects.create(**data) From 06ee63ff068ac462b6ebe76e20cd1649965c6904 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Sat, 1 Jun 2024 21:23:41 +0530 Subject: [PATCH 09/21] made changes --- .../api/serializers/patient_consultation.py | 16 ++++++++++------ ...tientconsultation_consent_records_and_more.py | 7 +------ care/facility/models/patient_consultation.py | 1 + 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/care/facility/api/serializers/patient_consultation.py b/care/facility/api/serializers/patient_consultation.py index 671d6dd09c..6dbf80be84 100644 --- a/care/facility/api/serializers/patient_consultation.py +++ b/care/facility/api/serializers/patient_consultation.py @@ -936,13 +936,17 @@ def clear_existing_records(self, consultation, type, self_id=None): consent.save() def create(self, validated_data): - self.clear_existing_records( - consultation=validated_data["consultation"], type=validated_data["type"] - ) + 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): - self.clear_existing_records( - consultation=instance.consultation, type=instance.type, self_id=instance.id - ) + with transaction.atomic(): + self.clear_existing_records( + consultation=instance.consultation, + type=instance.type, + self_id=instance.id, + ) return super().update(instance, validated_data) diff --git a/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py b/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py index ac092302d1..1b5c413292 100644 --- a/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py +++ b/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py @@ -70,17 +70,12 @@ def reverse_migrate(apps, schema_editor): kwargs = {} - if type(consent.patient_code_status) == int: - kwargs = { - "patient_code_status": consent.patient_code_status, - } - consultation.consent_records.append( { "type": consent.type, "deleted": consent.archived, "id": str(consent.external_id), - **kwargs, + "patient_code_status": consent.patient_code_status**kwargs, } ) consultation.save() diff --git a/care/facility/models/patient_consultation.py b/care/facility/models/patient_consultation.py index e768740d61..fcdf6e2e4f 100644 --- a/care/facility/models/patient_consultation.py +++ b/care/facility/models/patient_consultation.py @@ -435,6 +435,7 @@ def save(self, *args, **kwargs): if self.archived: files = FileUpload.objects.filter( associating_id=self.external_id, + file_type=FileUpload.FileType.CONSENT_RECORD, is_archived=False, ) files.update( From bccd3af83f0b4a6d4a4de2864baf32fd67e9ac94 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Tue, 4 Jun 2024 16:01:03 +0530 Subject: [PATCH 10/21] updated migrations and used bulk updates --- .../api/serializers/patient_consultation.py | 22 ++++++++++++++----- ...tconsultation_consent_records_and_more.py} | 7 ++---- 2 files changed, 18 insertions(+), 11 deletions(-) rename care/facility/migrations/{0441_remove_patientconsultation_consent_records_and_more.py => 0442_remove_patientconsultation_consent_records_and_more.py} (98%) diff --git a/care/facility/api/serializers/patient_consultation.py b/care/facility/api/serializers/patient_consultation.py index 6dbf80be84..792ee9b401 100644 --- a/care/facility/api/serializers/patient_consultation.py +++ b/care/facility/api/serializers/patient_consultation.py @@ -42,6 +42,7 @@ EncounterSymptom, Symptom, ) +from care.facility.models.file_upload import FileUpload from care.facility.models.icd11_diagnosis import ( ConditionVerificationStatus, ConsultationDiagnosis, @@ -928,12 +929,21 @@ def clear_existing_records(self, consultation, type, self_id=None): consultation=consultation, type=type ).exclude(id=self_id) - # looping because .update does not call model save method - for consent in consents: - consent.archived = True - consent.archived_by = self.context["request"].user - consent.archived_date = timezone.now() - consent.save() + consents.update( + archived=True, + archived_by=self.context["request"].user, + archived_date=timezone.now(), + ) + FileUpload.objects.filter( + associating_id__in=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.archived_by, + ) def create(self, validated_data): with transaction.atomic(): diff --git a/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py b/care/facility/migrations/0442_remove_patientconsultation_consent_records_and_more.py similarity index 98% rename from care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py rename to care/facility/migrations/0442_remove_patientconsultation_consent_records_and_more.py index 1b5c413292..713f41ead7 100644 --- a/care/facility/migrations/0441_remove_patientconsultation_consent_records_and_more.py +++ b/care/facility/migrations/0442_remove_patientconsultation_consent_records_and_more.py @@ -67,22 +67,19 @@ def reverse_migrate(apps, schema_editor): ) for consent in PatientConsent.objects.all(): consultation = consent.consultation - - kwargs = {} - consultation.consent_records.append( { "type": consent.type, "deleted": consent.archived, "id": str(consent.external_id), - "patient_code_status": consent.patient_code_status**kwargs, + "patient_code_status": consent.patient_code_status, } ) consultation.save() dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ("facility", "0440_merge_20240528_1613"), + ("facility", "0441_delete_patientteleconsultation"), ] operations = [ From ca64f22be1bb23e77594ad46e1bf57b45b09e178 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Tue, 4 Jun 2024 17:17:51 +0530 Subject: [PATCH 11/21] Update care/facility/api/serializers/patient_consultation.py --- care/facility/api/serializers/patient_consultation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/care/facility/api/serializers/patient_consultation.py b/care/facility/api/serializers/patient_consultation.py index 792ee9b401..9a04c7b871 100644 --- a/care/facility/api/serializers/patient_consultation.py +++ b/care/facility/api/serializers/patient_consultation.py @@ -942,7 +942,7 @@ def clear_existing_records(self, consultation, type, self_id=None): is_archived=True, archived_datetime=timezone.now(), archive_reason="Consent Archived", - archived_by=self.archived_by, + archived_by=self.context["request"].user, ) def create(self, validated_data): From 7d87822a4b3b80c689125dac115aa6c19f64f94a Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Tue, 4 Jun 2024 18:57:09 +0530 Subject: [PATCH 12/21] fixed --- care/facility/api/serializers/patient_consultation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/care/facility/api/serializers/patient_consultation.py b/care/facility/api/serializers/patient_consultation.py index 9a04c7b871..18e49f089d 100644 --- a/care/facility/api/serializers/patient_consultation.py +++ b/care/facility/api/serializers/patient_consultation.py @@ -935,7 +935,7 @@ def clear_existing_records(self, consultation, type, self_id=None): archived_date=timezone.now(), ) FileUpload.objects.filter( - associating_id__in=consents.values_list("external_id", flat=True), + associating_id__in=list(consents.values_list("external_id", flat=True)), file_type=FileUpload.FileType.CONSENT_RECORD, is_archived=False, ).update( From c41e19adce984d5587cba6c3168f09d94b0062f2 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Wed, 5 Jun 2024 19:04:24 +0530 Subject: [PATCH 13/21] fixed permissions and router --- .../api/viewsets/patient_consultation.py | 42 +++++++++++++++++-- config/api_router.py | 5 ++- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/care/facility/api/viewsets/patient_consultation.py b/care/facility/api/viewsets/patient_consultation.py index 6f7fdc61b1..abd19dfd2f 100644 --- a/care/facility/api/viewsets/patient_consultation.py +++ b/care/facility/api/viewsets/patient_consultation.py @@ -312,11 +312,45 @@ class PatientConsentViewSet( 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) - return self.queryset.filter(consultation__external_id=consultation_id) + return self.queryset.filter( + consultation=self.get_consultation(self.kwargs["consultation_id"]) + ) def perform_create(self, serializer): - consultation_id = self.kwargs.get("consultation_external_id", None) - consultation = PatientConsultation.objects.get(external_id=consultation_id) + consultation = self.get_consultation(self.kwargs["consultation_id"]) serializer.save(consultation=consultation, created_by=self.request.user) diff --git a/config/api_router.py b/config/api_router.py index 81b1c36263..78cb45736b 100644 --- a/config/api_router.py +++ b/config/api_router.py @@ -292,8 +292,9 @@ r"events", PatientConsultationEventViewSet, basename="consultation-events" ) -consultation_nested_router.register(r"consents", PatientConsentViewSet) -consultation_nested_router.register(r"events", PatientConsultationEventViewSet) +consultation_nested_router.register( + r"consents", PatientConsentViewSet, basename="consultation-consents" +) router.register("event_types", EventTypeViewSet, basename="event-types") From d35552118015eda1d2fc776cd2b8f19237b4ae30 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Wed, 5 Jun 2024 22:39:32 +0530 Subject: [PATCH 14/21] fixes --- care/facility/api/viewsets/patient_consultation.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/care/facility/api/viewsets/patient_consultation.py b/care/facility/api/viewsets/patient_consultation.py index abd19dfd2f..da55a44ed9 100644 --- a/care/facility/api/viewsets/patient_consultation.py +++ b/care/facility/api/viewsets/patient_consultation.py @@ -347,10 +347,14 @@ def get_consultation(self, consultation_id): return consultation.filter(applied_filters).first() def get_queryset(self): - return self.queryset.filter( - consultation=self.get_consultation(self.kwargs["consultation_id"]) - ) + consultation_id = self.kwargs.get("consultation_external_id", None) + 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 = self.get_consultation(self.kwargs["consultation_id"]) + 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) From 7ba63ead54fdfe730900a5feeac35484232f6f00 Mon Sep 17 00:00:00 2001 From: Aakash Singh Date: Thu, 6 Jun 2024 12:30:59 +0530 Subject: [PATCH 15/21] cleanup queryset --- .../api/viewsets/patient_consultation.py | 51 ++++--------------- 1 file changed, 10 insertions(+), 41 deletions(-) diff --git a/care/facility/api/viewsets/patient_consultation.py b/care/facility/api/viewsets/patient_consultation.py index da55a44ed9..b7c4f2b512 100644 --- a/care/facility/api/viewsets/patient_consultation.py +++ b/care/facility/api/viewsets/patient_consultation.py @@ -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 @@ -34,6 +34,7 @@ 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): @@ -312,49 +313,17 @@ class PatientConsentViewSet( 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 + def get_consultation_obj(self): + return get_object_or_404( + get_consultation_queryset(self.request.user).filter( + external_id=self.kwargs["consultation_external_id"] ) - 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) - if not consultation_id: - raise NotFound({"detail": "Consultation not found"}) - return self.queryset.filter(consultation=self.get_consultation(consultation_id)) + return self.queryset.filter(consultation=self.get_consultation_obj()) 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) + serializer.save( + consultation=self.get_consultation_obj(), created_by=self.request.user + ) From 77939cd67769502023ca484834c017f1c5a20956 Mon Sep 17 00:00:00 2001 From: Aakash Singh Date: Thu, 6 Jun 2024 12:47:58 +0530 Subject: [PATCH 16/21] rebase migrations --- ...3_remove_patientconsultation_consent_records_and_more.py} | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) rename care/facility/migrations/{0442_remove_patientconsultation_consent_records_and_more.py => 0443_remove_patientconsultation_consent_records_and_more.py} (98%) diff --git a/care/facility/migrations/0442_remove_patientconsultation_consent_records_and_more.py b/care/facility/migrations/0443_remove_patientconsultation_consent_records_and_more.py similarity index 98% rename from care/facility/migrations/0442_remove_patientconsultation_consent_records_and_more.py rename to care/facility/migrations/0443_remove_patientconsultation_consent_records_and_more.py index 713f41ead7..9c48295837 100644 --- a/care/facility/migrations/0442_remove_patientconsultation_consent_records_and_more.py +++ b/care/facility/migrations/0443_remove_patientconsultation_consent_records_and_more.py @@ -79,7 +79,10 @@ def reverse_migrate(apps, schema_editor): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ("facility", "0441_delete_patientteleconsultation"), + ( + "facility", + "0442_remove_patientconsultation_unique_patient_no_within_facility", + ), ] operations = [ From 33fb8db33872acdabc80ebfb2124bdfc76922a48 Mon Sep 17 00:00:00 2001 From: Aakash Singh Date: Thu, 6 Jun 2024 12:59:50 +0530 Subject: [PATCH 17/21] allow only home facility users to create or update consent --- .../api/serializers/patient_consultation.py | 28 +++++++++++++++---- .../api/viewsets/patient_consultation.py | 8 +++--- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/care/facility/api/serializers/patient_consultation.py b/care/facility/api/serializers/patient_consultation.py index 78ee169941..3385cd21cb 100644 --- a/care/facility/api/serializers/patient_consultation.py +++ b/care/facility/api/serializers/patient_consultation.py @@ -885,6 +885,15 @@ class Meta: ) 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" ): @@ -908,14 +917,14 @@ def validate(self, attrs): ) return attrs - def clear_existing_records(self, consultation, type, self_id=None): + def clear_existing_records(self, consultation, type, user, 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_by=user, archived_date=timezone.now(), ) FileUpload.objects.filter( @@ -926,21 +935,28 @@ def clear_existing_records(self, consultation, type, self_id=None): is_archived=True, archived_datetime=timezone.now(), archive_reason="Consent Archived", - archived_by=self.context["request"].user, + archived_by=user, ) def create(self, validated_data): with transaction.atomic(): self.clear_existing_records( - consultation=validated_data["consultation"], type=validated_data["type"] + consultation=self.context["consultation"], + type=validated_data["type"], + user=self.context["request"].user, + ) + return super().create( + validated_data, + consultation=self.context["consultation"], + 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) + return super().update(instance, validated_data) diff --git a/care/facility/api/viewsets/patient_consultation.py b/care/facility/api/viewsets/patient_consultation.py index b7c4f2b512..4fc1b857b2 100644 --- a/care/facility/api/viewsets/patient_consultation.py +++ b/care/facility/api/viewsets/patient_consultation.py @@ -323,7 +323,7 @@ def get_consultation_obj(self): def get_queryset(self): return self.queryset.filter(consultation=self.get_consultation_obj()) - def perform_create(self, serializer): - serializer.save( - consultation=self.get_consultation_obj(), created_by=self.request.user - ) + def get_serializer_context(self): + data = super().get_serializer_context() + data["consultation"] = self.get_consultation_obj() + return data From 6d6752aaa40e8c2478a08807a550ab17db3dab14 Mon Sep 17 00:00:00 2001 From: Aakash Singh Date: Thu, 6 Jun 2024 13:08:38 +0530 Subject: [PATCH 18/21] fixes --- .../api/serializers/patient_consultation.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/care/facility/api/serializers/patient_consultation.py b/care/facility/api/serializers/patient_consultation.py index 3385cd21cb..1918b37441 100644 --- a/care/facility/api/serializers/patient_consultation.py +++ b/care/facility/api/serializers/patient_consultation.py @@ -922,10 +922,11 @@ def clear_existing_records(self, consultation, type, user, self_id=None): consultation=consultation, type=type ).exclude(id=self_id) + archived_date = timezone.now() consents.update( archived=True, archived_by=user, - archived_date=timezone.now(), + archived_date=archived_date, ) FileUpload.objects.filter( associating_id__in=list(consents.values_list("external_id", flat=True)), @@ -933,7 +934,7 @@ def clear_existing_records(self, consultation, type, user, self_id=None): is_archived=False, ).update( is_archived=True, - archived_datetime=timezone.now(), + archived_datetime=archived_date, archive_reason="Consent Archived", archived_by=user, ) @@ -945,11 +946,9 @@ def create(self, validated_data): type=validated_data["type"], user=self.context["request"].user, ) - return super().create( - validated_data, - consultation=self.context["consultation"], - created_by=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(): From 73765b50f696bddbff186f33ebe75d571d0d565f Mon Sep 17 00:00:00 2001 From: Aakash Singh Date: Thu, 6 Jun 2024 13:12:39 +0530 Subject: [PATCH 19/21] add is_migrated field --- ...remove_patientconsultation_consent_records_and_more.py | 8 ++++++++ care/facility/models/patient_consultation.py | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/care/facility/migrations/0443_remove_patientconsultation_consent_records_and_more.py b/care/facility/migrations/0443_remove_patientconsultation_consent_records_and_more.py index 9c48295837..788bf8f88e 100644 --- a/care/facility/migrations/0443_remove_patientconsultation_consent_records_and_more.py +++ b/care/facility/migrations/0443_remove_patientconsultation_consent_records_and_more.py @@ -37,6 +37,7 @@ def migrate_consents(apps, schema_editor): patient_code_status=consent.get("patient_code_status", None), created_by=consultation.created_by, archived=consent.get("deleted", False), + is_migrated=True, ) old_id = consent.get("id") @@ -163,6 +164,13 @@ def reverse_migrate(apps, schema_editor): to=settings.AUTH_USER_MODEL, ), ), + ( + "is_migrated", + models.BooleanField( + default=False, + help_text="This field is to throw caution to data that was previously ported over", + ), + ), ], bases=( models.Model, diff --git a/care/facility/models/patient_consultation.py b/care/facility/models/patient_consultation.py index 1eb9c94d3c..4db0163f30 100644 --- a/care/facility/models/patient_consultation.py +++ b/care/facility/models/patient_consultation.py @@ -403,6 +403,10 @@ class PatientConsent(BaseModel, ConsultationRelatedPermissionMixin): created_by = models.ForeignKey( User, on_delete=models.PROTECT, related_name="created_consents" ) + is_migrated = models.BooleanField( + default=False, + help_text="This field is to throw caution to data that was previously ported over", + ) class Meta: constraints = [ From f243b65ba9c2ce5fed57717fcb527c9d0d8905c7 Mon Sep 17 00:00:00 2001 From: Aakash Singh Date: Thu, 6 Jun 2024 15:09:40 +0530 Subject: [PATCH 20/21] fix permission --- care/facility/api/serializers/patient_consultation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/care/facility/api/serializers/patient_consultation.py b/care/facility/api/serializers/patient_consultation.py index 1918b37441..6c0e132e16 100644 --- a/care/facility/api/serializers/patient_consultation.py +++ b/care/facility/api/serializers/patient_consultation.py @@ -887,7 +887,7 @@ class Meta: def validate(self, attrs): user = self.context["request"].user if ( - user.user_type <= User.TYPE_VALUE_MAP["DistrictAdmin"] + user.user_type < User.TYPE_VALUE_MAP["DistrictAdmin"] and self.context["consultation"].facility_id != user.home_facility_id ): raise ValidationError( From 137cfb8b67ebb0f1c543b8bb8d84ee04092c3c51 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Thu, 6 Jun 2024 15:50:13 +0530 Subject: [PATCH 21/21] remove types from migrations --- ...ntconsultation_consent_records_and_more.py | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/care/facility/migrations/0443_remove_patientconsultation_consent_records_and_more.py b/care/facility/migrations/0443_remove_patientconsultation_consent_records_and_more.py index 788bf8f88e..d6f72ca5ef 100644 --- a/care/facility/migrations/0443_remove_patientconsultation_consent_records_and_more.py +++ b/care/facility/migrations/0443_remove_patientconsultation_consent_records_and_more.py @@ -8,24 +8,13 @@ from django.utils import timezone import care.facility.models.mixins.permissions.patient -from care.facility.models.file_upload import FileUpload as FileUploadModel -from care.facility.models.patient_consultation import ( - PatientConsent as PatientConsentModel, -) -from care.facility.models.patient_consultation import ( - PatientConsultation as PatientConsultationModel, -) class Migration(migrations.Migration): def migrate_consents(apps, schema_editor): - PatientConsultation: PatientConsultationModel = apps.get_model( - "facility", "PatientConsultation" - ) - PatientConsent: PatientConsentModel = apps.get_model( - "facility", "PatientConsent" - ) - FileUpload: FileUploadModel = apps.get_model("facility", "FileUpload") + PatientConsultation = apps.get_model("facility", "PatientConsultation") + PatientConsent = apps.get_model("facility", "PatientConsent") + FileUpload = apps.get_model("facility", "FileUpload") consultations = PatientConsultation.objects.filter( consent_records__isnull=False ) @@ -44,7 +33,7 @@ def migrate_consents(apps, schema_editor): files = FileUpload.objects.filter( associating_id=old_id, - file_type=FileUploadModel.FileType.CONSENT_RECORD, + file_type=7, ) kwargs = { @@ -63,9 +52,7 @@ def migrate_consents(apps, schema_editor): files.update(**kwargs) def reverse_migrate(apps, schema_editor): - PatientConsent: PatientConsentModel = apps.get_model( - "facility", "PatientConsent" - ) + PatientConsent = apps.get_model("facility", "PatientConsent") for consent in PatientConsent.objects.all(): consultation = consent.consultation consultation.consent_records.append(