JupyterHealth Exchange is a Django web application that facilitates sharing patient-consented health data with authorized users via a Web UI and REST, MCP, and FHIR APIs.
Features include:- OAuth 2.0, OIDC using django-oauth-toolkit and grafana-django-saml2-auth for SAML/SSO
- Simple Role Based Access Control
- FHIR R5 validation using fhir.resources
- Open mHealth validation using JSON schemas
- REST APIs using Django Rest Framework
- Built-in, light-weight Vanilla JS SPA UI (npm not required) using oidc-clinet-ts, handlebars and bootstrap
Researchers create studies and recruit patients, who consent and submit observations via client applications, and the data is then stored in JupyterHealth Exchange and queried by researchers using Jupyter Notebooks or other systems.
Typical Data Flow
Users manage the system via the Web UI, and data producers receive invitation credentials by email, manage consents through the Admin API, and upload data to JupyterHealth Exchange using the FHIR API. Data consumers such as Jupyter Notebooks or other systems then query and read the data through REST and MCP APIs.
Note
Getting started with Docker is in the works!
-
Set up your Python environment and install dependencies from
Pipfile- this project uses Django version 5.2 which requires python 3.12- NB: If using pipenv it is recommended to run
pipenv syncagainst the lock file to match package versions
- NB: If using pipenv it is recommended to run
-
Create a new Postgres DB (currently only Postgres is supported)
-
Copy
dot_env_example.txtto.envand update theDB_*parameters to match (2) above.- Optionally you can add a Django
SECRET_KEYby running the command below or you can leave this for now to use a randomly generated value at runtime (this will not work with more than one worker)$ openssl rand -base64 32
- Optionally you can add a Django
-
Ensure the
.envis loaded into your Python environment, eg for pipenv run$ pipenv shell -
Run the Django migration
$ python manage.py migrateto create the database tables. -
Seed the database by running the Django management command
$ python manage.py seed -
Start the server with
$ python manage.py runserver -
Browse to http://localhost:8000/admin and enter the credentials
sam@example.comJhe1234! -
Under Django OAuth Toolkit > Applications you should see the seeded OAuth2 application named
JHE Admin UIwith a Redirect URI forhttp://localhost:8000/auth/callback- this is used for the Web UI OAuth 2.0 login. -
Click the LOG OUT button at the top
-
Finally, we need to create an RS256 Private Key for signing the JWT
-
Run
openssl genrsa -out oidc.key 4096 -
Run
awk '{printf "%s%s", (NR==1?"":"\\n"), $0}' oidc.keyto remove line breaks Note: some python environments and OS combinations do not handle the "\n" so you may need to include line breaks in the.envfile. -
Return to the
.envfile and update theOIDC_RSA_PRIVATE_KEY -
Keep the
oidc.keysomewhere safe
-
-
Browse to http://localhost:8000/ and log in with the credentials
mary@example.comJhe1234!and you should be directed to the/portal/organizationspath with some example Organizations is the dropdown -
New users can be signed up from the base URL (eg http://localhost:8000/) with the default invitation code "jhe" which is set from the
REGISTRATION_INVITE_CODEin.env
Note
Due to browser security restrictions and the oidc-client-ts used for authentication, the web app must be accessed over HTTPS for any hostname other than localhost - see Running in Production below.
Entities are based on the HL7 FHIR model, a widely used healthcare standard for organizing and exchanging clinical information between systems.
- Any user accessing the Web UI is a Practitioner by default. In practice this might be a researcher, a clinician, an administrator or even an individual setting up JupyterHealth Exchange to view their own health data.
- Patient users are registered by Practitioners and are sent a link to authenticate and upload data.
- The same OAuth 2.0 strategy is used for both Practitioners and Patients, the only difference being that the authorization code is provided out-of-band for Patients (ie invitation links).
- An Organization is a group of Practitioners, Patients and Studies (FHIR Groups) and is used to manage access to data.
- An Organization is typically hierarchical with sub-Organizations like Institutions, Departments, Labs etc.
- A Practitioner belongs to one or more Organization.
- A Patient belongs to one or more Organization.
- A Study belongs to one single Organization.
- A Study is a Group of Patients and belongs to a single Organization.
- A Study has one or more Clients (apps that talk to JupyterHealth Exchange), one or more matching Data Sources (anything that produces data) and one or more Scope Requests (eg Blood Pressure, Heart Rate, etc)
- When a Patient is added to a Study, they must explicitly consent to sharing the requested Scopes before any personal data (Observations) can be uploaded or shared.
- An Observation is Patient data and belongs to a single Patient.
- An Observation must reference a Patient ID as the subject and a Data Source ID as the device.
- Personal device data is expected to be in the Open mHealth (JSON) format however the system can be easily extended to support any binary data attachments or discrete Observation records.
- Observation data is stored as a valueAttachment in Base 64 encoded JSON binary.
- Authorization to view Observations depends on the relationship of Organization, Study and Scopes/consents as described above.
- A Data Source is anything that produces Observations (typically a device app eg iHealth). An Observation references a Data Source ID in the device field.
- A Data Source supports one or more Scopes (types) of Observations (eg Blood Glucose).
- A Study has one or more associated Data Sources.
- A Data Source has one Client. In some cases a single app may be both a Data Source and a Client, in which case a record is created for each and both are added to the Study.
- Clients are apps that talk to JupyterHealth Exchange.
- Each Client has its own OAuth 2.0 Client ID.
- A Study has one or more associated Clients.
- A Client has one or more associated Data Sources. In some cases a single app may be both a Data Source and a Client, in which case a record is created for each and both are added to the Study.
- Sign up as a new user from the Web UI.
- Create a new Organization (your user is automatically added to the Organization with a Manager role).
- Create a new Study for the Organization (View Organization → Studies+).
- Create a new Patient for the Organization using a different email than step 1 (Patients → Add Patient).
- Add Data Sources and Scopes to the Study (View Study → Data Sources+, Scope Requests+).
- Add the Patient to the Study (Patients → Select patient → Add Patient(s) to Study).
- Create an Invitation Link for the Patient (View Patient → Generate Invitation Link).
- Use the code in the invitation link with the Auth API to exchange it for an access token.
- Upload Observations using the FHIR API and access token.
- View the Observations from the Web UI.
See doc for test requirements, coding standards, and PR checklist.


