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

Make care single source of truth for asset config on middleware #2000

Merged
merged 30 commits into from
Apr 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a2e9ac2
add routes for middleware to fetch asset config
sainak Mar 21, 2024
c3b47fe
cache middleware jwks
sainak Mar 21, 2024
b6b386b
allow asset authentication for assetbed
sainak Mar 21, 2024
e394f3b
squash migrations
sainak Mar 21, 2024
90779c6
fix key cache issue
sainak Mar 25, 2024
1070ee9
fix assetbed filter for middlewares
sainak Mar 25, 2024
faf6ad0
return asset presets from patient_from_asset route itself
sainak Mar 25, 2024
53159c5
add is_parsed_by_ocr flag to indicate ocr use for automated round
sainak Mar 25, 2024
cd992b7
improve regex match for hostname query
sainak Mar 25, 2024
9d37cac
Merge remote-tracking branch 'origin/develop' into sainak/feat/middle…
sainak Mar 25, 2024
b4ea44c
squash migrations
sainak Mar 25, 2024
01dd94c
Discard changes to config/settings/local.py
sainak Mar 25, 2024
76d2754
Merge remote-tracking branch 'origin/develop' into sainak/feat/middle…
sainak Apr 5, 2024
989c770
update migrations
sainak Apr 5, 2024
41f5920
Merge remote-tracking branch 'origin/develop' into sainak/feat/middle…
sainak Apr 8, 2024
1457312
add permission classes to test views
sainak Apr 8, 2024
ac92d0f
take jwks as kwarg for jwt generation
sainak Apr 8, 2024
567f2de
split open id auth response for easier mocking
sainak Apr 8, 2024
81597d9
test middleware auth
sainak Apr 8, 2024
87364d6
remove indexes
sainak Apr 9, 2024
b873d18
remove unnessary comments
sainak Apr 9, 2024
7f5270e
use request mocks to test openid auth
sainak Apr 10, 2024
5aa499d
make push config logic async
sainak Apr 10, 2024
7855ae3
improve query
sainak Apr 10, 2024
e682454
fix inconsistent validation
sainak Apr 10, 2024
47b41d4
Merge remote-tracking branch 'origin/develop' into sainak/feat/middle…
sainak Apr 10, 2024
0df52b4
fix signal
sainak Apr 10, 2024
a14e86c
Merge Develop
vigneshhari Apr 14, 2024
1d9f718
Relock dependencies
vigneshhari Apr 14, 2024
2044e57
update migrations
sainak Apr 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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