Design · Requirement #352 · safety.viz

The basic app: load, map and view a study on one page

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.

2026-10-02 · Requirement: the basic app (#352) · Objective: data loading and mapping (#329) · Depends on: the standard domain set and its manifest (#325)

1What the user sees

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.

  1. The page opens on the demo study when it is served from the site, and on an empty data panel when it is the downloaded file.
  2. The user drops files. Each file is read, assigned to a domain, and shown with how many of that domain's columns it carries.
  3. Under each file is one mapping table. Rows the tool is sure of are filled; rows it guessed are filled and say so; rows it could not fill say which charts need them.
  4. The chart list on the left reports, for every chart, whether the data supports it and, if not, exactly what is missing.
  5. Choosing a chart draws it from the user's data. Choosing another replaces it.
  6. The mapping downloads as a small JSON file. Dropping that file back onto the page restores it.

2Why this is small

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.

3Three things get mapped, not one

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.

WhatThe questionHow it is pre-filled
Which file is which domainIs 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.
ColumnsWhich 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 namesWhat 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.
Why measure names are in the basic version

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.

4The fourteen charts

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.

ChartReadsMust haveMeasure names
Safety HistogramLabs and vitalsmeasure, result
Safety Outlier ExplorerLabs and vitalsmeasure, result
Safety Results Over TimeLabs and vitalsmeasure, result, visit
Safety Shift PlotLabs and vitalsmeasure, result, visit
Safety Delta-DeltaLabs and vitalsparticipant, measure, result, visit
Hepatic Safety ExplorerLabs and vitalsparticipant, measure, result, upper limit of normalALT, AST, total bilirubin, ALP
Hepatic ALT WaterfallLabs and vitalsparticipant, measure, result, upper limit of normal, armALT, total bilirubin
Nephrotoxicity ExplorerLabs and vitalsparticipant, measure, resultcreatinine
QT Safety ExplorerECGmeasure, result, arm, baseline valueQTcF, QTcB, heart rate
Adverse Event ExplorerAdverse eventsparticipant, body system, preferred term, arm
Adverse Event TimelinesAdverse eventsparticipant, sequence, start day, end day, term
Time-to-Event ExplorerAdverse events and subjectboth files; participant, event day, follow-up day
Participant ProfileLabs and vitals, plus adverse eventsparticipant, measure, result, upper limit of normalALT, AST, total bilirubin, ALP
Patient Journey ExplorerSix domains of its ownNot 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:

5The rules

Placing a file

Guessing

Carried over from the August data-loading designs

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.

Chart status

StatusMeansWhat the page says
readyThe domain is loaded and every required column and measure name is mapped.Nothing more; choosing it draws it.
missingThe 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 fileNo file is placed in a domain the chart reads."No ECG file loaded". A legitimate final state, not an error.
needs more domainsThe chart reads domains the manifest does not name.Which chart and why.
did not drawThe 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.

Filters and grouping

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).

Limits stated on the page

6How it is built

PieceWhereWhat it does
Parsersrc/app/parse.jsCSV and JSON text to rows. The quote-aware parser the demo pages already use, in one place.
Detectionsrc/app/detect.jsColumn names and the manifest in; the best domain and its count out.
Mappingsrc/app/mapping.jsBuilds 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.
Statussrc/app/status.jsMapping and manifest in; one status per chart out, with the names of what is missing.
Chart recipessrc/app/charts.jsPer chart: which domains it takes, how to turn a mapping into its settings, and how to hand it its data.
Pagesrc/app/page.jsThe chart list, the data panel and the single chart mount. The only piece that touches the document.
Bundlenpm run build:appOne script holding the charts and the app, and one HTML file with that script and its styles inlined.
Sitescripts/site.mjsBuilds portfolio/index.html and portfolio/safety.viz-app.html, and copies the demo extracts beside them for the Load demo data button.

7Tests and evidence

8Four pull requests, in order

Task 1 · needs the manifest

Add the app core: parsing, placing, mapping and chart status

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

Add the portfolio page on the demo study

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

Add the data panel: load files, correct the mapping, redraw

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

Add the single-file build and serve it from the site

One HTML file that opens from disk with no network, with its size printed by the build, linked from the portfolio page.

9Decisions taken in this design

Each is the default the session builds unless you say otherwise.

D1Key measure names are mapped in the basic version

Section 3. It adds one kind of row to the mapping table and makes five charts work that otherwise would not.

Taken: in scope. The requirement's definition of done is revised to say so.
D2Guesses are filled but always labelled

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.

Taken: fill and label.
D3One chart on screen at a time

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.

Taken: a list of all, one drawn.
D4The app is a separate bundle and is not committed

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.

Taken: separate, built by the site.
D5The mapping file can be dropped back in

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.

Taken: in scope, marked provisional in the file itself.
D6The Participant Profile is a rail, not a destination

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).

Taken: listed with a status; opens where it already does.
D7The Patient Journey Explorer waits for the manifest

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.

Taken: listed as "needs more domains"; the supported count on the demo study is 13 of 14.
D8Release

safety.viz v1.9.0, whose milestone exists and is empty. The manifest ships in v1.8.0 before it.

Taken: v1.9.0, proposed until you confirm the version plan.

10What stays with the existing requirements

RequirementKeeps
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.