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

Production Release v24.22.0 #2195

Merged
merged 8 commits into from
May 27, 2024
24 changes: 0 additions & 24 deletions care/abdm/api/viewsets/consent.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,30 +140,6 @@ def fetch(self, request, pk):
ConsentRequestSerializer(consent).data, status=status.HTTP_200_OK
)

def list(self, request, *args, **kwargs):
if ratelimit(request, "consent__list", [request.user.username]):
raise CaptchaRequiredException(
detail={
"status": 429,
"detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}",
},
code=status.HTTP_429_TOO_MANY_REQUESTS,
)

return super().list(request, *args, **kwargs)

def retrieve(self, request, *args, **kwargs):
if ratelimit(request, "consent__retrieve", [kwargs["pk"]]):
raise CaptchaRequiredException(
detail={
"status": 429,
"detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}",
},
code=status.HTTP_429_TOO_MANY_REQUESTS,
)

return super().retrieve(request, *args, **kwargs)


class ConsentCallbackViewSet(GenericViewSet):
permission_classes = (IsAuthenticated,)
Expand Down
9 changes: 0 additions & 9 deletions care/abdm/api/viewsets/health_information.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,6 @@ class HealthInformationViewSet(GenericViewSet):
permission_classes = (IsAuthenticated,)

def retrieve(self, request, pk):
if ratelimit(request, "health_information__retrieve", [pk]):
raise CaptchaRequiredException(
detail={
"status": 429,
"detail": f"Request limit reached. Try after {USER_READABLE_RATE_LIMIT_TIME}",
},
code=status.HTTP_429_TOO_MANY_REQUESTS,
)

files = FileUpload.objects.filter(
Q(internal_name=f"{pk}.json") | Q(associating_id=pk),
file_type=FileUpload.FileType.ABDM_HEALTH_INFORMATION.value,
Expand Down
15 changes: 15 additions & 0 deletions care/facility/api/viewsets/bed.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,21 @@ class BedFilter(filters.FilterSet):
facility = filters.UUIDFilter(field_name="facility__external_id")
location = filters.UUIDFilter(field_name="location__external_id")
bed_type = CareChoiceFilter(choice_dict=inverse_bed_type)
not_occupied_by_asset_type = filters.CharFilter(
method="filter_bed_is_not_occupied_by_asset_type"
)

def filter_bed_is_not_occupied_by_asset_type(self, queryset, name, value):
if value:
return queryset.filter(
~Exists(
AssetBed.objects.filter(
bed__id=OuterRef("id"),
asset__asset_class=value,
)
)
)
return queryset


class BedViewSet(
Expand Down
65 changes: 15 additions & 50 deletions care/facility/api/viewsets/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -602,55 +602,6 @@ def transfer(self, request, *args, **kwargs):
return Response(data=response_serializer.data, status=status.HTTP_200_OK)


class FacilityDischargedPatientFilterSet(filters.FilterSet):
disease_status = CareChoiceFilter(choice_dict=DISEASE_STATUS_DICT)
phone_number = filters.CharFilter(field_name="phone_number")
emergency_phone_number = filters.CharFilter(field_name="emergency_phone_number")
name = filters.CharFilter(field_name="name", lookup_expr="icontains")
gender = filters.NumberFilter(field_name="gender")
age = filters.NumberFilter(field_name="age")
age_min = filters.NumberFilter(field_name="age", lookup_expr="gte")
age_max = filters.NumberFilter(field_name="age", lookup_expr="lte")
created_date = filters.DateFromToRangeFilter(field_name="created_date")
modified_date = filters.DateFromToRangeFilter(field_name="modified_date")
srf_id = filters.CharFilter(field_name="srf_id")
is_declared_positive = filters.BooleanFilter(field_name="is_declared_positive")
date_declared_positive = filters.DateFromToRangeFilter(
field_name="date_declared_positive"
)
date_of_result = filters.DateFromToRangeFilter(field_name="date_of_result")
last_vaccinated_date = filters.DateFromToRangeFilter(
field_name="last_vaccinated_date"
)
is_antenatal = filters.BooleanFilter(field_name="is_antenatal")
last_menstruation_start_date = filters.DateFromToRangeFilter(
field_name="last_menstruation_start_date"
)
date_of_delivery = filters.DateFromToRangeFilter(field_name="date_of_delivery")
# Location Based Filtering
district = filters.NumberFilter(field_name="district__id")
district_name = filters.CharFilter(
field_name="district__name", lookup_expr="icontains"
)
local_body = filters.NumberFilter(field_name="local_body__id")
local_body_name = filters.CharFilter(
field_name="local_body__name", lookup_expr="icontains"
)
state = filters.NumberFilter(field_name="state__id")
state_name = filters.CharFilter(field_name="state__name", lookup_expr="icontains")
# Vaccination Filters
covin_id = filters.CharFilter(field_name="covin_id")
is_vaccinated = filters.BooleanFilter(field_name="is_vaccinated")
number_of_doses = filters.NumberFilter(field_name="number_of_doses")
last_consultation__new_discharge_reason = filters.ChoiceFilter(
field_name="last_consultation__new_discharge_reason",
choices=NewDischargeReasonEnum.choices,
)
last_consultation_discharge_date = filters.DateFromToRangeFilter(
field_name="last_consultation__discharge_date"
)


@extend_schema_view(tags=["patient"])
class FacilityDischargedPatientViewSet(GenericViewSet, mixins.ListModelMixin):
permission_classes = (IsAuthenticated, DRYPermissions)
Expand All @@ -661,7 +612,7 @@ class FacilityDischargedPatientViewSet(GenericViewSet, mixins.ListModelMixin):
rest_framework_filters.OrderingFilter,
PatientCustomOrderingFilter,
)
filterset_class = FacilityDischargedPatientFilterSet
filterset_class = PatientFilterSet
queryset = (
PatientRegistration.objects.select_related(
"local_body",
Expand Down Expand Up @@ -714,11 +665,25 @@ class FacilityDischargedPatientViewSet(GenericViewSet, mixins.ListModelMixin):
)
)

date_range_fields = [
"created_date",
"modified_date",
"date_declared_positive",
"date_of_result",
"last_vaccinated_date",
"last_consultation_encounter_date",
"last_consultation_discharge_date",
"last_consultation_symptoms_onset_date",
]

ordering_fields = [
"id",
"name",
"created_date",
"modified_date",
"review_time",
"last_consultation__current_bed__bed__name",
"date_declared_positive",
]

def get_queryset(self) -> QuerySet:
Expand Down
10 changes: 8 additions & 2 deletions care/facility/management/commands/load_event_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,14 @@ class Command(BaseCommand):
"fields": ("course_in_facility",),
},
{
"name": "TREATING_PHYSICIAN",
"fields": ("treating_physician",),
"name": "INVESTIGATION",
"fields": ("investigation",),
},
# disabling until we have a better way to serialize user objects
# {
# "name": "TREATING_PHYSICIAN",
# "fields": ("treating_physician",),
# },
),
},
{
Expand Down Expand Up @@ -240,6 +245,7 @@ class Command(BaseCommand):
"RESPIRATORY",
"INTAKE_OUTPUT",
"VENTILATOR_MODES",
"TREATING_PHYSICIAN",
)

def create_objects(
Expand Down
27 changes: 27 additions & 0 deletions care/facility/migrations/0437_alter_dailyround_rounds_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 4.2.8 on 2024-05-17 04:55

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("facility", "0436_remove_dailyround_temperature_measured_at"),
]

operations = [
migrations.AlterField(
model_name="dailyround",
name="rounds_type",
field=models.IntegerField(
choices=[
(0, "NORMAL"),
(50, "DOCTORS_LOG"),
(100, "VENTILATOR"),
(200, "ICU"),
(300, "AUTOMATED"),
(400, "TELEMEDICINE"),
],
default=0,
),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Generated by Django 4.2.10 on 2024-05-21 12:29

from django.db import migrations, models


class Migration(migrations.Migration):
def rename_categories_in_events(apps, schema_editor):
PatientConsultationEvent = apps.get_model(
"facility", "PatientConsultationEvent"
)

PatientConsultationEvent.objects.filter(
event_type__name="CATEGORY", value__category="Stable"
).update(value={"category": "Mild"})
PatientConsultationEvent.objects.filter(
event_type__name="PATIENT_CATEGORY", value__category="Stable"
).update(value={"patient_category": "Mild"})
PatientConsultationEvent.objects.filter(
event_type__name="CATEGORY", value__category="Abnormal"
).update(value={"category": "Moderate"})
PatientConsultationEvent.objects.filter(
event_type__name="PATIENT_CATEGORY", value__category="Abnormal"
).update(value={"patient_category": "Moderate"})

dependencies = [
("facility", "0437_alter_dailyround_rounds_type"),
]

operations = [
migrations.AlterField(
model_name="dailyround",
name="patient_category",
field=models.CharField(
choices=[
("Comfort", "Comfort Care"),
("Stable", "Mild"),
("Moderate", "Moderate"),
("Critical", "Critical"),
],
max_length=8,
null=True,
),
),
migrations.AlterField(
model_name="patientconsultation",
name="category",
field=models.CharField(
choices=[
("Comfort", "Comfort Care"),
("Stable", "Mild"),
("Moderate", "Moderate"),
("Critical", "Critical"),
],
max_length=8,
null=True,
),
),
]
1 change: 1 addition & 0 deletions care/facility/models/daily_round.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
class DailyRound(PatientBaseModel):
class RoundsType(enum.Enum):
NORMAL = 0
DOCTORS_LOG = 50
VENTILATOR = 100
ICU = 200
AUTOMATED = 300
Expand Down
4 changes: 2 additions & 2 deletions care/facility/models/patient_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ def reverse_choices(choices):

CATEGORY_CHOICES = [
("Comfort", "Comfort Care"),
("Stable", "Stable"),
("Moderate", "Abnormal"),
("Stable", "Mild"),
("Moderate", "Moderate"),
("Critical", "Critical"),
]

Expand Down
8 changes: 4 additions & 4 deletions care/facility/tasks/asset_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ def check_asset_status():
)
else:
result = asset_class.api_get(asset_class.get_url("devices/status"))
except Exception:
logger.warn(f"Middleware {resolved_middleware} is down", exc_info=True)
except Exception as e:
logger.warn(f"Middleware {resolved_middleware} is down", e)

# If no status is returned, setting default status as down
if not result or "error" in result:
Expand Down Expand Up @@ -116,5 +116,5 @@ def check_asset_status():
status=new_status.value,
timestamp=status_record.get("time", timezone.now()),
)
except Exception:
logger.error("Error in Asset Status Check", exc_info=True)
except Exception as e:
logger.error("Error in Asset Status Check", e)
8 changes: 4 additions & 4 deletions care/facility/tasks/location_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ def check_location_status():
if result:
new_status = AvailabilityStatus.OPERATIONAL

except Exception:
logger.warn(f"Middleware {resolved_middleware} is down", exc_info=True)
except Exception as e:
logger.warn(f"Middleware {resolved_middleware} is down", e)

# Fetching the last record of the location
last_record = (
Expand All @@ -75,5 +75,5 @@ def check_location_status():
timestamp=timezone.now(),
)
logger.info(f"Location {location.external_id} status: {new_status.value}")
except Exception:
logger.error("Error in Location Status Check", exc_info=True)
except Exception as e:
logger.error("Error in Location Status Check", e)
28 changes: 28 additions & 0 deletions care/facility/tests/test_bed_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from rest_framework.test import APITestCase

from care.facility.models import Bed
from care.facility.models.bed import AssetBed
from care.utils.assetintegration.asset_classes import AssetClasses
from care.utils.tests.test_utils import TestUtils


Expand Down Expand Up @@ -36,3 +38,29 @@ def test_list_beds(self):
with self.assertNumQueries(5):
response = self.client.get("/api/v1/bed/")
self.assertEqual(response.status_code, status.HTTP_200_OK)

def test_list_non_occupied_beds(self):
linked_bed = Bed.objects.create(
name="linked_bed",
location=self.asset_location,
facility=self.facility,
)
asset = self.create_asset(
self.asset_location, asset_class=AssetClasses.HL7MONITOR.name
)
AssetBed.objects.create(bed=linked_bed, asset=asset)

# 4 beds 1 linked with HL7MONITOR and 3 created in setup

response = self.client.get("/api/v1/bed/")

# Assert list returns 4 beds
self.assertEqual(response.json()["count"], 4)

response_with_not_occupied_bed = self.client.get(
"/api/v1/bed/",
{"not_occupied_by_asset_type": "HL7MONITOR"},
)

# Assert count of unoccupied beds is 3
self.assertEqual(response_with_not_occupied_bed.json()["count"], 3)
7 changes: 7 additions & 0 deletions care/facility/tests/test_patient_daily_rounds_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,10 @@ def test_log_update_without_bed_for_domiciliary(
format="json",
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

def test_doctors_log_update(self):
response = self.client.post(
f"/api/v1/consultation/{self.consultation_with_bed.external_id}/daily_rounds/",
data={**self.log_update, "rounds_type": "DOCTORS_LOG"},
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
Loading