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.22.0 #2191

Merged
merged 7 commits into from
May 23, 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 @@
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

Check warning on line 55 in care/facility/api/viewsets/bed.py

View check run for this annotation

Codecov / codecov/patch

care/facility/api/viewsets/bed.py#L55

Added line #L55 was not covered by tests


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 @@
)
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)

Check warning on line 73 in care/facility/tasks/asset_monitor.py

View check run for this annotation

Codecov / codecov/patch

care/facility/tasks/asset_monitor.py#L72-L73

Added lines #L72 - L73 were not covered by tests

# 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 @@
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)

Check warning on line 120 in care/facility/tasks/asset_monitor.py

View check run for this annotation

Codecov / codecov/patch

care/facility/tasks/asset_monitor.py#L119-L120

Added lines #L119 - L120 were not covered by tests
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 @@
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)

Check warning on line 57 in care/facility/tasks/location_monitor.py

View check run for this annotation

Codecov / codecov/patch

care/facility/tasks/location_monitor.py#L56-L57

Added lines #L56 - L57 were not covered by tests

# Fetching the last record of the location
last_record = (
Expand All @@ -75,5 +75,5 @@
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)

Check warning on line 79 in care/facility/tasks/location_monitor.py

View check run for this annotation

Codecov / codecov/patch

care/facility/tasks/location_monitor.py#L78-L79

Added lines #L78 - L79 were not covered by tests
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