diff --git a/care/abdm/api/serializers/health_facility.py b/care/abdm/api/serializers/health_facility.py new file mode 100644 index 0000000000..b624047266 --- /dev/null +++ b/care/abdm/api/serializers/health_facility.py @@ -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",) diff --git a/care/abdm/api/viewsets/auth.py b/care/abdm/api/viewsets/auth.py index e72b3975d7..ab7c88a1d4 100644 --- a/care/abdm/api/viewsets/auth.py +++ b/care/abdm/api/viewsets/auth.py @@ -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( diff --git a/care/abdm/api/viewsets/health_facility.py b/care/abdm/api/viewsets/health_facility.py new file mode 100644 index 0000000000..66acc4e741 --- /dev/null +++ b/care/abdm/api/viewsets/health_facility.py @@ -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) diff --git a/care/abdm/api/viewsets/healthid.py b/care/abdm/api/viewsets/healthid.py index aa365ad005..1090c12d98 100644 --- a/care/abdm/api/viewsets/healthid.py +++ b/care/abdm/api/viewsets/healthid.py @@ -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) diff --git a/care/abdm/migrations/0009_healthfacility.py b/care/abdm/migrations/0009_healthfacility.py new file mode 100644 index 0000000000..0480e91d9a --- /dev/null +++ b/care/abdm/migrations/0009_healthfacility.py @@ -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, + }, + ), + ] diff --git a/care/abdm/models.py b/care/abdm/models.py index fcd2539f9b..eafd5af1f7 100644 --- a/care/abdm/models.py +++ b/care/abdm/models.py @@ -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 diff --git a/care/abdm/utils/api_call.py b/care/abdm/utils/api_call.py index 4ff2a157ea..e955ab0142 100644 --- a/care/abdm/utils/api_call.py +++ b/care/abdm/utils/api_call.py @@ -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" @@ -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 @@ -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", @@ -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 @@ -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 @@ -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) @@ -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) @@ -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: { @@ -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"])}, }, } @@ -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 diff --git a/config/api_router.py b/config/api_router.py index 52e4f48295..6c7415dbdb 100644 --- a/config/api_router.py +++ b/config/api_router.py @@ -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, @@ -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 = [