Skip to content

Commit

Permalink
Merge pull request #1962 from coronasafe/staging
Browse files Browse the repository at this point in the history
Production release v24.11.0
  • Loading branch information
gigincg committed Mar 11, 2024
2 parents dbcee30 + 2bd7118 commit ff7d5de
Show file tree
Hide file tree
Showing 27 changed files with 1,474 additions and 84 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ makemigrations:
docker compose exec backend bash -c "python manage.py makemigrations"

test:
docker compose exec backend bash -c "python manage.py test --keepdb --parallel"
docker compose exec backend bash -c "python manage.py test --keepdb --parallel --shuffle"

test-coverage:
docker compose exec backend bash -c "coverage run manage.py test --settings=config.settings.test --keepdb --parallel"
docker compose exec backend bash -c "coverage run manage.py test --settings=config.settings.test --keepdb --parallel --shuffle"
docker compose exec backend bash -c "coverage combine || true; coverage xml"
docker compose cp backend:/app/coverage.xml coverage.xml
15 changes: 9 additions & 6 deletions care/facility/api/serializers/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@


class PatientMetaInfoSerializer(serializers.ModelSerializer):
occupation = ChoiceField(choices=PatientMetaInfo.OccupationChoices)
occupation = ChoiceField(choices=PatientMetaInfo.OccupationChoices, allow_null=True)

class Meta:
model = PatientMetaInfo
Expand Down Expand Up @@ -312,9 +312,8 @@ def create(self, validated_data):
Disease.objects.bulk_create(diseases, ignore_conflicts=True)

if meta_info:
meta_info_obj = PatientMetaInfo.objects.create(**meta_info)
patient.meta_info = meta_info_obj
patient.save()
patient.meta_info = PatientMetaInfo.objects.create(**meta_info)
patient.meta_info.save()

if contacted_patients:
contacted_patient_objs = [
Expand Down Expand Up @@ -361,8 +360,12 @@ def update(self, instance, validated_data):
Disease.objects.bulk_create(diseases, ignore_conflicts=True)

if meta_info:
for key, value in meta_info.items():
setattr(patient.meta_info, key, value)
if patient.meta_info is None:
meta_info_obj = PatientMetaInfo.objects.create(**meta_info)
patient.meta_info = meta_info_obj
else:
for key, value in meta_info.items():
setattr(patient.meta_info, key, value)
patient.meta_info.save()

if self.partial is not True: # clear the list and enter details if PUT
Expand Down
27 changes: 27 additions & 0 deletions care/facility/api/serializers/patient_consultation.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,10 +509,37 @@ def validate_encounter_date(self, value):
)
return value

def validate_patient_no(self, value):
if value is None:
return None
return value.strip()

def validate(self, attrs):
validated = super().validate(attrs)
# TODO Add Bed Authorisation Validation

if (
not self.instance
and "suggestion" in validated
and validated["suggestion"] == SuggestionChoices.A
):
patient_no = validated.get("patient_no")
if not patient_no:
raise ValidationError(
{"ip_no": ["This field is required for admission."]}
)
if PatientConsultation.objects.filter(
patient_no=patient_no,
facility=(
self.instance.facility
if self.instance
else validated.get("patient").facility
),
).exists():
raise ValidationError(
"Patient number must be unique within the facility."
)

if (
"suggestion" in validated
and validated["suggestion"] != SuggestionChoices.DD
Expand Down
13 changes: 11 additions & 2 deletions care/facility/api/viewsets/bed.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.core.exceptions import ValidationError as DjangoValidationError
from django.db import IntegrityError, transaction
from django.db.models import OuterRef, Subquery
from django.db.models import Exists, OuterRef, Subquery
from django_filters import rest_framework as filters
from drf_spectacular.utils import extend_schema, extend_schema_view
from rest_framework import filters as drf_filters
Expand Down Expand Up @@ -50,7 +50,7 @@ class BedViewSet(
):
queryset = (
Bed.objects.all()
.select_related("facility", "location")
.select_related("facility", "location", "location__facility")
.order_by("-created_date")
)
serializer_class = BedSerializer
Expand All @@ -63,6 +63,15 @@ class BedViewSet(
def get_queryset(self):
user = self.request.user
queryset = self.queryset

queryset = queryset.annotate(
is_occupied=Exists(
ConsultationBed.objects.filter(
bed__id=OuterRef("id"), end_date__isnull=True
)
)
)

if user.is_superuser:
pass
elif user.user_type >= User.TYPE_VALUE_MAP["StateLabAdmin"]:
Expand Down
42 changes: 40 additions & 2 deletions care/facility/api/viewsets/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.contrib.postgres.search import TrigramSimilarity
from django.db import models
from django.db.models import Case, OuterRef, Q, Subquery, When
from django.db.models.query import QuerySet
from django_filters import rest_framework as filters
from djqscsv import render_to_csv_response
from drf_spectacular.utils import extend_schema, extend_schema_view
Expand Down Expand Up @@ -272,10 +273,11 @@ def filter_queryset(self, request, queryset, view):
elif view.action != "transfer":
allowed_facilities = get_accessible_facilities(request.user)
q_filters = Q(facility__id__in=allowed_facilities)
if view.action == "retrieve":
q_filters |= Q(consultations__facility__id__in=allowed_facilities)
q_filters |= Q(last_consultation__assigned_to=request.user)
q_filters |= Q(assigned_to=request.user)
queryset = queryset.filter(q_filters)

return queryset

def filter_list_queryset(self, request, queryset, view):
Expand Down Expand Up @@ -312,7 +314,7 @@ def filter_queryset(self, request, queryset, view):
)
).order_by(ordering)

return queryset
return queryset.distinct(ordering.lstrip("-") if ordering else "id")


@extend_schema_view(history=extend_schema(tags=["patient"]))
Expand Down Expand Up @@ -509,6 +511,42 @@ def transfer(self, request, *args, **kwargs):
return Response(data=response_serializer.data, status=status.HTTP_200_OK)


class FacilityDischargedPatientFilterSet(filters.FilterSet):
name = filters.CharFilter(field_name="name", lookup_expr="icontains")


@extend_schema_view(tags=["patient"])
class FacilityDischargedPatientViewSet(GenericViewSet, mixins.ListModelMixin):
permission_classes = (IsAuthenticated, DRYPermissions)
lookup_field = "external_id"
serializer_class = PatientListSerializer
filter_backends = (filters.DjangoFilterBackend,)
filterset_class = FacilityDischargedPatientFilterSet
queryset = PatientRegistration.objects.select_related(
"local_body",
"district",
"state",
"ward",
"assigned_to",
"facility",
"facility__ward",
"facility__local_body",
"facility__district",
"facility__state",
"last_consultation",
"last_consultation__assigned_to",
"last_edited",
"created_by",
)

def get_queryset(self) -> QuerySet:
qs = super().get_queryset()
return qs.filter(
Q(consultations__facility__external_id=self.kwargs["facility_external_id"])
& Q(consultations__discharge_date__isnull=False)
).distinct()


class FacilityPatientStatsHistoryFilterSet(filters.FilterSet):
entry_date = filters.DateFromToRangeFilter(field_name="entry_date")

Expand Down
9 changes: 6 additions & 3 deletions care/facility/api/viewsets/patient_consultation.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,12 @@ def get_queryset(self):
patient__facility__district=self.request.user.district
)
allowed_facilities = get_accessible_facilities(self.request.user)
applied_filters = Q(patient__facility__id__in=allowed_facilities)
applied_filters |= Q(assigned_to=self.request.user)
applied_filters |= Q(patient__assigned_to=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 self.queryset.filter(applied_filters)

@extend_schema(tags=["consultation"])
Expand Down
5 changes: 3 additions & 2 deletions care/facility/api/viewsets/prescription.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from django.utils import timezone
from django_filters import rest_framework as filters
from drf_spectacular.utils import extend_schema
from dry_rest_permissions.generics import DRYPermissions
from redis_om import FindQuery
from rest_framework import mixins, status
from rest_framework.decorators import action
Expand Down Expand Up @@ -50,7 +51,7 @@ class MedicineAdministrationViewSet(
mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet
):
serializer_class = MedicineAdministrationSerializer
permission_classes = (IsAuthenticated,)
permission_classes = (IsAuthenticated, DRYPermissions)
queryset = MedicineAdministration.objects.all().order_by("-created_date")
lookup_field = "external_id"
filter_backends = (filters.DjangoFilterBackend,)
Expand Down Expand Up @@ -94,7 +95,7 @@ class ConsultationPrescriptionViewSet(
GenericViewSet,
):
serializer_class = PrescriptionSerializer
permission_classes = (IsAuthenticated,)
permission_classes = (IsAuthenticated, DRYPermissions)
queryset = Prescription.objects.all().order_by("-created_date")
lookup_field = "external_id"
filter_backends = (filters.DjangoFilterBackend,)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Generated by Django 4.2.10 on 2024-02-20 13:26

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("facility", "0414_remove_bed_old_name"),
]

operations = [
migrations.AlterField(
model_name="patientmetainfo",
name="head_of_household",
field=models.BooleanField(blank=True, null=True),
),
migrations.AlterField(
model_name="patientmetainfo",
name="occupation",
field=models.IntegerField(
blank=True,
choices=[
(1, "STUDENT"),
(2, "BUSINESSMAN"),
(3, "HEALTH_CARE_WORKER"),
(4, "HEALTH_CARE_LAB_WORKER"),
(5, "ANIMAL_HANDLER"),
(6, "OTHERS"),
],
null=True,
),
),
]
42 changes: 42 additions & 0 deletions care/facility/migrations/0415_auto_20240227_0130.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Generated by Django 4.2.10 on 2024-02-26 20:00

from django.db import migrations
from django.db.models import Count, F, Value, Window
from django.db.models.functions import RowNumber


def fix_duplicate_patient_numbers(apps, schema_editor):
PatientConsultation = apps.get_model("facility", "PatientConsultation")

PatientConsultation.objects.filter(patient_no__regex=r"^\s*$").update(
patient_no=None
)

window = Window(
expression=RowNumber(),
partition_by=[F("patient_no"), F("facility")],
order_by=F("id").asc(),
)

consultations = PatientConsultation.objects.annotate(row_number=window).filter(
row_number__gt=1, patient_no__isnull=False
)

for consultation in consultations:
consultation.patient_no = (
f"{consultation.patient_no}_{consultation.row_number - 1}"
)

PatientConsultation.objects.bulk_update(
consultations, ["patient_no"], batch_size=2000
)


class Migration(migrations.Migration):
dependencies = [
("facility", "0414_remove_bed_old_name"),
]

operations = [
migrations.RunPython(fix_duplicate_patient_numbers, migrations.RunPython.noop),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 4.2.10 on 2024-02-26 20:08

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("facility", "0415_auto_20240227_0130"),
]

operations = [
migrations.AddConstraint(
model_name="patientconsultation",
constraint=models.UniqueConstraint(
condition=models.Q(("patient_no__isnull", False)),
fields=("patient_no", "facility"),
name="unique_patient_no_within_facility",
),
),
]
30 changes: 30 additions & 0 deletions care/facility/migrations/0416_update_facility_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 4.2.10 on 2024-03-02 05:39

from django.db import migrations


def update_facility_types(apps, schema_editor):
Facility = apps.get_model("facility", "Facility")
facilities_to_update = {
801: 800, # 24x7 Public Health Centres to Primary Health Centres
820: 800, # Urban Primary Health Center to Primary Health Centres
831: 830, # Taluk Headquarters Hospitals to Taluk Hospitals
850: 860, # General hospitals to District Hospitals
900: 910, # Co-operative hospitals to Autonomous healthcare facility
950: 870, # Corona Testing Labs to Govt. Labs
1000: 3, # Corona Care Centre to Other
8: 870, # Govt Hospital to Govt Medical College Hospitals
}

for old_id, new_id in facilities_to_update.items():
Facility.objects.filter(facility_type=old_id).update(facility_type=new_id)


class Migration(migrations.Migration):
dependencies = [
("facility", "0415_alter_patientmetainfo_head_of_household_and_more"),
]

operations = [
migrations.RunPython(update_facility_types, migrations.RunPython.noop),
]
12 changes: 12 additions & 0 deletions care/facility/migrations/0417_merge_20240305_1805.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Generated by Django 4.2.10 on 2024-03-05 12:35

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("facility", "0416_patientconsultation_unique_patient_no_within_facility"),
("facility", "0416_update_facility_types"),
]

operations = []

0 comments on commit ff7d5de

Please sign in to comment.