Skip to content

Commit

Permalink
Make care single source of truth for asset config on middleware (#2000)
Browse files Browse the repository at this point in the history
* add routes for middleware to fetch asset config

* cache middleware jwks

* allow asset authentication for assetbed

* squash migrations

* fix key cache issue

* fix assetbed filter for middlewares

* return asset presets from patient_from_asset route itself

* add is_parsed_by_ocr flag to indicate ocr use for automated round

* improve regex match for hostname query

* squash migrations

* Discard changes to config/settings/local.py

* update migrations

* add permission classes to test views

* take jwks as kwarg for jwt generation

* split open id auth response for easier mocking

* test middleware auth

* remove indexes

* remove unnessary comments

* use request mocks to test openid auth

* make push config logic async

* improve query

* fix inconsistent validation

* fix signal

* Relock dependencies

* update migrations

---------

Co-authored-by: vigneshhari <vichuhari100@gmail.com>
  • Loading branch information
sainak and vigneshhari committed Apr 14, 2024
1 parent 9e02579 commit 87cd40c
Show file tree
Hide file tree
Showing 21 changed files with 623 additions and 62 deletions.
1 change: 1 addition & 0 deletions Pipfile
Expand Up @@ -64,6 +64,7 @@ ipython = "==8.15.0"
isort = "==5.12.0"
mypy = "==1.9.0"
pre-commit = "==3.4.0"
requests-mock = "==1.12.1"
tblib = "==2.0.0"
watchdog = "==3.0.0"
werkzeug = "==2.3.8"
Expand Down
51 changes: 30 additions & 21 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

90 changes: 86 additions & 4 deletions care/facility/api/serializers/asset.py
@@ -1,7 +1,9 @@
from datetime import datetime

from django.core.cache import cache
from django.db import transaction
from django.db import models, transaction
from django.db.models import F, Value
from django.db.models.functions import Cast, Coalesce, NullIf
from django.shortcuts import get_object_or_404
from django.utils.timezone import now
from drf_spectacular.utils import extend_schema_field
Expand Down Expand Up @@ -31,6 +33,7 @@
UserDefaultAssetLocation,
)
from care.users.api.serializers.user import UserBaseMinimumSerializer
from care.utils.assetintegration.asset_classes import AssetClasses
from care.utils.assetintegration.hl7monitor import HL7MonitorAsset
from care.utils.assetintegration.onvif import OnvifAsset
from care.utils.assetintegration.ventilator import VentilatorAsset
Expand Down Expand Up @@ -210,13 +213,58 @@ def validate(self, attrs):
):
raise ValidationError({"asset_class": "Cannot change asset class"})

if meta := attrs.get("meta"):
current_location = attrs.get(
"current_location", self.instance.current_location
)
ip_address = meta.get("local_ip_address")
middleware_hostname = (
meta.get("middleware_hostname")
or current_location.middleware_address
or current_location.facility.middleware_address
)
if ip_address and middleware_hostname:
asset_using_ip = (
Asset.objects.annotate(
resolved_middleware_hostname=Coalesce(
NullIf(
Cast(
F("meta__middleware_hostname"), models.CharField()
),
Value('""'),
),
NullIf(
F("current_location__middleware_address"), Value("")
),
F("current_location__facility__middleware_address"),
output_field=models.CharField(),
)
)
.filter(
asset_class__in=[
AssetClasses.ONVIF.name,
AssetClasses.HL7MONITOR.name,
],
current_location__facility=current_location.facility_id,
resolved_middleware_hostname=middleware_hostname,
meta__local_ip_address=ip_address,
)
.exclude(id=self.instance.id if self.instance else None)
.only("name")
.first()
)
if asset_using_ip:
raise ValidationError(
f"IP Address {ip_address} is already in use by {asset_using_ip.name} asset"
)

return super().validate(attrs)

def create(self, validated_data):
last_serviced_on = validated_data.pop("last_serviced_on", None)
note = validated_data.pop("note", None)
with transaction.atomic():
asset_instance = super().create(validated_data)
asset_instance: Asset = super().create(validated_data)
if last_serviced_on or note:
asset_service = AssetService(
asset=asset_instance, serviced_on=last_serviced_on, note=note
Expand All @@ -226,7 +274,7 @@ def create(self, validated_data):
asset_instance.save(update_fields=["last_service"])
return asset_instance

def update(self, instance, validated_data):
def update(self, instance: Asset, validated_data):
user = self.context["request"].user
with transaction.atomic():
if validated_data.get("last_serviced_on") and (
Expand Down Expand Up @@ -271,11 +319,45 @@ def update(self, instance, validated_data):
asset=instance,
performed_by=user,
).save()
updated_instance = super().update(instance, validated_data)
updated_instance: Asset = super().update(instance, validated_data)
cache.delete(f"asset:{instance.external_id}")
return updated_instance


class AssetConfigSerializer(ModelSerializer):
id = UUIDField(source="external_id")
type = CharField(source="asset_class")
description = CharField(default="")
ip_address = CharField(default="")
access_key = CharField(default="")
username = CharField(default="")
password = CharField(default="")
port = serializers.IntegerField(default=80)

def to_representation(self, instance: Asset):
data = super().to_representation(instance)
data["ip_address"] = instance.meta.get("local_ip_address")
if camera_access_key := instance.meta.get("camera_access_key"):
values = camera_access_key.split(":")
if len(values) == 3:
data["username"], data["password"], data["access_key"] = values
return data

class Meta:
model = Asset
fields = (
"id",
"name",
"type",
"description",
"ip_address",
"access_key",
"username",
"password",
"port",
)


class AssetTransactionSerializer(ModelSerializer):
id = UUIDField(source="external_id", read_only=True)
asset = AssetBareMinimumSerializer(read_only=True)
Expand Down
17 changes: 10 additions & 7 deletions care/facility/api/serializers/patient_consultation.py
Expand Up @@ -10,7 +10,10 @@
from care.abdm.utils.api_call import AbdmGateway
from care.facility.api.serializers import TIMESTAMP_FIELDS
from care.facility.api.serializers.asset import AssetLocationSerializer
from care.facility.api.serializers.bed import ConsultationBedSerializer
from care.facility.api.serializers.bed import (
AssetBedSerializer,
ConsultationBedSerializer,
)
from care.facility.api.serializers.consultation_diagnosis import (
ConsultationCreateDiagnosisSerializer,
ConsultationDiagnosisSerializer,
Expand Down Expand Up @@ -765,14 +768,14 @@ def create(self, validated_data):
raise NotImplementedError


class PatientConsultationIDSerializer(serializers.ModelSerializer):
consultation_id = serializers.UUIDField(source="external_id", read_only=True)
patient_id = serializers.UUIDField(source="patient.external_id", read_only=True)
bed_id = serializers.UUIDField(source="current_bed.bed.external_id", read_only=True)
class PatientConsultationIDSerializer(serializers.Serializer):
consultation_id = serializers.UUIDField(read_only=True)
patient_id = serializers.UUIDField(read_only=True)
bed_id = serializers.UUIDField(read_only=True)
asset_beds = AssetBedSerializer(many=True, read_only=True)

class Meta:
model = PatientConsultation
fields = ("consultation_id", "patient_id", "bed_id")
fields = ("consultation_id", "patient_id", "bed_id", "asset_beds")


class EmailDischargeSummarySerializer(serializers.Serializer):
Expand Down

0 comments on commit 87cd40c

Please sign in to comment.