Skip to content

Commit

Permalink
Added support to map care facilities with abdm health facility (#1550)
Browse files Browse the repository at this point in the history
* added health facility in abdm

* link health facility on create and update

* get hip_id from health facility

* added authorization

* use external_id instead of id

---------

Co-authored-by: Vignesh Hari <[email protected]>
  • Loading branch information
khavinshankar and vigneshhari authored Aug 25, 2023
1 parent df1244b commit 1ece5cc
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 9 deletions.
11 changes: 11 additions & 0 deletions care/abdm/api/serializers/health_facility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from rest_framework import serializers

from care.abdm.models import HealthFacility


class HealthFacilitySerializer(serializers.ModelSerializer):
id = serializers.CharField(source="external_id", read_only=True)

class Meta:
model = HealthFacility
exclude = ("deleted",)
1 change: 1 addition & 0 deletions care/abdm/api/viewsets/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ def post(self, request, *args, **kwargs):

AbdmGateway().data_notify(
{
"health_id": consent["notification"]["consentDetail"]["patient"]["id"],
"consent_id": data["hiRequest"]["consent"]["id"],
"transaction_id": data["transactionId"],
"care_contexts": list(
Expand Down
72 changes: 72 additions & 0 deletions care/abdm/api/viewsets/health_facility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from django.shortcuts import get_object_or_404
from rest_framework.mixins import (
CreateModelMixin,
ListModelMixin,
RetrieveModelMixin,
UpdateModelMixin,
)
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet

from care.abdm.api.serializers.health_facility import HealthFacilitySerializer
from care.abdm.models import HealthFacility
from care.abdm.utils.api_call import Bridge
from care.utils.queryset.facility import get_facility_queryset


class HealthFacilityViewSet(
GenericViewSet,
CreateModelMixin,
ListModelMixin,
RetrieveModelMixin,
UpdateModelMixin,
):
serializer_class = HealthFacilitySerializer
model = HealthFacility
queryset = HealthFacility.objects.all()
permission_classes = (IsAuthenticated,)
lookup_field = "facility__external_id"

def get_queryset(self):
queryset = self.queryset
facilities = get_facility_queryset(self.request.user)
return queryset.filter(facility__in=facilities)

def get_facility(self, facility_external_id):
facilities = get_facility_queryset(self.request.user)
return get_object_or_404(facilities.filter(external_id=facility_external_id))

def link_health_facility(self, hf_id, facility_id):
facility = self.get_facility(facility_id)
return Bridge().add_update_service(
{
"id": hf_id,
"name": facility.name,
"type": "HIP",
"active": True,
"alias": ["CARE_HIP"],
}
)

def create(self, request, *args, **kwargs):
if (
self.link_health_facility(
request.data["hf_id"], request.data["facility"]
).status_code
== 200
):
return super().create(request, *args, **kwargs)

return Response({"message": "Error linking health facility"}, status=400)

def update(self, request, *args, **kwargs):
if (
self.link_health_facility(
request.data["hf_id"], kwargs["facility__external_id"]
).status_code
== 200
):
return super().update(request, *args, **kwargs)

return Response({"message": "Error linking health facility"}, status=400)
4 changes: 3 additions & 1 deletion care/abdm/api/viewsets/healthid.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,9 @@ def patient_sms_notify(self, request, *args, **kwargs):
status=status.HTTP_404_NOT_FOUND,
)

response = AbdmGateway().patient_sms_notify({"phone": patient.phone_number})
response = AbdmGateway().patient_sms_notify(
{"phone": patient.phone_number, "healthId": patient.abha_number.health_id}
)

return Response(response, status=status.HTTP_202_ACCEPTED)

Expand Down
55 changes: 55 additions & 0 deletions care/abdm/migrations/0009_healthfacility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Generated by Django 4.2.2 on 2023-08-21 09:53

import uuid

import django.db.models.deletion
from django.db import migrations, models


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

operations = [
migrations.CreateModel(
name="HealthFacility",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"external_id",
models.UUIDField(db_index=True, default=uuid.uuid4, unique=True),
),
(
"created_date",
models.DateTimeField(auto_now_add=True, db_index=True, null=True),
),
(
"modified_date",
models.DateTimeField(auto_now=True, db_index=True, null=True),
),
("deleted", models.BooleanField(db_index=True, default=False)),
("hf_id", models.CharField(max_length=50, unique=True)),
(
"facility",
models.OneToOneField(
on_delete=django.db.models.deletion.PROTECT,
to="facility.facility",
to_field="external_id",
),
),
],
options={
"abstract": False,
},
),
]
10 changes: 10 additions & 0 deletions care/abdm/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,13 @@ class AbhaNumber(BaseModel):

def __str__(self):
return self.abha_number


class HealthFacility(BaseModel):
hf_id = models.CharField(max_length=50, unique=True)
facility = models.OneToOneField(
"facility.Facility", on_delete=models.PROTECT, to_field="external_id"
)

def __str__(self):
return self.hf_id + " " + self.facility.name
40 changes: 32 additions & 8 deletions care/abdm/utils/api_call.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

GATEWAY_API_URL = settings.ABDM_URL
HEALTH_SERVICE_API_URL = settings.HEALTH_SERVICE_API_URL
ABDM_DEVSERVICE_URL = GATEWAY_API_URL + "/devservice"
ABDM_GATEWAY_URL = GATEWAY_API_URL + "/gateway"
ABDM_TOKEN_URL = ABDM_GATEWAY_URL + "/v0.5/sessions"
ABDM_TOKEN_CACHE_KEY = "abdm_token"
Expand All @@ -42,6 +43,8 @@ def __init__(self, gateway, token):
self.url = GATEWAY_API_URL
elif gateway == "abdm_gateway":
self.url = ABDM_GATEWAY_URL
elif gateway == "abdm_devservice":
self.url = ABDM_DEVSERVICE_URL
else:
self.url = GATEWAY_API_URL
self.token = token
Expand Down Expand Up @@ -115,7 +118,7 @@ def get(self, path, params=None, auth=None):
logger.info("{} Response: {}".format(response.status_code, response.text))
return response

def post(self, path, data=None, auth=None, additional_headers=None):
def post(self, path, data=None, auth=None, additional_headers=None, method="POST"):
url = self.url + path
headers = {
"Content-Type": "application/json",
Expand All @@ -133,7 +136,7 @@ def post(self, path, data=None, auth=None, additional_headers=None):
data_json = json.dumps(data)
# logger.info("curl -X POST {} {} -d {}".format(url, headers_string, data_json))
logger.info("Posting Request to: {}".format(url))
response = requests.post(url, headers=headers, data=data_json)
response = requests.request(method, url, headers=headers, data=data_json)
logger.info("{} Response: {}".format(response.status_code, response.text))
return response

Expand Down Expand Up @@ -338,12 +341,17 @@ def verify_document_mobile_otp(self, data):
class AbdmGateway:
# TODO: replace this with in-memory db (redis)
temp_memory = {}
hip_name = "Coronasafe Care 01"
hip_id = "IN3210000017"

def __init__(self):
self.api = APIGateway("abdm_gateway", None)

def get_hip_id_by_health_id(self, health_id):
return (
AbhaNumber.objects.filter(Q(abha_number=health_id) | Q(health_id=health_id))
.first()
.patientregistration.facility.healthfacility.hf_id
)

def add_care_context(self, access_token, request_id):
if request_id not in self.temp_memory:
return
Expand Down Expand Up @@ -415,7 +423,10 @@ def fetch_modes(self, data):
"query": {
"id": data["healthId"],
"purpose": data["purpose"] if "purpose" in data else "KYC_AND_LINK",
"requester": {"type": "HIP", "id": self.hip_id},
"requester": {
"type": "HIP",
"id": self.get_hip_id_by_health_id(data["healthId"]),
},
},
}
response = self.api.post(path, payload, None, additional_headers)
Expand Down Expand Up @@ -443,7 +454,10 @@ def init(self, prev_request_id):
"id": data["healthId"],
"purpose": data["purpose"] if "purpose" in data else "KYC_AND_LINK",
"authMode": data["authMode"] if "authMode" in data else "DEMOGRAPHICS",
"requester": {"type": "HIP", "id": self.hip_id},
"requester": {
"type": "HIP",
"id": self.get_hip_id_by_health_id(data["healthId"]),
},
},
}
response = self.api.post(path, payload, None, additional_headers)
Expand Down Expand Up @@ -705,7 +719,7 @@ def data_notify(self, data):
),
"statusNotification": {
"sessionStatus": "TRANSFERRED",
"hipId": self.hip_id,
"hipId": self.get_hip_id_by_health_id(data["health_id"]),
"statusResponses": list(
map(
lambda context: {
Expand Down Expand Up @@ -753,7 +767,7 @@ def patient_sms_notify(self, data):
),
"notification": {
"phoneNo": f"+91-{data['phone']}",
"hip": {"name": self.hip_name, "id": self.hip_id},
"hip": {"id": self.get_hip_id_by_health_id(data["healthId"])},
},
}

Expand All @@ -766,3 +780,13 @@ def on_share(self, data):
additional_headers = {"X-CM-ID": settings.X_CM_ID}
response = self.api.post(path, data, None, additional_headers)
return response


class Bridge:
def __init__(self):
self.api = APIGateway("abdm_devservice", None)

def add_update_service(self, data):
path = "/v1/bridges/addUpdateServices"
response = self.api.post(path, data, method="PUT")
return response
4 changes: 4 additions & 0 deletions config/api_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from rest_framework_nested.routers import NestedSimpleRouter

from care.abdm.api.viewsets.abha import AbhaViewSet
from care.abdm.api.viewsets.health_facility import HealthFacilityViewSet
from care.abdm.api.viewsets.healthid import ABDMHealthIDViewSet
from care.facility.api.viewsets.ambulance import (
AmbulanceCreateViewSet,
Expand Down Expand Up @@ -222,6 +223,9 @@
# ABDM endpoints
if settings.ENABLE_ABDM:
router.register("abdm/healthid", ABDMHealthIDViewSet, basename="abdm-healthid")
router.register(
"abdm/health_facility", HealthFacilityViewSet, basename="abdm-healthfacility"
)

app_name = "api"
urlpatterns = [
Expand Down

0 comments on commit 1ece5cc

Please sign in to comment.