Step 1: Scaffold the New Plugin Package
Create a new directory for the Streamlit plugin alongside your other packages. By isolating it, users who only want to write backend scripts won't be forced to install Streamlit.
Directory Structure:
imednet-python-sdk/
├── packages/
│ ├── core/
│ ├── plugins-workflows/
│ ├── providers-airflow/
│ └── plugins-streamlit/ <-- NEW DIRECTORY
│ ├── pyproject.toml
│ └── src/imednet_streamlit/
│ ├── __init__.py
│ ├── app.py <-- The main Streamlit dashboard
│ └── components/ <-- Reusable UI widgets
Step 2: Define the Isolated Dependencies
Create the pyproject.toml inside packages/plugins-streamlit/. Notice how it explicitly depends on your core imednet package and optionally the imednet-workflows package if you want to reuse the Pandas logic you built there.
packages/plugins-streamlit/pyproject.toml
[project]
name = "imednet-streamlit"
version = "0.1.0"
description = "Interactive Streamlit reporting dashboards for iMednet EDC."
dependencies = [
"imednet", # Your core SDK
"imednet-workflows", # Optional: if you need your Pandas data extractors
"streamlit>=1.30.0",
"altair>=5.0.0" # For charting
]
[build-system]
requires = ["hatchling"] # Or whichever build system your workspace uses
build-backend = "hatchling.build"
Step 3: Build the Dashboard Logic
Inside your new package, write the Streamlit application. It will import the strictly isolated core SDK to fetch data, ensuring all API calls go through your standardized retry policies and authentication layers.
packages/plugins-streamlit/src/imednet_streamlit/app.py
import streamlit as st
import pandas as pd
from imednet import ImednetSDK
from imednet.auth import APIKeyAuth
st.set_page_config(page_title="iMednet Reporting", layout="wide")
st.title("iMednet EDC Operations Dashboard")
# 1. Safely handle credentials via Streamlit's secrets management
api_key = st.sidebar.text_input("API Key", type="password")
study_key = st.sidebar.text_input("Study Key")
if api_key and study_key:
# 2. Instantiate the Core SDK
auth = APIKeyAuth(api_key=api_key, security_key="your-sec-key")
sdk = ImednetSDK(auth=auth)
st.success("Connected to iMednet API")
# 3. Fetch Data using the SDK (Using caching to prevent API spam)
@st.cache_data(ttl=600)
def fetch_queries():
# Using the core SDK to fetch the data
queries = sdk.queries.list(study_key=study_key)
# Convert to a format Streamlit/Pandas likes
return pd.DataFrame([q.model_dump() for q in queries])
with st.spinner("Fetching queries..."):
df_queries = fetch_queries()
# 4. Render UI
st.subheader("Query Status Overview")
st.dataframe(df_queries)
# Example Altair Chart
st.bar_chart(df_queries['annotationStatus'].value_counts())
else:
st.warning("Please enter your credentials in the sidebar to begin.")
Step 4: Dynamically Attach it to the Core CLI
The best developer experience is allowing users to launch the dashboard directly from your CLI (e.g., imednet dashboard).
Update your core CLI to dynamically load a dashboard command, degrading gracefully if the user hasn't installed the imednet-streamlit package.
packages/core/src/imednet/cli/main.py
import typer
import subprocess
import sys
app = typer.Typer(help="iMednet Developer CLI")
# ... (Existing core commands) ...
@app.command("dashboard")
def run_dashboard():
"""
Launch the interactive iMednet Streamlit dashboard.
(Requires the 'imednet-streamlit' plugin)
"""
try:
# Check if the package is installed
import imednet_streamlit
import imednet_streamlit.app as app_module
# Resolve the absolute path to the app.py file
app_path = app_module.__file__
typer.secho("Launching iMednet Dashboard...", fg=typer.colors.GREEN)
# Subprocess call to launch the streamlit server
subprocess.run([sys.executable, "-m", "streamlit", "run", app_path])
except ImportError:
typer.secho(
"Dashboard plugin not found. Please install it by running:\n"
"pip install imednet-streamlit",
fg=typer.colors.RED
)
raise typer.Exit(code=1)
Step 5: Update the Workspace Build Tooling
Finally, ensure your root orchestrator (like your Makefile) knows about the new package so it gets linted and tested automatically.
Makefile
.PHONY: test lint
# Added the new package to the list
PACKAGES = packages/core packages/plugins-workflows packages/providers-airflow packages/plugins-streamlit
lint:
@for pkg in $(PACKAGES); do \
echo "Linting $$pkg..."; \
ruff check $$pkg/src $$pkg/tests; \
done
Why this approach is bulletproof:
- Zero Core Bloat: Users deploying your standard SDK in an AWS Lambda function won't be forced to download the massive
streamlit and altair binaries.
- Seamless UX: Users who do want the UI just run
pip install imednet-streamlit and suddenly the imednet dashboard CLI command magically starts working.
- API Consistency: The Streamlit app isn't making rogue
requests.get() calls; it relies entirely on your hardened packages/core/src/imednet/sdk.py, meaning it automatically inherits all the retry logic and structural validation you've already built!
Step 1: Scaffold the New Plugin Package
Create a new directory for the Streamlit plugin alongside your other packages. By isolating it, users who only want to write backend scripts won't be forced to install Streamlit.
Directory Structure:
Step 2: Define the Isolated Dependencies
Create the
pyproject.tomlinsidepackages/plugins-streamlit/. Notice how it explicitly depends on your coreimednetpackage and optionally theimednet-workflowspackage if you want to reuse the Pandas logic you built there.packages/plugins-streamlit/pyproject.tomlStep 3: Build the Dashboard Logic
Inside your new package, write the Streamlit application. It will import the strictly isolated core SDK to fetch data, ensuring all API calls go through your standardized retry policies and authentication layers.
packages/plugins-streamlit/src/imednet_streamlit/app.pyStep 4: Dynamically Attach it to the Core CLI
The best developer experience is allowing users to launch the dashboard directly from your CLI (e.g.,
imednet dashboard).Update your core CLI to dynamically load a
dashboardcommand, degrading gracefully if the user hasn't installed theimednet-streamlitpackage.packages/core/src/imednet/cli/main.pyStep 5: Update the Workspace Build Tooling
Finally, ensure your root orchestrator (like your
Makefile) knows about the new package so it gets linted and tested automatically.MakefileWhy this approach is bulletproof:
streamlitandaltairbinaries.pip install imednet-streamlitand suddenly theimednet dashboardCLI command magically starts working.requests.get()calls; it relies entirely on your hardenedpackages/core/src/imednet/sdk.py, meaning it automatically inherits all the retry logic and structural validation you've already built!