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

Live Camera Feed Ehnahcement(Review, QA and testing) #1576

Closed
wants to merge 49 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
eacf4e6
rlative move validation
JahnabDutta Jul 9, 2023
2250c4c
camera boundary minor changes
JahnabDutta Jul 11, 2023
565e29d
Merge branch 'master' into add-boundary-preset
JahnabDutta Jul 11, 2023
4f0f3d6
move Boundary based validation from AssetViewset to OnvifAsset
JahnabDutta Jul 14, 2023
3926591
Merge branch 'master' into add-boundary-preset
JahnabDutta Jul 14, 2023
d4c39c5
Merge branch 'coronasafe:master' into add-boundary-preset
JahnabDutta Jul 17, 2023
f8d3d00
add test for operate_asset for onvif assets
JahnabDutta Jul 17, 2023
be69d00
add test skeletons for other assets
JahnabDutta Jul 17, 2023
cf29b9b
Merge branch 'master' into add-boundary-preset
JahnabDutta Jul 19, 2023
fcb38fe
Merge branch 'coronasafe:master' into add-boundary-preset
JahnabDutta Jul 21, 2023
addfd1c
initial commit
JahnabDutta Jul 21, 2023
91783b0
add locking mechanism to asset
JahnabDutta Jul 21, 2023
04fa29f
verify locking only for movement actions
JahnabDutta Jul 22, 2023
3708d12
Merge branch 'coronasafe:master' into add-boundary-preset
JahnabDutta Jul 29, 2023
fef448d
Merge branch 'coronasafe:master' into lock-asset-feature
JahnabDutta Jul 29, 2023
11b772a
add lock action
JahnabDutta Jul 29, 2023
63961a2
Merge branch 'lock-asset-feature' of https://github.com/JahnabDutta/c…
JahnabDutta Jul 29, 2023
6b3b501
add privacy field in consultationbed model and toggle_patient_privacy…
JahnabDutta Aug 7, 2023
b16e4fc
merge conflicts
JahnabDutta Aug 7, 2023
745c99a
change privacy toggle privildeges
JahnabDutta Aug 8, 2023
6635b94
Merge branch 'coronasafe:master' into add-boundary-preset
JahnabDutta Aug 9, 2023
e2422bd
Merge branch 'coronasafe:master' into lock-asset-feature
JahnabDutta Aug 9, 2023
4973918
Merge branch 'coronasafe:master' into patient-privacy-feature
JahnabDutta Aug 9, 2023
a6763e2
Merge branch 'patient-privacy-feature' of https://github.com/JahnabDu…
JahnabDutta Aug 9, 2023
3211244
patient privacy tests
JahnabDutta Aug 10, 2023
b077553
change functions for lock/unlock asset
JahnabDutta Aug 13, 2023
abb0c3f
add push notification
JahnabDutta Aug 13, 2023
eab6aaf
add asset_unlock in notification event type
JahnabDutta Aug 13, 2023
2f000fd
use send_webpush instead of new class
JahnabDutta Aug 15, 2023
2ee66d3
send current asset occupier information to user
JahnabDutta Aug 21, 2023
0bc2fb4
Merge branch 'coronasafe:master' into add-boundary-preset
JahnabDutta Aug 21, 2023
9f8a05d
Merge branch 'coronasafe:master' into patient-privacy-feature
JahnabDutta Aug 21, 2023
1b17b1d
Merge branch 'coronasafe:master' into lock-asset-feature
JahnabDutta Aug 21, 2023
9093bdf
Merge branch 'lock-asset-feature' of https://github.com/JahnabDutta/c…
JahnabDutta Aug 21, 2023
88934c9
add lock asset test
JahnabDutta Aug 22, 2023
95f20d6
Merge branch 'master' into add-boundary-preset
JahnabDutta Aug 23, 2023
7289938
replace boolean result with validation error
JahnabDutta Aug 23, 2023
60b6478
Merge branch 'add-boundary-preset' of https://github.com/JahnabDutta/…
JahnabDutta Aug 23, 2023
521606d
Merge branch 'coronasafe:master' into patient-privacy-feature
JahnabDutta Aug 23, 2023
f255045
Merge branch 'add-boundary-preset' into patient-privacy-feature
JahnabDutta Aug 23, 2023
4215840
raise permission error in validate action
JahnabDutta Aug 23, 2023
862cebc
add request asset feature
JahnabDutta Aug 25, 2023
1964c5a
add migration
JahnabDutta Aug 27, 2023
01c15a2
change locking feature
JahnabDutta Aug 27, 2023
9950a6c
Merge branch 'lock-asset-feature' into qa-testing
JahnabDutta Sep 5, 2023
815239d
Merge branch 'coronasafe:master' into qa-testing
JahnabDutta Sep 5, 2023
38f2c57
new migration to avoid conflict
JahnabDutta Sep 5, 2023
011322a
change testing criteria to handle middleware server failures
JahnabDutta Sep 5, 2023
ae969a9
Merge branch 'master' into qa-testing
sainak Sep 11, 2023
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
23 changes: 21 additions & 2 deletions care/facility/api/viewsets/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from rest_framework import filters as drf_filters
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.exceptions import APIException, ValidationError
from rest_framework.exceptions import APIException, PermissionDenied, ValidationError
from rest_framework.mixins import (
CreateModelMixin,
DestroyModelMixin,
Expand Down Expand Up @@ -282,9 +282,28 @@ def operate_assets(self, request, *args, **kwargs):
"middleware_hostname": asset.current_location.facility.middleware_address,
}
)
result = asset_class.handle_action(action)
asset_class.validate_action(action)
result = asset_class.handle_action(
action,
{
"username": request.user.username,
"asset_id": asset.external_id,
},
)
return Response({"result": result}, status=status.HTTP_200_OK)

except PermissionDenied as e:
return Response(
{
"message": e.detail.get("message", None),
"username": e.detail.get("username", None),
"firstName": e.detail.get("firstName", None),
"lastName": e.detail.get("lastName", None),
"role": e.detail.get("role", None),
"homeFacility": e.detail.get("homeFacility", None),
},
status=status.HTTP_409_CONFLICT,
)
except ValidationError as e:
return Response({"message": e.detail}, status=status.HTTP_400_BAD_REQUEST)

Expand Down
39 changes: 39 additions & 0 deletions care/facility/api/viewsets/bed.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from drf_spectacular.utils import extend_schema, extend_schema_view
from rest_framework import filters as drf_filters
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied
from rest_framework.exceptions import ValidationError as DRFValidationError
from rest_framework.fields import get_error_detail
Expand Down Expand Up @@ -232,3 +233,41 @@ def get_queryset(self):
allowed_facilities = get_accessible_facilities(user)
queryset = queryset.filter(bed__facility__id__in=allowed_facilities)
return queryset

@extend_schema(
description="Toggle patient privacy",
responses={status.HTTP_200_OK: None},
request=None,
tags=["consultationbed"],
)
@action(detail=True, methods=["PATCH"])
def toggle_patient_privacy(self, request, *args, **kwargs):
try:
user: User = request.user
if (
user.user_type == User.TYPE_VALUE_MAP["WardAdmin"]
or user.user_type == User.TYPE_VALUE_MAP["LocalBodyAdmin"]
or user.user_type == User.TYPE_VALUE_MAP["DistrictAdmin"]
or user.user_type == User.TYPE_VALUE_MAP["StateAdmin"]
or (
user.user_type == User.TYPE_VALUE_MAP["Doctor"]
and user.home_facility.external_id
== self.get_object().bed.facility.external_id
)
or (
user.user_type == User.TYPE_VALUE_MAP["Staff"]
and user.home_facility.external_id
== self.get_object().bed.facility.external_id
)
):
consultation_bed: ConsultationBed = self.get_object()
consultation_bed.privacy = not consultation_bed.privacy
consultation_bed.save()
return Response({"status": "success"}, status=status.HTTP_200_OK)
raise PermissionDenied(
detail="You do not have permission to perform this action"
)
except PermissionDenied as e:
return Response({"message": e.detail}, status=status.HTTP_403_FORBIDDEN)
except Exception as e:
return Response({"message": e}, status=status.HTTP_400_BAD_REQUEST)
17 changes: 17 additions & 0 deletions care/facility/migrations/0379_consultationbed_privacy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.2 on 2023-08-27 15:03

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("facility", "0378_consultationbedasset_consultationbed_assets"),
]

operations = [
migrations.AddField(
model_name="consultationbed",
name="privacy",
field=models.BooleanField(default=False),
),
]
12 changes: 12 additions & 0 deletions care/facility/migrations/0383_merge_20230905_1213.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Generated by Django 4.2.2 on 2023-09-05 06:43

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("facility", "0379_consultationbed_privacy"),
("facility", "0382_assetservice_remove_asset_last_serviced_on_and_more"),
]

operations = []
1 change: 1 addition & 0 deletions care/facility/models/bed.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class ConsultationBed(BaseModel):
bed = models.ForeignKey(Bed, on_delete=models.PROTECT, null=False, blank=False)
start_date = models.DateTimeField(null=False, blank=False)
end_date = models.DateTimeField(null=True, blank=True, default=None)
privacy = models.BooleanField(default=False)
meta = JSONField(default=dict, blank=True)
assets = models.ManyToManyField(
Asset, through="ConsultationBedAsset", related_name="assigned_consultation_beds"
Expand Down
1 change: 1 addition & 0 deletions care/facility/models/notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class Medium(enum.Enum):

class Event(enum.Enum):
MESSAGE = 0
ASSET_UNLOCKED = 10
PATIENT_CREATED = 20
PATIENT_UPDATED = 30
PATIENT_DELETED = 40
Expand Down
198 changes: 198 additions & 0 deletions care/facility/tests/test_asset_operate_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
from rest_framework import status
from rest_framework.test import APIRequestFactory, APITestCase

from care.facility.api.viewsets.asset import AssetViewSet
from care.facility.models import Asset, AssetBed, AssetLocation, Bed, FacilityUser
from care.facility.tests.mixins import TestClassMixin
from care.utils.tests.test_base import TestBase


class AssetViewSetTestCase(TestBase, TestClassMixin, APITestCase):
asset_id = None

def setUp(self):
self.factory = APIRequestFactory()
state = self.create_state()
district = self.create_district(state=state)
self.user = self.create_user(district=district, username="test user")
self.user_2 = self.create_user(district=district, username="test user 2")
facility = self.create_facility(district=district, user=self.user)
self.facility_user = FacilityUser.objects.create(
user=self.user_2, facility=facility, created_by=self.user
)
self.asset1_location = AssetLocation.objects.create(
name="asset1 location", location_type=1, facility=facility
)

# depends upon the operational dev camera config
self.onvif_meta = {
"asset_type": "CAMERA",
"local_ip_address": "192.168.1.64",
"camera_access_key": "remote_user:2jCkrCRSeahzKEU:d5694af2-21e2-4a39-9bad-2fb98d9818bd",
"middleware_hostname": "dev_middleware.coronasafe.live",
}
self.hl7monitor_meta = {}
self.ventilator_meta = {}
self.bed = Bed.objects.create(
name="Test Bed",
facility=facility,
location=self.asset1_location,
meta={},
bed_type=1,
)
self.asset: Asset = Asset.objects.create(
name="Test Asset",
current_location=self.asset1_location,
asset_type=50,
)

def test_onvif_relative_move(self):
self.asset.asset_class = "ONVIF"
self.asset.meta = self.onvif_meta
self.asset.save()
boundary_asset_bed = AssetBed.objects.create(
asset=self.asset,
bed=self.bed,
meta={
"range": {
"max_x": 2,
"min_x": -2,
"max_y": 2,
"min_y": -2,
}
},
)
sample_data = {
"action": {
"type": "relative_move",
"data": {
"x": 0.1,
"y": 0.1,
"zoom": 0.1,
"camera_state": {"x": 1.5, "y": 1.5, "zoom": 0},
"id": boundary_asset_bed.external_id,
},
}
}
response = self.new_request(
(
f"/api/v1/asset/{self.asset.external_id}/operate_assets/",
sample_data,
"json",
),
{"post": "operate_assets"},
AssetViewSet,
self.user,
{"external_id": self.asset.external_id},
)
self.assertIn(response.status_code, [200, 500])
sample_data_invald = {
"action": {
"type": "relative_move",
"data": {
"x": 0.6,
"y": 0.1,
"zoom": 0.1,
"camera_state": {"x": 1.5, "y": 1.5, "zoom": 0},
"id": boundary_asset_bed.external_id,
},
}
}
response_invalid = self.new_request(
(
f"/api/v1/asset/{self.asset.external_id}/operate_assets/",
sample_data_invald,
"json",
),
{"post": "operate_assets"},
AssetViewSet,
self.user,
{"external_id": self.asset.external_id},
)

self.assertEqual(response_invalid.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
response_invalid.data.get("message", {}).get("action", {}).code, "invalid"
)

def test_hl7monitor(self):
self.asset.asset_class = "HL7MONITOR"
self.asset.meta = self.hl7monitor_meta
self.asset.save()
pass

def test_ventilator(self):
self.asset.asset_class = "VENTILATOR"
self.asset.meta = self.ventilator_meta
self.asset.save()
pass

def test_lock_asset(self):
self.asset.asset_class = "ONVIF"
self.asset.meta = self.onvif_meta
self.asset.save()
sample_data_lock = {
"action": {
"type": "lock_asset",
"data": {},
}
}
sample_data_unlock = {
"action": {
"type": "unlock_asset",
"data": {},
}
}
response = self.new_request(
(
f"/api/v1/asset/{self.asset.external_id}/operate_assets/",
sample_data_lock,
"json",
),
{"post": "operate_assets"},
AssetViewSet,
self.user,
{"external_id": self.asset.external_id},
)

self.assertEqual(response.status_code, status.HTTP_200_OK)

response = self.new_request(
(
f"/api/v1/asset/{self.asset.external_id}/operate_assets/",
sample_data_unlock,
"json",
),
{"post": "operate_assets"},
AssetViewSet,
self.user_2,
{"external_id": self.asset.external_id},
)
self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)

response = self.new_request(
(
f"/api/v1/asset/{self.asset.external_id}/operate_assets/",
sample_data_unlock,
"json",
),
{"post": "operate_assets"},
AssetViewSet,
self.user,
{"external_id": self.asset.external_id},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)

response = self.new_request(
(
f"/api/v1/asset/{self.asset.external_id}/operate_assets/",
sample_data_lock,
"json",
),
{"post": "operate_assets"},
AssetViewSet,
self.user_2,
{"external_id": self.asset.external_id},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.asset.delete()
Loading
Loading