%%{init: {'theme':'base','themeVariables':{'primaryColor':'#28344f','primaryTextColor':'#eef2fb','primaryBorderColor':'#7d9bd6','lineColor':'#aab8d6','clusterBkg':'#161d33','clusterBorder':'#3a476a','tertiaryColor':'#20406a','tertiaryTextColor':'#eef2fb','tertiaryBorderColor':'#7d9bd6','secondaryColor':'#20406a','secondaryTextColor':'#eef2fb'}}}%%
flowchart LR
S[Startup] --> C[Conduct] --> L[DB Lock] --> R[Reporting]
S -.-> M1["Protocol<br/>SAP · FPFV"]
C -.-> M2[LPLV]
L -.-> M3[DBL]
R -.-> M4["TLFs · CSR"]
Study Lifecycle Monitor
A visual, runnable guide to the operational-health console
Run / share / read / build - pick your mode. This page shows the console (diagrams + a live roll-up computed from the portfolio), the run-and-build page lets you run it and nudge a what-if, the dashboard is a shareable snapshot, and the run-and-build page shows how it is made. Synthetic, no PHI, no participant-level data. A screening lens for operational health, never a reported clinical number.
What it watches
Operational health of every study from protocol to CSR, on one screen: are milestones slipping, is the query backlog clearing before database lock, is TLF production keeping pace, is double-programming on track.
How the risk score works - it is transparent
No black box. Each study gets four operational sub-scores (0-100), blended into one:
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#28344f','primaryTextColor':'#eef2fb','primaryBorderColor':'#7d9bd6','lineColor':'#aab8d6','clusterBkg':'#161d33','clusterBorder':'#3a476a','tertiaryColor':'#20406a','tertiaryTextColor':'#eef2fb','tertiaryBorderColor':'#7d9bd6','secondaryColor':'#20406a','secondaryTextColor':'#eef2fb'}}}%%
flowchart LR
T["Timeline<br/>overdue + lock pressure"] --> O
D["Data<br/>queries + clean/SDV"] --> O
F["TLF<br/>finalized vs pressure"] --> O
Rs["Resource<br/>double-prog + discrep"] --> O
O{{"overall = 0.30·T + 0.28·D<br/>+ 0.27·F + 0.15·R"}}
O --> RAG["RAG bands<br/>red · amber · green"]
Because it is a formula, you can defend every number and change what “risk” means in one place (score_study() in R/logic.R).
The live portfolio - computed from the data
Everything below is rendered from four CSVs, the same files the Shiny app reads: studies.csv, milestones.csv, deliverables.csv, and query_trend.csv (in the shiny/data/ folder). Edit those CSVs (or run the app) and these move.
| Severity | Study | Signal | Detail |
|---|---|---|---|
| red | CP-108 | TLF production behind | 10% finalized while in Reporting |
| red | CP-108 | Milestone overdue | TLFs, CSR past due |
| red | CP-106 | TLF production behind | 34% finalized while in DB Lock |
| red | CP-105 | TLF production behind | 24% finalized while in DB Lock |
| amber | CP-108 | Double-programming lag | 42% double-programmed, 16 open discrepancies |
| amber | CP-106 | Double-programming lag | 58% double-programmed, 14 open discrepancies |
| amber | CP-105 | Double-programming lag | 43% double-programmed, 2 open discrepancies |
| amber | CP-107 | TLF production behind | 44% finalized while in Reporting |
| amber | CP-107 | Double-programming lag | 55% double-programmed, 7 open discrepancies |
Run it - and change it
shiny::runApp("lifecycle_dashboard/shiny")
# needs: install.packages(c("shiny","bslib","DT","ggplot2"))Pick a study, then move the what-if sliders (open queries, double-programming) and watch that study’s row in the heatmap re-colour. The run-and-build page shows how the app is built from R/logic.R.
Going live: R/gen_data.R writes the four CSVs (studies.csv, milestones.csv, deliverables.csv, query_trend.csv) so you can see the shape. Replace them with a read-only CTMS / EDC export carrying the same columns and the console is your portfolio. Nothing writes back to a source system.
The other formats
| Mode | Artifact |
|---|---|
| Run | Run & build the Shiny app |
| Share (no R) | Dashboard |
| Build | Run-and-build walkthrough |
Synthetic, deterministic, no PHI. Operational screening, not a clinical readout.