π« Webapp to explore cohorts metadata for the iCARE4CVD project
See the codeWebapp enabling the upload, exploration, and mapping of cohort metadata, built for the iCARE4CVD project.
It interacts with a privacy computing platform (Decentriq) to create secure Data Clean Rooms (DCRs) where data scientists can run analyses on selected cohorts. The Cohort Explorer handles only cohort metadata; the actual sensitive cohort data is uploaded directly to Decentriq DCRs by data custodians after the DCR is configured via the Explorer.
This platform enables data custodians and data scientists to:
snomedct:12345) to URIs.[!IMPORTANT] Only the designated owner of a cohort (as specified in the central
iCARE4CVD_Cohorts.xlsxspreadsheet) and platform administrators can upload or modify the data dictionary and mappings for that cohort.
The Cohort Explorer follows a standard web application architecture with three main components, orchestrated using Docker Compose:
frontend/): A client-side web application built with Next.js (React) and TypeScript. It provides the user interface for exploring cohorts, managing uploads, defining mappings, and initiating DCR creation. It interacts with the Backend API. Styling is done with TailwindCSS and DaisyUI.backend/): A Python API server built with FastAPI. It handles:
db): An Oxigraph triplestore instance. It stores all cohort and variable metadata as RDF triples, conforming to the custom iCARE4CVD ontology. It exposes a SPARQL endpoint accessible only by the Backend.Data Flow:
iCARE4CVD_Cohorts.xlsx file containing general information about all participating cohorts. This data is parsed by the backend and stored in a dedicated graph (icare:graph/metadata) in the triplestore.cohort-explorer-ontology.ttl). These triples are stored in a named graph specific to the cohort (e.g., icare:cohort/{cohort_id})./insert-triples endpoint), which stores these mappings as RDF triples in a separate named graph for that cohort (e.g., icare:cohort/{cohort_id}/mappings)./create-provision-dcr endpoint).Authentication:
/auth/callback).[!NOTE] The current approach of fetching all metadata to the frontend works well for the expected scale of the iCARE4CVD project. If the number of cohorts/variables grows significantly, this could be optimized by performing filtering and searching directly via SPARQL queries on the backend, returning only the relevant subset of data to the frontend.
Metadata is stored in an Oxigraph triplestore using RDF. The structure is defined by a custom OWL ontology (cohort-explorer-ontology.ttl) with the namespace icare: <https://w3id.org/icare4cvd/>.
Key Classes:
icare:Cohort: Represents a study cohort. Linked to its variables via icare:hasVariable. Has properties like dc:identifier, icare:institution, icare:cohortType, icare:studyParticipants, etc. General cohort metadata resides in the icare:graph/metadata named graph.icare:Variable: Represents a variable (column) within a cohort's data dictionary. Has properties like dc:identifier, rdfs:label, icare:varType, icare:units, icare:omop domain, etc. Linked to categories via icare:categories. Can be mapped using icare:conceptId (original) and icare:mappedId (user-defined). Variable definitions reside in the cohort's named graph (e.g., icare:cohort/{cohort_id}).icare:VariableCategory: Represents a specific category for a categorical variable. Has rdf:value, rdfs:label, and can be mapped using icare:mappedId.Mappings defined via the UI are stored in a separate named graph (e.g., icare:cohort/{cohort_id}/mappings) to distinguish them from the base metadata derived directly from the uploaded file.
[!WARNING] Running the Cohort Explorer requires the spreadsheet containing general cohort information. It must be an Excel file named
iCARE4CVD_Cohorts.xlsxlocated in thedata/directory (create this directory if it doesn't exist). The spreadsheet needs a sheet namedDescriptionswith specific columns (see original README section for details).
Clone the repository:
git clone <repository-url>
cd cohort-explorer
Create .env file: Copy the required environment variables (OAuth Client ID/Secret, Decentriq API token, JWT Secret, Admin emails, etc.) into a .env file at the root of the repository. Ask project administrators for necessary secrets.
# Example .env structure
AUTH_ENDPOINT=https://auth0.com # Or your Decentriq auth endpoint
CLIENT_ID=YOUR_DECENTRIQ_CLIENT_ID
CLIENT_SECRET=YOUR_DECENTRIQ_CLIENT_SECRET
DECENTRIQ_EMAIL=YOUR_DECENTRIQ_ADMIN_EMAIL # Email linked to the token below
DECENTRIQ_TOKEN=YOUR_DECENTRIQ_API_TOKEN # Get from Decentriq platform
JWT_SECRET=GENERATE_A_STRONG_SECRET # e.g., using python -c "import secrets; print(secrets.token_urlsafe(32))"
[email protected],[email protected] # Comma-separated list of admin emails
TOGATHER_API_KEY=YOUR_TOGATHER_API_KEY # If using Togather integration
# Frontend needs this at build time if not using default localhost:3000
# NEXT_PUBLIC_API_URL=http://localhost:3000
[!WARNING]
- Ensure the user email you intend to log in with during development is included in the
ADMINSlist if you need administrative privileges (like uploading any cohort).- The
DECENTRIQ_TOKENis required for the backend to interact with the Decentriq API (e.g., for DCR creation).
Prepare Data Directory:
data/ directory at the root of the project if it doesn't exist.iCARE4CVD_Cohorts.xlsx file inside the data/ directory.data/cohorts/.data/db/.Start Services (Docker - Recommended):
docker compose up --build
This command builds the images (if necessary) and starts the frontend, backend, and db services defined in docker-compose.yml. Hot-reloading is enabled for both the frontend and backend.
http://localhost:3001.http://localhost:3000.http://localhost:7878.[!IMPORTANT] For Decentriq OAuth to function correctly during local development, the provider must be configured to allow redirects to
http://localhost:3000/auth/callback.
[!TIP] For optimal IDE support (autocompletion, type checking) within the
backendcode, install dependencies locally usinguv:uv sync --system # Or use a virtual environmentSimilarly, for the frontend:
cd frontend pnpm install # or npm install / yarn install cd ..
Start Services (Manual - Alternative):
You can run each component manually outside Docker. Ensure the .env file is present in the backend/ directory for this method.
docker compose up db
cd backend
# Assumes .env file is here
DEV_MODE=true uv run uvicorn src.main:app --host 0.0.0.0 --port 3000 --reload
cd ..
cd frontend
pnpm install # If not already done
pnpm dev # Starts on http://localhost:3001 by default
cd ..
Run the formatting script to ensure code consistency:
./scripts/fmt.sh
This uses Ruff/Black for Python and Prettier for TypeScript/JSON/etc.
# Make sure uv is installed and you are in the project root
cd backend
# Update dependencies in pyproject.toml if needed
uv pip compile pyproject.toml -o requirements.txt --upgrade
# Update locked dev dependencies (if using hatch)
# hatch run compile:dev
cd ..
# Rebuild docker image if running in docker
docker compose build backend
cd frontend
pnpm up --latest # Or use npm/yarn equivalent
cd ..
# Rebuild docker image if running in docker
docker compose build frontend
Deployment uses Docker Compose with the docker-compose.prod.yml configuration file, which builds optimized production images and typically runs behind a reverse proxy like Nginx.
Prerequisites:
docker-compose.prod.yml includes labels for nginx-proxy/nginx-proxy.data/ directory exists on the host and contains the iCARE4CVD_Cohorts.xlsx file.Configuration:
.env file on the server with the production configuration (different hostnames, potentially different secrets, ensure JWT_SECRET is strong and persistent).
# Example Production .env
AUTH_ENDPOINT=https://auth0.com # Production Decentriq endpoint
CLIENT_ID=PROD_DECENTRIQ_CLIENT_ID
CLIENT_SECRET=PROD_DECENTRIQ_CLIENT_SECRET
DECENTRIQ_EMAIL=PROD_DECENTRIQ_ADMIN_EMAIL
DECENTRIQ_TOKEN=PROD_DECENTRIQ_API_TOKEN
JWT_SECRET=YOUR_VERY_STRONG_PRODUCTION_SECRET
[email protected],[email protected]
TOGATHER_API_KEY=PROD_TOGATHER_API_KEY
# These are used by nginx-proxy in the example prod compose file
# Ensure these match your DNS and proxy setup
FRONTEND_HOSTNAME=explorer.yourdomain.com
BACKEND_HOSTNAME=api.explorer.yourdomain.com
docker-compose.prod.yml if necessary:
NEXT_PUBLIC_API_URL build argument for the frontend.FRONTEND_URL environment variable for the backend.VIRTUAL_HOST, LETSENCRYPT_HOST, etc., environment variables to match your domain and reverse proxy setup. Remove them if using a different proxy solution.Deployment:
# Pull latest code
git pull origin main # Or your production branch
# Create/Update data directory and .env file on the server
# Build and deploy
docker compose -f docker-compose.prod.yml up --build -d
The --build flag ensures images are rebuilt if the code has changed. -d runs the containers in detached mode.
Configure Reverse Proxy: Ensure your reverse proxy correctly routes traffic for your chosen hostnames (e.g., explorer.yourdomain.com, api.explorer.yourdomain.com) to the appropriate container ports (frontend:3000, backend:80) and handles SSL termination.
The Oxigraph database persists its data in the data/db directory (mounted as a volume).
Use with caution! This deletes all stored metadata and mappings.
docker compose downrm -rf data/dbdocker compose up -d (or docker compose -f docker-compose.prod.yml up -d for production)Upon restart, the init_triplestore function in the backend will run:
iCARE4CVD_Cohorts.xlsx file.*_datadictionary.csv files found in data/cohorts/*/.[!WARNING] Resetting the database permanently deletes:
- All concept mappings added manually via the Cohort Explorer UI.
- Information about the Decentriq "Airlock" data preview setting for cohorts (it defaults to
falseupon reloading from files; Admins must re-upload affected dictionaries with the correct setting).
Dump the entire triplestore content (all named graphs) into a single N-Quads file.
# Run from the host machine
curl -X GET -H 'Accept: application/n-quads' http://localhost:7878/store > data/triplestore_dump_$(date +%Y%m%d).nq
curl command inside the backend container, ensuring the output path is accessible from the host (e.g., mounted /data volume).
# Run from the host machine
docker compose -f docker-compose.prod.yml exec backend curl -X GET -H 'Accept: application/n-quads' http://db:7878/store > data/triplestore_dump_$(date +%Y%m%d).nq
Reload a dump into an empty triplestore.
db service) is running.triplestore_dump_YYYYMMDD.nq) accordingly.# Run from the host machine
curl -X POST -T data/triplestore_dump_YYYYMMDD.nq -H 'Content-Type: application/n-quads' http://localhost:7878/store
curl command inside the backend container, ensuring the input dump file is accessible within the container (e.g., in the mounted /data volume).
# Run from the host machine
# Note: The path /data/triplestore_dump... is INSIDE the container
docker compose -f docker-compose.prod.yml exec backend curl -X POST -T /data/triplestore_dump_YYYYMMDD.nq -H 'Content-Type: application/n-quads' http://db:7878/store
To move the application to a different server:
data/ directory (including iCARE4CVD_Cohorts.xlsx, data/cohorts/, and data/db/) to the new server..env file on the new server.generate_mappings function in upload.py) is experimental and currently disabled.LICENSE.txt (Please review the license file for details).
π« Webapp to explore cohorts metadata for the iCARE4CVD project
See the codeWebapp enabling the upload, exploration, and mapping of cohort metadata, built for the iCARE4CVD project.
It interacts with a privacy computing platform (Decentriq) to create secure Data Clean Rooms (DCRs) where data scientists can run analyses on selected cohorts. The Cohort Explorer handles only cohort metadata; the actual sensitive cohort data is uploaded directly to Decentriq DCRs by data custodians after the DCR is configured via the Explorer.
This platform enables data custodians and data scientists to:
snomedct:12345) to URIs.[!IMPORTANT] Only the designated owner of a cohort (as specified in the central
iCARE4CVD_Cohorts.xlsxspreadsheet) and platform administrators can upload or modify the data dictionary and mappings for that cohort.
The Cohort Explorer follows a standard web application architecture with three main components, orchestrated using Docker Compose:
frontend/): A client-side web application built with Next.js (React) and TypeScript. It provides the user interface for exploring cohorts, managing uploads, defining mappings, and initiating DCR creation. It interacts with the Backend API. Styling is done with TailwindCSS and DaisyUI.backend/): A Python API server built with FastAPI. It handles:
db): An Oxigraph triplestore instance. It stores all cohort and variable metadata as RDF triples, conforming to the custom iCARE4CVD ontology. It exposes a SPARQL endpoint accessible only by the Backend.Data Flow:
iCARE4CVD_Cohorts.xlsx file containing general information about all participating cohorts. This data is parsed by the backend and stored in a dedicated graph (icare:graph/metadata) in the triplestore.cohort-explorer-ontology.ttl). These triples are stored in a named graph specific to the cohort (e.g., icare:cohort/{cohort_id})./insert-triples endpoint), which stores these mappings as RDF triples in a separate named graph for that cohort (e.g., icare:cohort/{cohort_id}/mappings)./create-provision-dcr endpoint).Authentication:
/auth/callback).[!NOTE] The current approach of fetching all metadata to the frontend works well for the expected scale of the iCARE4CVD project. If the number of cohorts/variables grows significantly, this could be optimized by performing filtering and searching directly via SPARQL queries on the backend, returning only the relevant subset of data to the frontend.
Metadata is stored in an Oxigraph triplestore using RDF. The structure is defined by a custom OWL ontology (cohort-explorer-ontology.ttl) with the namespace icare: <https://w3id.org/icare4cvd/>.
Key Classes:
icare:Cohort: Represents a study cohort. Linked to its variables via icare:hasVariable. Has properties like dc:identifier, icare:institution, icare:cohortType, icare:studyParticipants, etc. General cohort metadata resides in the icare:graph/metadata named graph.icare:Variable: Represents a variable (column) within a cohort's data dictionary. Has properties like dc:identifier, rdfs:label, icare:varType, icare:units, icare:omop domain, etc. Linked to categories via icare:categories. Can be mapped using icare:conceptId (original) and icare:mappedId (user-defined). Variable definitions reside in the cohort's named graph (e.g., icare:cohort/{cohort_id}).icare:VariableCategory: Represents a specific category for a categorical variable. Has rdf:value, rdfs:label, and can be mapped using icare:mappedId.Mappings defined via the UI are stored in a separate named graph (e.g., icare:cohort/{cohort_id}/mappings) to distinguish them from the base metadata derived directly from the uploaded file.
[!WARNING] Running the Cohort Explorer requires the spreadsheet containing general cohort information. It must be an Excel file named
iCARE4CVD_Cohorts.xlsxlocated in thedata/directory (create this directory if it doesn't exist). The spreadsheet needs a sheet namedDescriptionswith specific columns (see original README section for details).
Clone the repository:
git clone <repository-url>
cd cohort-explorer
Create .env file: Copy the required environment variables (OAuth Client ID/Secret, Decentriq API token, JWT Secret, Admin emails, etc.) into a .env file at the root of the repository. Ask project administrators for necessary secrets.
# Example .env structure
AUTH_ENDPOINT=https://auth0.com # Or your Decentriq auth endpoint
CLIENT_ID=YOUR_DECENTRIQ_CLIENT_ID
CLIENT_SECRET=YOUR_DECENTRIQ_CLIENT_SECRET
DECENTRIQ_EMAIL=YOUR_DECENTRIQ_ADMIN_EMAIL # Email linked to the token below
DECENTRIQ_TOKEN=YOUR_DECENTRIQ_API_TOKEN # Get from Decentriq platform
JWT_SECRET=GENERATE_A_STRONG_SECRET # e.g., using python -c "import secrets; print(secrets.token_urlsafe(32))"
[email protected],[email protected] # Comma-separated list of admin emails
TOGATHER_API_KEY=YOUR_TOGATHER_API_KEY # If using Togather integration
# Frontend needs this at build time if not using default localhost:3000
# NEXT_PUBLIC_API_URL=http://localhost:3000
[!WARNING]
- Ensure the user email you intend to log in with during development is included in the
ADMINSlist if you need administrative privileges (like uploading any cohort).- The
DECENTRIQ_TOKENis required for the backend to interact with the Decentriq API (e.g., for DCR creation).
Prepare Data Directory:
data/ directory at the root of the project if it doesn't exist.iCARE4CVD_Cohorts.xlsx file inside the data/ directory.data/cohorts/.data/db/.Start Services (Docker - Recommended):
docker compose up --build
This command builds the images (if necessary) and starts the frontend, backend, and db services defined in docker-compose.yml. Hot-reloading is enabled for both the frontend and backend.
http://localhost:3001.http://localhost:3000.http://localhost:7878.[!IMPORTANT] For Decentriq OAuth to function correctly during local development, the provider must be configured to allow redirects to
http://localhost:3000/auth/callback.
[!TIP] For optimal IDE support (autocompletion, type checking) within the
backendcode, install dependencies locally usinguv:uv sync --system # Or use a virtual environmentSimilarly, for the frontend:
cd frontend pnpm install # or npm install / yarn install cd ..
Start Services (Manual - Alternative):
You can run each component manually outside Docker. Ensure the .env file is present in the backend/ directory for this method.
docker compose up db
cd backend
# Assumes .env file is here
DEV_MODE=true uv run uvicorn src.main:app --host 0.0.0.0 --port 3000 --reload
cd ..
cd frontend
pnpm install # If not already done
pnpm dev # Starts on http://localhost:3001 by default
cd ..
Run the formatting script to ensure code consistency:
./scripts/fmt.sh
This uses Ruff/Black for Python and Prettier for TypeScript/JSON/etc.
# Make sure uv is installed and you are in the project root
cd backend
# Update dependencies in pyproject.toml if needed
uv pip compile pyproject.toml -o requirements.txt --upgrade
# Update locked dev dependencies (if using hatch)
# hatch run compile:dev
cd ..
# Rebuild docker image if running in docker
docker compose build backend
cd frontend
pnpm up --latest # Or use npm/yarn equivalent
cd ..
# Rebuild docker image if running in docker
docker compose build frontend
Deployment uses Docker Compose with the docker-compose.prod.yml configuration file, which builds optimized production images and typically runs behind a reverse proxy like Nginx.
Prerequisites:
docker-compose.prod.yml includes labels for nginx-proxy/nginx-proxy.data/ directory exists on the host and contains the iCARE4CVD_Cohorts.xlsx file.Configuration:
.env file on the server with the production configuration (different hostnames, potentially different secrets, ensure JWT_SECRET is strong and persistent).
# Example Production .env
AUTH_ENDPOINT=https://auth0.com # Production Decentriq endpoint
CLIENT_ID=PROD_DECENTRIQ_CLIENT_ID
CLIENT_SECRET=PROD_DECENTRIQ_CLIENT_SECRET
DECENTRIQ_EMAIL=PROD_DECENTRIQ_ADMIN_EMAIL
DECENTRIQ_TOKEN=PROD_DECENTRIQ_API_TOKEN
JWT_SECRET=YOUR_VERY_STRONG_PRODUCTION_SECRET
[email protected],[email protected]
TOGATHER_API_KEY=PROD_TOGATHER_API_KEY
# These are used by nginx-proxy in the example prod compose file
# Ensure these match your DNS and proxy setup
FRONTEND_HOSTNAME=explorer.yourdomain.com
BACKEND_HOSTNAME=api.explorer.yourdomain.com
docker-compose.prod.yml if necessary:
NEXT_PUBLIC_API_URL build argument for the frontend.FRONTEND_URL environment variable for the backend.VIRTUAL_HOST, LETSENCRYPT_HOST, etc., environment variables to match your domain and reverse proxy setup. Remove them if using a different proxy solution.Deployment:
# Pull latest code
git pull origin main # Or your production branch
# Create/Update data directory and .env file on the server
# Build and deploy
docker compose -f docker-compose.prod.yml up --build -d
The --build flag ensures images are rebuilt if the code has changed. -d runs the containers in detached mode.
Configure Reverse Proxy: Ensure your reverse proxy correctly routes traffic for your chosen hostnames (e.g., explorer.yourdomain.com, api.explorer.yourdomain.com) to the appropriate container ports (frontend:3000, backend:80) and handles SSL termination.
The Oxigraph database persists its data in the data/db directory (mounted as a volume).
Use with caution! This deletes all stored metadata and mappings.
docker compose downrm -rf data/dbdocker compose up -d (or docker compose -f docker-compose.prod.yml up -d for production)Upon restart, the init_triplestore function in the backend will run:
iCARE4CVD_Cohorts.xlsx file.*_datadictionary.csv files found in data/cohorts/*/.[!WARNING] Resetting the database permanently deletes:
- All concept mappings added manually via the Cohort Explorer UI.
- Information about the Decentriq "Airlock" data preview setting for cohorts (it defaults to
falseupon reloading from files; Admins must re-upload affected dictionaries with the correct setting).
Dump the entire triplestore content (all named graphs) into a single N-Quads file.
# Run from the host machine
curl -X GET -H 'Accept: application/n-quads' http://localhost:7878/store > data/triplestore_dump_$(date +%Y%m%d).nq
curl command inside the backend container, ensuring the output path is accessible from the host (e.g., mounted /data volume).
# Run from the host machine
docker compose -f docker-compose.prod.yml exec backend curl -X GET -H 'Accept: application/n-quads' http://db:7878/store > data/triplestore_dump_$(date +%Y%m%d).nq
Reload a dump into an empty triplestore.
db service) is running.triplestore_dump_YYYYMMDD.nq) accordingly.# Run from the host machine
curl -X POST -T data/triplestore_dump_YYYYMMDD.nq -H 'Content-Type: application/n-quads' http://localhost:7878/store
curl command inside the backend container, ensuring the input dump file is accessible within the container (e.g., in the mounted /data volume).
# Run from the host machine
# Note: The path /data/triplestore_dump... is INSIDE the container
docker compose -f docker-compose.prod.yml exec backend curl -X POST -T /data/triplestore_dump_YYYYMMDD.nq -H 'Content-Type: application/n-quads' http://db:7878/store
To move the application to a different server:
data/ directory (including iCARE4CVD_Cohorts.xlsx, data/cohorts/, and data/db/) to the new server..env file on the new server.generate_mappings function in upload.py) is experimental and currently disabled.LICENSE.txt (Please review the license file for details).