Design · Requirement #352 · safety.viz
A study programmer drops their own files onto one page, corrects what the tool guessed, and looks at their study in the safety charts. Nothing is installed and nothing leaves the browser. This is the thinnest version of that path that is still useful, built so the existing requirements widen it instead of replacing it.
One page at portfolio/ on the safety.viz site, with two parts: a list of charts on the left and whatever the user chose on the right. The right side is either the data panel or one chart.
| The charts need | Your column | |
|---|---|---|
| Participant | SUBJECT ▾ | guessed |
| Measure name | LBTEST ▾ | guessed |
| Result | STRESN ▾ | same name |
| Upper limit of normal | choose… ▾ | needed by 3 charts |
| ALT is called | Alanine Aminotransferase ▾ | guessed |
Changing a row updates the chart statuses on the left at once.
Read from safety.viz dev on 2026-10-02.
What does not exist yet: the manifest, any page hosting more than one chart, and a shared file parser. The same CSV parser is pasted into each chart's demo script.
The requirement as filed spoke only of columns. Reading the charts showed two more things a user's data has to tell the tool, and the basic app handles all three in the same table.
| What | The question | How it is pre-filled |
|---|---|---|
| Which file is which domain | Is this the labs file, the adverse events file, the subject file or the ECG file? | The domain whose columns the file matches best, with the count shown. The user can change it, or set a file aside. |
| Columns | Which of your columns holds the participant, the measure name, the result, and so on? | Same name first; then a short list of known alternatives, such as the SDTM name. |
| Key measure names | What does your data call ALT, AST, total bilirubin, alkaline phosphatase, creatinine, QTcF, QTcB and heart rate? | A short list of known names per measure, compared with the values actually present in the measure column. |
Five charts find their measures by name: the hepatic explorer, the hepatic waterfall, the kidney explorer, the QT explorer and the participant profile. The library's default for ALT is "Aminotransferase, alanine (ALT)"; the site's own demo data calls it "Alanine Aminotransferase", and every demo page overrides the setting by hand. Without this row in the mapping table those five charts would draw nothing on the demo data, let alone on a user's.
What each chart reads and what it cannot draw without, taken from its schema. "Must have" is the schema's required list; everything else degrades when absent.
| Chart | Reads | Must have | Measure names |
|---|---|---|---|
| Safety Histogram | Labs and vitals | measure, result | |
| Safety Outlier Explorer | Labs and vitals | measure, result | |
| Safety Results Over Time | Labs and vitals | measure, result, visit | |
| Safety Shift Plot | Labs and vitals | measure, result, visit | |
| Safety Delta-Delta | Labs and vitals | participant, measure, result, visit | |
| Hepatic Safety Explorer | Labs and vitals | participant, measure, result, upper limit of normal | ALT, AST, total bilirubin, ALP |
| Hepatic ALT Waterfall | Labs and vitals | participant, measure, result, upper limit of normal, arm | ALT, total bilirubin |
| Nephrotoxicity Explorer | Labs and vitals | participant, measure, result | creatinine |
| QT Safety Explorer | ECG | measure, result, arm, baseline value | QTcF, QTcB, heart rate |
| Adverse Event Explorer | Adverse events | participant, body system, preferred term, arm | |
| Adverse Event Timelines | Adverse events | participant, sequence, start day, end day, term | |
| Time-to-Event Explorer | Adverse events and subject | both files; participant, event day, follow-up day | |
| Participant Profile | Labs and vitals, plus adverse events | participant, measure, result, upper limit of normal | ALT, AST, total bilirubin, ALP |
| Patient Journey Explorer | Six domains of its own | Not drawable from the four standard domains; listed as "needs more domains" | |
Three charts do not mount like the rest, so the app carries one small recipe per chart saying how to hand it its data:
The four open.gismo loading designs measured that a matcher's most confident wrong guess is the dangerous one, because it empties charts silently. Two of their conclusions are kept here: a guess is always labelled as a guess, and a gap is priced in the charts it turns off, never in a count of columns. The recommendation is at The read, and the bench behind it.
| Status | Means | What the page says |
|---|---|---|
| ready | The domain is loaded and every required column and measure name is mapped. | Nothing more; choosing it draws it. |
| missing | The domain is loaded and something required is unmapped. | The names of what is missing, in the user's terms: "needs upper limit of normal". |
| no file | No file is placed in a domain the chart reads. | "No ECG file loaded". A legitimate final state, not an error. |
| needs more domains | The chart reads domains the manifest does not name. | Which chart and why. |
| did not draw | The chart was ready, was chosen, and threw or drew nothing. | The chart's own message. Status is corrected by what happened, not left at "ready". |
The supported count at the top of the list is the number of charts that are ready. On the demo study it reads 13 of 14, the exception being the Patient Journey Explorer.
Each chart keeps its own filter controls. The app offers a chart whichever of arm, site, sex and race are mapped in the domain it reads. One set of filters across every chart stays with the study-level settings requirement (#327).
| Piece | Where | What it does |
|---|---|---|
| Parser | src/app/parse.js | CSV and JSON text to rows. The quote-aware parser the demo pages already use, in one place. |
| Detection | src/app/detect.js | Column names and the manifest in; the best domain and its count out. |
| Mapping | src/app/mapping.js | Builds the pre-filled mapping for a domain, with each row's source: same name, guessed or empty. Holds the lists of known alternatives for columns and measure names. |
| Status | src/app/status.js | Mapping and manifest in; one status per chart out, with the names of what is missing. |
| Chart recipes | src/app/charts.js | Per chart: which domains it takes, how to turn a mapping into its settings, and how to hand it its data. |
| Page | src/app/page.js | The chart list, the data panel and the single chart mount. The only piece that touches the document. |
| Bundle | npm run build:app | One script holding the charts and the app, and one HTML file with that script and its styles inlined. |
| Site | scripts/site.mjs | Builds portfolio/index.html and portfolio/safety.viz-app.html, and copies the demo extracts beside them for the Load demo data button. |
Task 1 · needs the manifest
The four pure pieces, the lists of known alternatives, the requirement matrix and their unit tests. Nothing visible yet. It reads the manifest, so it starts when the manifest task (safety.viz#138) has merged.
Task 2
The chart recipes, the page, the app bundle and the site generator's new page. After this the dev site shows every chart behind one list, with statuses, on the demo data.
Task 3
The drop zone, the mapping table, the mapping file, the renamed-column study and the browser tests that load it and prove nothing leaves the page.
Task 4
One HTML file that opens from disk with no network, with its size printed by the build, linked from the portfolio page.
Each is the default the session builds unless you say otherwise.
Section 3. It adds one kind of row to the mapping table and makes five charts work that otherwise would not.
The data loading objective asks for a mapping auto-filled from the detected standard. The August designs argued for filling nothing automatically. This takes the middle: same-name and known-alternative matches are filled, each says which it was, and nothing fuzzier is attempted.
The portfolio objective speaks of every chart on one page. Drawing fourteen at once on a 5.9 MB extract is slow and unreadable; the old app also showed one at a time. Every chart is on the page's list; one is drawn.
Keeps the chart library's bundle, and so the R widgets, unchanged in size. The cost is that the app cannot be loaded from the committed dist/; it exists on the site and as a build output.
Not in the requirement as filed. Without it a user who maps thirty columns and closes the tab starts again. Its format is provisional and is replaced by the study configuration when that requirement (#327) lands.
It opens beside the charts that already host it. Opening it from every chart, on the mapped identifier, stays with the portfolio shell (#326) and fit and load (#335).
It reads exposure, adverse events, labs, concomitant medications, medical history and disposition under SDTM names. The four-domain manifest names none of those as it needs them.
safety.viz v1.9.0, whose milestone exists and is empty. The manifest ships in v1.8.0 before it.
| Requirement | Keeps |
|---|---|
| The portfolio shell (#326) | The participant profile as one shared drill-down from any chart; the evidence page with one test per chart. |
| Study-level settings and filters (#327) | All of it: arm, site and population filters across every chart, and the study configuration that replaces the provisional mapping file. |
| Local file loading and standard detection (#333) | Detecting the standard, showing the runner-up, the full set of placement messages, and extending the renamed-column study. |
| The mapping module (#334) | Field-by-field validation of values, such as a result column that is not numeric, and saving into the study configuration. |
| Fit and load (#335) | The drill-down following the mapped participant identifier across charts and domains. |
| The single-file build (#336) | The inlined demo study, the size budget and the fresh-machine test in Chrome, Safari and Edge. |