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

Merge Develop to Staging v24.30.0 #2308

Merged
merged 37 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
36f3a36
added tests for beds endpoints
DraKen0009 Apr 20, 2024
bdb5173
Merge branch 'develop' into bed-test-cases
DraKen0009 May 3, 2024
b789986
added count check
DraKen0009 May 15, 2024
7e877e1
Merge branch 'develop' into bed-test-cases
DraKen0009 May 15, 2024
e7799c9
updated list bed testcase
DraKen0009 May 23, 2024
fdcc40c
Disallow read only users from performing writes to asset
rithviknishad Jun 6, 2024
d9f04fc
Merge branch 'refs/heads/master' into bed-test-cases
DraKen0009 Jun 7, 2024
d68c9b1
fixed conflicts
DraKen0009 Jun 7, 2024
ee11cb4
merging
DraKen0009 Jun 7, 2024
07f0cd3
fixed the test cases
DraKen0009 Jun 7, 2024
931e61c
Return files with consent lists and consent filters
shivankacker Jun 8, 2024
c3015bc
fix tests
shivankacker Jun 8, 2024
77110e2
Merge branch 'develop' into rithviknishad/fix/disable-asset-create-fo…
rithviknishad Jun 13, 2024
f0908c3
Merge branch 'develop' of https://github.com/coronasafe/care into con…
shivankacker Jun 23, 2024
80f1d13
used boolean field
shivankacker Jun 23, 2024
bafa39c
made changes
shivankacker Jun 24, 2024
f15a6fb
fixed tests
shivankacker Jun 24, 2024
fada136
minor fixes
shivankacker Jul 2, 2024
e45b430
revert change
shivankacker Jul 2, 2024
02a2170
switched to array for inexpensive queries
shivankacker Jul 4, 2024
5b74301
fix
shivankacker Jul 5, 2024
ceed9cf
added is_expired property on patient model and PatientSearchSerializer
khavinshankar Jul 8, 2024
1787dc5
added is_expired validation in patient/transfer
khavinshankar Jul 8, 2024
155875d
use death_datetime to check if patient is expired or not
khavinshankar Jul 9, 2024
841e89b
added test `test_transfer_with_expired_patient`
khavinshankar Jul 9, 2024
fdbf135
fixed the tests
khavinshankar Jul 9, 2024
f23128d
Merge branch 'develop' into fix-6022
nihal467 Jul 9, 2024
02b4f97
Select related created_by and updated_by users for encounter symptoms…
rithviknishad Jul 9, 2024
b4dbc46
Merge branch 'develop' into bed-test-cases
sainak Jul 10, 2024
b30aee6
Merge pull request #2104 from DraKen0009/bed-test-cases
vigneshhari Jul 12, 2024
3034030
Merge branch 'develop' into rithviknishad/fix/disable-asset-create-fo…
vigneshhari Jul 13, 2024
f89e730
Merge pull request #2252 from coronasafe/rithviknishad/fix/disable-as…
vigneshhari Jul 13, 2024
5aca29e
Merge pull request #2293 from coronasafe/fix-6022
vigneshhari Jul 13, 2024
f5638c7
Merge branch 'develop' into rithviknishad/fix/encounter-symptoms-n+1
vigneshhari Jul 13, 2024
24d1842
Merge pull request #2297 from coronasafe/rithviknishad/fix/encounter-…
vigneshhari Jul 13, 2024
c9caff8
Merge pull request #2255 from coronasafe/consent-filters
vigneshhari Jul 13, 2024
ac7f696
Merge migrations (#2303)
shivankacker Jul 17, 2024
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
1 change: 1 addition & 0 deletions care/facility/api/serializers/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ class Meta:
"facility",
"allow_transfer",
"is_active",
"is_expired",
)


Expand Down
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
2 changes: 1 addition & 1 deletion care/facility/api/viewsets/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ class AssetViewSet(
lookup_field = "external_id"
filter_backends = (filters.DjangoFilterBackend, drf_filters.SearchFilter)
search_fields = ["name", "serial_number", "qr_code_id"]
permission_classes = [IsAuthenticated]
permission_classes = (IsAuthenticated, DRYPermissions)
filterset_class = AssetFilter

def get_queryset(self):
Expand Down
2 changes: 1 addition & 1 deletion care/facility/api/viewsets/encounter_symptom.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def filter_is_cured(self, queryset, name, value):
class EncounterSymptomViewSet(ModelViewSet):
serializer_class = EncounterSymptomSerializer
permission_classes = (IsAuthenticated, DRYPermissions)
queryset = EncounterSymptom.objects.all()
queryset = EncounterSymptom.objects.select_related("created_by", "updated_by")
filter_backends = (filters.DjangoFilterBackend,)
filterset_class = EncounterSymptomFilter
lookup_field = "external_id"
Expand Down
34 changes: 34 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 Expand Up @@ -565,6 +591,14 @@ def transfer(self, request, *args, **kwargs):
patient = PatientRegistration.objects.get(external_id=kwargs["external_id"])
facility = Facility.objects.get(external_id=request.data["facility"])

if patient.is_expired:
return Response(
{
"Patient": "Patient transfer cannot be completed because the patient is expired"
},
status=status.HTTP_406_NOT_ACCEPTABLE,
)

if patient.is_active and facility == patient.facility:
return Response(
{
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
),
]
13 changes: 13 additions & 0 deletions care/facility/migrations/0445_merge_20240715_0301.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Generated by Django 4.2.10 on 2024-07-14 21:31

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("facility", "0444_alter_medicineadministration_dosage_and_more"),
("facility", "0444_patientconsultation_has_consents_and_more"),
]

operations = []
20 changes: 20 additions & 0 deletions care/facility/models/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,26 @@ def delete(self, *args, **kwargs):
AssetBed.objects.filter(asset=self).update(deleted=True)
super().delete(*args, **kwargs)

@staticmethod
def has_write_permission(request):
if request.user.asset or request.user.user_type in User.READ_ONLY_TYPES:
return False
return (
request.user.is_superuser
or request.user.verified
and request.user.user_type >= User.TYPE_VALUE_MAP["Staff"]
)

def has_object_write_permission(self, request):
return self.has_write_permission(request)

@staticmethod
def has_read_permission(request):
return request.user.is_superuser or request.user.verified

def has_object_read_permission(self, request):
return self.has_read_permission(request)

def __str__(self):
return self.name

Expand Down
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 ''}"
4 changes: 4 additions & 0 deletions care/facility/models/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,10 @@ class TestTypeEnum(enum.Enum):

objects = BaseManager()

@property
def is_expired(self) -> bool:
return self.death_datetime is not None

def __str__(self):
return f"{self.name} - {self.year_of_birth} - {self.get_gender_display()}"

Expand Down
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
16 changes: 16 additions & 0 deletions care/facility/tests/test_asset_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from rest_framework.test import APITestCase

from care.facility.models import Asset, Bed
from care.users.models import User
from care.utils.assetintegration.asset_classes import AssetClasses
from care.utils.tests.test_utils import TestUtils

Expand All @@ -17,6 +18,11 @@ def setUpTestData(cls) -> None:
cls.facility = cls.create_facility(cls.super_user, cls.district, cls.local_body)
cls.asset_location = cls.create_asset_location(cls.facility)
cls.user = cls.create_user("staff", cls.district, home_facility=cls.facility)
cls.state_admin_ro = cls.create_user(
"stateadmin-ro",
cls.district,
user_type=User.TYPE_VALUE_MAP["StateReadOnlyAdmin"],
)
cls.patient = cls.create_patient(
cls.district, cls.facility, local_body=cls.local_body
)
Expand All @@ -38,6 +44,16 @@ def test_create_asset(self):
response = self.client.post("/api/v1/asset/", sample_data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

def test_create_asset_read_only(self):
sample_data = {
"name": "Test Asset",
"asset_type": 50,
"location": self.asset_location.external_id,
}
self.client.force_authenticate(self.state_admin_ro)
response = self.client.post("/api/v1/asset/", sample_data)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

def test_create_asset_with_warranty_past(self):
sample_data = {
"name": "Test Asset",
Expand Down
Loading
Loading