support forwarding vitals and syncing patients to multiple configurable URLs
This commit is contained in:
+55
-55
@@ -41,69 +41,54 @@ async def forward_vitals_to_api(vitals: NormalizedVitals) -> bool:
|
||||
|
||||
client = get_http_client()
|
||||
|
||||
# 1. Forward to primary target API (e.g. Injomo Dev Workflow)
|
||||
async def _forward_primary() -> bool:
|
||||
primary_url = settings.target_api_url
|
||||
if not primary_url:
|
||||
async def _post_target(url: str, label: str) -> bool:
|
||||
if not url:
|
||||
return False
|
||||
curr_port = os.getenv("PORT", "8000")
|
||||
if f":{curr_port}" in url and ("localhost" in url or "127.0.0.1" in url):
|
||||
return False
|
||||
try:
|
||||
primary_headers = headers.copy()
|
||||
req_headers = headers.copy()
|
||||
if settings.api_token:
|
||||
primary_headers["Authorization"] = f"Bearer {settings.api_token}"
|
||||
response = await client.post(
|
||||
primary_url,
|
||||
json=payload,
|
||||
headers=primary_headers
|
||||
)
|
||||
req_headers["Authorization"] = f"Bearer {settings.api_token}"
|
||||
url_to_call = url
|
||||
if settings.api_token and "?" not in url and "/api/push-vitals" in url:
|
||||
url_to_call = f"{url}?api_key={settings.api_token}"
|
||||
response = await client.post(url_to_call, json=payload, headers=req_headers)
|
||||
response.raise_for_status()
|
||||
logger.info(f"Successfully forwarded vitals to primary API for device {vitals.device_id}")
|
||||
logger.info(f"Successfully forwarded vitals to {label} ({url}) for device {vitals.device_id}")
|
||||
return True
|
||||
except Exception as e:
|
||||
err_msg = str(e).strip() or "timeout / connection reset"
|
||||
logger.warning(f"Error forwarding to primary API: {type(e).__name__} ({err_msg})")
|
||||
logger.warning(f"Error forwarding to {label} ({url}): {type(e).__name__} ({err_msg})")
|
||||
return False
|
||||
|
||||
# 2. Forward to Cloud Server
|
||||
async def _forward_cloud() -> bool:
|
||||
cloud_base = get_cloud_base_url()
|
||||
cloud_url = f"{cloud_base}/api/push-vitals"
|
||||
is_on_cloud = os.getenv("RENDER") is not None or os.getenv("PORT") == "10000" or "render.local" in os.getenv("HOSTNAME", "")
|
||||
if is_on_cloud and ("onrender.com" in cloud_base):
|
||||
return True
|
||||
try:
|
||||
url_with_key = cloud_url
|
||||
if settings.api_token:
|
||||
url_with_key = f"{cloud_url}?api_key={settings.api_token}"
|
||||
targets = []
|
||||
if settings.target_api_url:
|
||||
targets.append((settings.target_api_url, "Primary Target API"))
|
||||
|
||||
response = await client.post(
|
||||
url_with_key,
|
||||
json=payload,
|
||||
headers=headers
|
||||
)
|
||||
response.raise_for_status()
|
||||
logger.info(f"Successfully forwarded vitals to Cloud API for device {vitals.device_id}")
|
||||
return True
|
||||
except Exception as e:
|
||||
err_msg = str(e).strip() or "timeout / connection reset"
|
||||
logger.warning(f"Error forwarding to Cloud API: {type(e).__name__} ({err_msg})")
|
||||
return False
|
||||
cloud_base = get_cloud_base_url()
|
||||
is_on_cloud = os.getenv("RENDER") is not None or os.getenv("PORT") == "10000" or "render.local" in os.getenv("HOSTNAME", "")
|
||||
if not (is_on_cloud and "onrender.com" in cloud_base):
|
||||
cloud_push_url = f"{cloud_base}/api/push-vitals" if not cloud_base.endswith("/api/push-vitals") else cloud_base
|
||||
if cloud_push_url not in [t[0] for t in targets]:
|
||||
targets.append((cloud_push_url, "Cloud Server"))
|
||||
|
||||
results = await asyncio.gather(_forward_primary(), _forward_cloud(), return_exceptions=True)
|
||||
for extra_url in getattr(settings, "forward_urls", []):
|
||||
if extra_url and extra_url not in [t[0] for t in targets]:
|
||||
targets.append((extra_url, "Configured Forward URL"))
|
||||
|
||||
tasks = [_post_target(url, lbl) for url, lbl in targets]
|
||||
results = await asyncio.gather(*tasks, return_exceptions=True)
|
||||
return any(r is True for r in results if not isinstance(r, Exception))
|
||||
|
||||
|
||||
async def sync_patient_assignment_to_cloud(patient_data: dict, device_id: str) -> bool:
|
||||
"""
|
||||
Synchronizes patient demographics assignment to Cloud Server.
|
||||
Synchronizes patient demographics assignment to Cloud and LAN servers.
|
||||
Ensures local and remote dashboards always show the exact same patient.
|
||||
"""
|
||||
cloud_base = get_cloud_base_url()
|
||||
is_on_cloud = os.getenv("RENDER") is not None or os.getenv("PORT") == "10000" or "render.local" in os.getenv("HOSTNAME", "")
|
||||
if is_on_cloud and ("onrender.com" in cloud_base):
|
||||
return True
|
||||
|
||||
client = get_http_client()
|
||||
cloud_assign_url = f"{cloud_base}/api/patient/assign"
|
||||
payload = {
|
||||
"device_id": device_id,
|
||||
"mrn": patient_data.get("mrn") or patient_data.get("patient_id") or "",
|
||||
@@ -113,14 +98,29 @@ async def sync_patient_assignment_to_cloud(patient_data: dict, device_id: str) -
|
||||
"bed_id": patient_data.get("bed_id"),
|
||||
"patient_type": patient_data.get("patient_type")
|
||||
}
|
||||
try:
|
||||
url_with_key = cloud_assign_url
|
||||
if settings.api_token:
|
||||
url_with_key = f"{cloud_assign_url}?api_key={settings.api_token}"
|
||||
resp = await client.post(url_with_key, json=payload, headers={"Content-Type": "application/json"})
|
||||
resp.raise_for_status()
|
||||
logger.info(f"Successfully synced patient {payload['mrn']} ({payload['name']}) to Cloud for {device_id}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not sync patient assignment to Cloud: {type(e).__name__} ({e})")
|
||||
return False
|
||||
|
||||
targets = []
|
||||
cloud_base = get_cloud_base_url()
|
||||
targets.append(f"{cloud_base}/api/patient/assign")
|
||||
|
||||
for extra_url in getattr(settings, "forward_urls", []):
|
||||
if "/api/push-vitals" in extra_url:
|
||||
assign_url = extra_url.replace("/api/push-vitals", "/api/patient/assign")
|
||||
if assign_url not in targets:
|
||||
targets.append(assign_url)
|
||||
|
||||
async def _sync_one(url: str) -> bool:
|
||||
try:
|
||||
url_with_key = url
|
||||
if settings.api_token:
|
||||
url_with_key = f"{url}?api_key={settings.api_token}"
|
||||
resp = await client.post(url_with_key, json=payload, headers={"Content-Type": "application/json"})
|
||||
resp.raise_for_status()
|
||||
logger.info(f"Successfully synced patient {payload['mrn']} to {url} for {device_id}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not sync patient assignment to {url}: {type(e).__name__} ({e})")
|
||||
return False
|
||||
|
||||
results = await asyncio.gather(*[_sync_one(u) for u in targets], return_exceptions=True)
|
||||
return any(r is True for r in results if not isinstance(r, Exception))
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
},
|
||||
"target_api_url": "https://services-api.a2zhealth.in/v1/contec-monitor",
|
||||
"cloud_api_url": "https://services-api.a2zhealth.in/v1/contec-monitor",
|
||||
"forward_urls": [
|
||||
"http://192.168.100.104:10000/api/push-vitals",
|
||||
"https://services-api.a2zhealth.in/v1/contec-monitor"
|
||||
],
|
||||
"api_token": "",
|
||||
"retry_interval_seconds": 60,
|
||||
"log_file": "hl7_forwarder.log",
|
||||
|
||||
@@ -11,6 +11,10 @@ class Settings(BaseSettings):
|
||||
device_models: dict = {}
|
||||
target_api_url: str = "https://services-api.a2zhealth.in/v1/contec-monitor"
|
||||
cloud_api_url: str = "https://services-api.a2zhealth.in/v1/contec-monitor"
|
||||
forward_urls: list = [
|
||||
"http://192.168.100.104:10000/api/push-vitals",
|
||||
"https://services-api.a2zhealth.in/v1/contec-monitor"
|
||||
]
|
||||
api_token: str = ""
|
||||
retry_interval_seconds: int = 60
|
||||
log_file: str = "hl7_forwarder.log"
|
||||
|
||||
Reference in New Issue
Block a user