Healthcare — hospital operations & patient flow
The problem. A hospital runs at the edge of capacity. Beds free up and fill minute by minute, the emergency department backs up, and a ward that quietly hits 100% occupancy turns into a corridor of waiting patients. The operations team needs a single live picture of capacity across every ward — and an alert the moment a unit is about to overflow, while there's still time to act.
This scenario is about live operational monitoring: model the hospital, stream occupancy in, and drive a capacity wall-board off a push connection. It uses only operational signals — bed counts, wait times, census — never patient records.
Set up demo data
New workspace? Run this once (Python) to create a ward's occupancy series, create the subscription before we listen, and push it over the 95% line. The hospital graph is created by step 1. Safe to re-run.
import datahub_sdk, numpy as np, pandas as pd
client = datahub_sdk.DataHubClient.from_env()
client.timeseries.create([datahub_sdk.TimeSeries(external_id="ward_icu_occupancy_pct", name="ICU occupancy %", unit="pct", value_type="float")])
client.subscriptions.create([datahub_sdk.Subscription(
external_id="bed_capacity", name="Bed capacity", timeseries=["ward_icu_occupancy_pct"])])
idx = pd.date_range(end=pd.Timestamp.now(tz="UTC"), periods=60, freq="1min")
occ = np.full(60, 88.0); occ[-10:] = 96.0 # crosses the 95% capacity threshold
client.timeseries.insert_from_lists(timestamps=idx, values=occ, ts="ward_icu_occupancy_pct")
1. Model the hospital
A hospital contains wards, a ward contains beds. The same hierarchy you'd model for any physical estate, which lets a ward-level alert roll up to the whole site.
- Java
- Python
- Rust
ResourceForm hospital = new ResourceForm();
hospital.setExternalId("hospital_central");
hospital.setName("Central Hospital");
hospital.setLabels(List.of("Hospital"));
ResourceForm ward = new ResourceForm();
ward.setExternalId("ward_icu");
ward.setName("Intensive care");
ward.setLabels(List.of("Ward"));
RelForm contains = new RelForm();
contains.setName("contains");
contains.setFromExternalId("hospital_central");
contains.setToExternalId("ward_icu");
client.resources().create(List.of(hospital, ward), List.of(contains));
import datahub_sdk
client.resources.create(
[datahub_sdk.Resource(external_id="hospital_central", name="Central Hospital", labels=["Hospital"]),
datahub_sdk.Resource(external_id="ward_icu", name="Intensive care", labels=["Ward"])],
[datahub_sdk.RelForm.by_external_ids("hospital_central", "ward_icu", "contains")])
use dataplatform_rust_sdk::resources::Resource;
use dataplatform_rust_sdk::relations::RelForm;
let mut hospital = Resource::new();
hospital.external_id = "hospital_central".into();
hospital.name = "Central Hospital".into();
hospital.labels = Some(vec!["Hospital".into()]);
let mut ward = Resource::new();
ward.external_id = "ward_icu".into();
ward.name = "Intensive care".into();
ward.labels = Some(vec!["Ward".into()]);
api.resources.create(
vec![hospital, ward],
vec![RelForm::by_external_ids("hospital_central", "ward_icu", "contains")],
).await?;
Capacity signals become series: ward_icu_occupancy_pct, ward_icu_free_beds,
ed_wait_minutes.
2. Drive the capacity wall-board live
A wall-board polling every minute is already stale. Subscribe to the occupancy series
and update the moment a reading lands — and raise a capacity_warning event when a
ward crosses its threshold, so charge nurses are paged before it's full. See
Consume live data for the listener lifecycle.
- Java
- Python
- Rust
import ai.intellistream.datahub.sdk.subscriptions.SubscriptionMessage;
try (var stream = client.subscriptions().listen(List.of("bed_capacity"))
.stream((SubscriptionMessage msg) -> { // auto-acks after each message
updateWallboard(msg.payload());
if (overThreshold(msg.payload())) { // e.g. occupancy > 95%
EventModel warning = new EventModel();
warning.setExternalId("capacity_warning_icu_" + System.currentTimeMillis());
warning.setType("capacity_warning");
warning.setStatus("open");
warning.setMetadata(Map.of("ward", "ward_icu", "occupancy_pct", "96"));
warning.setEventTime(ZonedDateTime.now());
client.events().create(List.of(warning));
}
})) {
awaitShutdown(); // your app lifecycle; closing the stream ends delivery
}
with client.subscriptions.listen(["bed_capacity"]) as listener:
for msg in listener:
update_wallboard(msg.payload)
if over_threshold(msg.payload): # e.g. occupancy > 95%
client.events.create([datahub_sdk.Event(
external_id=f"capacity_warning_icu_{int(pd.Timestamp.now().timestamp())}",
type="capacity_warning", status="open",
event_time=pd.Timestamp.now(tz="UTC"),
metadata={"ward": "ward_icu", "occupancy_pct": "96"})])
listener.ack([msg.message_id])
use dataplatform_rust_sdk::events::Event;
use chrono::Utc;
let mut listener = api.subscriptions.listen(&["bed_capacity"]).await?;
while let Some(Ok(msg)) = listener.next().await {
update_wallboard(&msg.payload);
if over_threshold(&msg.payload) { // e.g. occupancy > 95%
let mut warning = Event::new(format!("capacity_warning_icu_{}", Utc::now().timestamp()));
warning.r#type = Some("capacity_warning".into());
warning.status = Some("open".into());
warning.add_metadata("ward".into(), "ward_icu".into());
warning.add_metadata("occupancy_pct".into(), "96".into());
warning.set_event_time(Utc::now());
api.events.create(&vec![warning]).await?;
}
listener.ack(&[msg.message_id.as_str()]).await?;
}
3. Review the day's pressure points
Hourly-average occupancy per ward shows planners when the squeeze happens, so they can shift staff and elective schedules. See Query & aggregate.
See the result
The ICU crossing 95% trips the loop:
capacity_warning_icu_… → open (occupancy 96% — page the charge nurse)
See also
- Consume live data — the capacity wall-board's push loop.
- Turn readings into events — the threshold rule.
- Query & aggregate — daily occupancy roll-ups.