Track
Track 2: Data-to-Insight. Stream data "is hard to interpret and does not clearly show patterns, risks, or health impact." Stream Check-up turns four data sets OneAquaHealth already publishes into one ranked list of streams. Each stream comes with its reasons in plain sentences and one action to take.
Inspiration
We started by reading OneAquaHealth's own public data: the service behind the Resilience Map and the HL7 Europe FHIR sandbox. Three things stood out:
- 95 of the 96 lab health-risk results are from 2023, and each stream was sampled only once.
- 10 research streams have no lab result at all, but look the same as the others on the map.
- 51 lab records hold values no stream can have, such as a pH of 83,800. The scale stops at 14.
Cities decide where to act using this picture. It is three years old, and part of it is wrong. We wanted a tool a non-technical coordinator could open and understand in ten seconds: which streams should we look at first, and why?
What it does
For every one of the 106 streams, Stream Check-up answers three questions:
- How old is what we know? The date and result of the last lab check.
- What has happened since? Days of heavy rain, hot days and heatwaves since that check.
- What is around it? Distance to the nearest sewage works, paved area and green cover.
Each stream gets one of four levels: Never checked, Check first, Check soon or Can wait. Every level comes with its reasons as sentences, not scores.
Then there is one action: ask for a visit. The request goes into OneAquaHealth's own FHIR test system as a ServiceRequest. A coordinator sees every request on one page and marks it as visited when a volunteer has been. A person always decides.
A second page, Record problems, checks the data itself and links to every record it questions. Where it is safe, Stream Check-up also acts on what it finds:
- It links the two systems. The map data and the FHIR system named the same streams differently, so results could not be matched. We added every research stream to the FHIR system carrying its map code. Unlinked streams went from 104 to 0.
- It pins a note to each impossible value. We never change OneAquaHealth's records. Each of the 51 values gets a
DetectedIssuenote with the likely correction, asking a person to confirm. - It watches nightly. A scheduled job re-reads the public data every night, and the home page lists what changed.
- It prints a city brief. One page per city, for a council meeting.
- It points citizens to a stream. "Near me" finds the nearby stream that most needs a check, then hands over to the existing OneAquaHealth Citizen Science App.
How the levels work
Nothing is predicted. Four facts per stream, all records OneAquaHealth already holds:
| Fact | More points when |
|---|---|
| Last lab health-risk result | the risk was higher |
| Days with $\geq 20$ mm of rain since then | there were more |
| Days reaching $\geq 30\,^{\circ}\mathrm{C}$ since then | there were more |
| Distance to the nearest sewage works | it is closer |
For each fact $f$, the streams are split into thirds, and stream $s$ gets $p_f(s) \in {0, 1, 2}$ points. The total is
$$P(s) = \sum_{f=1}^{4} p_f(s), \qquad 0 \le P(s) \le 8$$
$P \ge 6$ means check first, $3 \le P \le 5$ check soon, and $P \le 2$ can wait. A stream with no lab result is always never checked.
How we built it
- Data: Python scripts, using only the standard library, copy the public data. That covers 106 sites, lab health-risk scores, 56 surrounding measures per site, daily weather since January 2023, and the FHIR sandbox records. The scripts apply the rules and write one data file.
- Site: React and Vite with a Leaflet map on OpenStreetMap, hosted on Vercel. It needs no login and costs nothing to host.
- Standards: everything we write follows HL7 FHIR R4. Each stream is a
Locationusing the OneAquaHealth location profile, each record note is aDetectedIssue, and each visit request is aServiceRequest. Sends are conditional, so sending twice never creates duplicates. - Checks: a GitHub workflow builds the OneAquaHealth implementation guide from source and runs the official HL7 validator on everything we write. 30 automated tests cover the rules and the records.
Challenges
- Our first idea failed its own test. We wanted to estimate the health of unsampled streams from streams with similar surroundings. We hid each of the 96 sampled streams in turn and estimated its risk from its five most similar neighbours across 53 measures. Measured by mean absolute error,
$$\text{MAE} = \frac{1}{96}\sum_{i=1}^{96} \left| \hat{r}_i - r_i \right|$$
the "similar streams" estimate scored $0.104$, while simply guessing the average scored $0.101$. Surroundings alone cannot tell you how a stream is doing. We dropped prediction, and this became the core argument: someone has to go and look. The test is in the repo as scripts/twin_test.py.
- Browsers could not reach the FHIR sandbox. It answers web pages with two
Access-Control-Allow-Originheaders, and browsers accept exactly one. We route visit requests through a small relay limited to that one record type, and list the issue on the Record problems page. - The official validator found our own mistakes. Two research sites have no name in the source data, and the OneAquaHealth profile requires one. Our notes bundle also lacked entry addresses. We fixed both, and the validator now reports no errors on the notes.
- Weather mostly separates cities. Weather is nearly identical for every stream in one city. We say this openly: inside a city, the order comes from the last lab result and the sewage distance.
What we learned
- The most useful thing a data tool can say is sometimes "this record cannot be right". The pH of 83,800 turned out to be 51 values about $10^4$ times too large, which looks like a decimal point lost on import.
- For people outside the lab, a ranking with reasons beats a score. A coordinator acts on "34 days of heavy rain since the last check, sewage works 1.1 km away", not on "0.40".
- Standards only help when the systems share names for things. Linking the 106 streams fixed a gap no dashboard could.
Limits
- It ranks where to look. It does not say a stream is unhealthy today.
- The cut-offs are simple choices. They are untested against new lab results, because none exist yet.
- Citizen checks made in the OneAquaHealth app are not public, so they are not included.
- No coordinator or citizen has used it yet.
- The demo video's narration is an AI voice.
What's next
- With OneAquaHealth's permission, read completed citizen checks, so a check closes its visit request automatically.
- Test the levels against the next round of lab samples.
- Translate the site into the five city languages.

Log in or sign up for Devpost to join the conversation.