diff --git a/fiveparaminte-main/routers/dashboard.py b/fiveparaminte-main/routers/dashboard.py index 5908179..acffcb2 100644 --- a/fiveparaminte-main/routers/dashboard.py +++ b/fiveparaminte-main/routers/dashboard.py @@ -468,3 +468,114 @@ def get_network_info(): f"6. The dashboard will show data automatically" ] } + + +# === Push Vitals Endpoint (for local-to-cloud sync) === +from schemas import NormalizedVitals +from datetime import datetime, timezone + +@router.post("/push-vitals") +async def push_vitals(vitals: NormalizedVitals, api_key: Optional[str] = None, db: Session = Depends(get_db)): + """ + Receive vitals pushed from a local instance and store + broadcast them to connected WebSocket clients. + """ + from config import settings + if settings.api_token and api_key != settings.api_token: + raise HTTPException(status_code=401, detail="Invalid or missing api_key") + + try: + now = datetime.now(timezone.utc) + + # 1. Device tracking + device = db.query(Device).filter(Device.device_id == vitals.device_id).first() + if not device: + device = Device( + device_id=vitals.device_id, + ip_address=vitals.ip_address or "remote-push", + status="active", + last_seen=now + ) + db.add(device) + else: + if vitals.ip_address: + device.ip_address = vitals.ip_address + device.status = "active" + device.last_seen = now + + # 2. Patient tracking + mrn = vitals.patient_id + patient = None + if mrn and mrn != "UNKNOWN": + patient = db.query(Patient).filter(or_(Patient.mrn == mrn, Patient.patient_id == mrn)).first() + if not patient: + patient = Patient( + patient_id=mrn, + mrn=mrn, + name=vitals.patient_name or "Unknown", + gender=vitals.patient_gender, + dob=vitals.patient_dob, + bed_id=vitals.bed_id, + device_id=vitals.device_id + ) + db.add(patient) + else: + if vitals.patient_name and vitals.patient_name != "Unknown": + patient.name = vitals.patient_name + if vitals.patient_gender: + patient.gender = vitals.patient_gender + if vitals.patient_dob: + patient.dob = vitals.patient_dob + if vitals.bed_id: + patient.bed_id = vitals.bed_id + patient.device_id = vitals.device_id + elif vitals.device_id: + # Check if there's already a patient assigned to this device + patient = db.query(Patient).filter(Patient.device_id == vitals.device_id, Patient.patient_id != "UNKNOWN").first() + if patient: + mrn = patient.mrn or patient.patient_id + + # 3. Create VitalReading + reading = VitalReading( + device_id=vitals.device_id, + patient_id=mrn or "UNKNOWN", + timestamp=vitals.timestamp or now, + heart_rate=vitals.heart_rate, + spo2=vitals.spo2, + systolic_bp=vitals.systolic_bp, + diastolic_bp=vitals.diastolic_bp, + map_bp=vitals.map_bp, + respiratory_rate=vitals.respiratory_rate, + temperature=vitals.temperature, + transmitted=True + ) + db.add(reading) + db.commit() + db.refresh(reading) + + # 4. Broadcast to WebSocket clients + vitals_dict = vitals.model_dump(mode="json") + vitals_dict["mrn"] = mrn or (patient.mrn if patient else None) + vitals_dict["patient_id"] = vitals_dict["mrn"] or vitals.patient_id + vitals_dict["name"] = vitals.patient_name or (patient.name if patient else None) + vitals_dict["patient_name"] = vitals_dict["name"] + vitals_dict["gender"] = vitals.patient_gender or (patient.gender if patient else None) + vitals_dict["patient_gender"] = vitals_dict["gender"] + vitals_dict["dob"] = vitals.patient_dob or (patient.dob if patient else None) + vitals_dict["patient_dob"] = vitals_dict["dob"] + vitals_dict["bed_id"] = vitals.bed_id or (patient.bed_id if patient else None) + + await ws_manager.broadcast_vitals(vitals_dict) + + return { + "status": "ok", + "reading_id": reading.id, + "device_id": vitals.device_id, + "patient_id": vitals_dict["patient_id"] + } + except HTTPException: + raise + except Exception as e: + logger.error(f"Push-vitals error: {e}") + db.rollback() + raise HTTPException(status_code=500, detail=f"Failed to store vitals: {str(e)}") + diff --git a/routers/dashboard.py b/routers/dashboard.py index 5908179..acffcb2 100644 --- a/routers/dashboard.py +++ b/routers/dashboard.py @@ -468,3 +468,114 @@ def get_network_info(): f"6. The dashboard will show data automatically" ] } + + +# === Push Vitals Endpoint (for local-to-cloud sync) === +from schemas import NormalizedVitals +from datetime import datetime, timezone + +@router.post("/push-vitals") +async def push_vitals(vitals: NormalizedVitals, api_key: Optional[str] = None, db: Session = Depends(get_db)): + """ + Receive vitals pushed from a local instance and store + broadcast them to connected WebSocket clients. + """ + from config import settings + if settings.api_token and api_key != settings.api_token: + raise HTTPException(status_code=401, detail="Invalid or missing api_key") + + try: + now = datetime.now(timezone.utc) + + # 1. Device tracking + device = db.query(Device).filter(Device.device_id == vitals.device_id).first() + if not device: + device = Device( + device_id=vitals.device_id, + ip_address=vitals.ip_address or "remote-push", + status="active", + last_seen=now + ) + db.add(device) + else: + if vitals.ip_address: + device.ip_address = vitals.ip_address + device.status = "active" + device.last_seen = now + + # 2. Patient tracking + mrn = vitals.patient_id + patient = None + if mrn and mrn != "UNKNOWN": + patient = db.query(Patient).filter(or_(Patient.mrn == mrn, Patient.patient_id == mrn)).first() + if not patient: + patient = Patient( + patient_id=mrn, + mrn=mrn, + name=vitals.patient_name or "Unknown", + gender=vitals.patient_gender, + dob=vitals.patient_dob, + bed_id=vitals.bed_id, + device_id=vitals.device_id + ) + db.add(patient) + else: + if vitals.patient_name and vitals.patient_name != "Unknown": + patient.name = vitals.patient_name + if vitals.patient_gender: + patient.gender = vitals.patient_gender + if vitals.patient_dob: + patient.dob = vitals.patient_dob + if vitals.bed_id: + patient.bed_id = vitals.bed_id + patient.device_id = vitals.device_id + elif vitals.device_id: + # Check if there's already a patient assigned to this device + patient = db.query(Patient).filter(Patient.device_id == vitals.device_id, Patient.patient_id != "UNKNOWN").first() + if patient: + mrn = patient.mrn or patient.patient_id + + # 3. Create VitalReading + reading = VitalReading( + device_id=vitals.device_id, + patient_id=mrn or "UNKNOWN", + timestamp=vitals.timestamp or now, + heart_rate=vitals.heart_rate, + spo2=vitals.spo2, + systolic_bp=vitals.systolic_bp, + diastolic_bp=vitals.diastolic_bp, + map_bp=vitals.map_bp, + respiratory_rate=vitals.respiratory_rate, + temperature=vitals.temperature, + transmitted=True + ) + db.add(reading) + db.commit() + db.refresh(reading) + + # 4. Broadcast to WebSocket clients + vitals_dict = vitals.model_dump(mode="json") + vitals_dict["mrn"] = mrn or (patient.mrn if patient else None) + vitals_dict["patient_id"] = vitals_dict["mrn"] or vitals.patient_id + vitals_dict["name"] = vitals.patient_name or (patient.name if patient else None) + vitals_dict["patient_name"] = vitals_dict["name"] + vitals_dict["gender"] = vitals.patient_gender or (patient.gender if patient else None) + vitals_dict["patient_gender"] = vitals_dict["gender"] + vitals_dict["dob"] = vitals.patient_dob or (patient.dob if patient else None) + vitals_dict["patient_dob"] = vitals_dict["dob"] + vitals_dict["bed_id"] = vitals.bed_id or (patient.bed_id if patient else None) + + await ws_manager.broadcast_vitals(vitals_dict) + + return { + "status": "ok", + "reading_id": reading.id, + "device_id": vitals.device_id, + "patient_id": vitals_dict["patient_id"] + } + except HTTPException: + raise + except Exception as e: + logger.error(f"Push-vitals error: {e}") + db.rollback() + raise HTTPException(status_code=500, detail=f"Failed to store vitals: {str(e)}") +