Assemble Study Data and Analysis Data Reviewer’s Guides from metadata.
Study Data and Analysis Data Reviewer’s Guides are written by hand in Word on every submission, yet most of their content is mechanical: a dataset inventory, a validation-findings table with sponsor explanations, and a list of the programs that produced the deliverables. All of that is already sitting in metadata. reviewerguider populates those sections and leaves the narrative ones – study background, rationale for non-standard decisions – to a human.
install.packages(
"https://clincoder.cloud/reviewerguider/reviewerguider_0.0.1.tar.gz",
repos = NULL, type = "source"
)
library(reviewerguider)
guide <- guide_spec(
"CDISCPILOT01",
type = "sdrg",
sponsor = "Acme Pharma",
protocol_title = "A Study of Drug X",
standards_version = "SDTMIG 3.4"
)
adsl <- read.csv(system.file("extdata", "adsl.csv", package = "reviewerguider"))
adae <- read.csv(system.file("extdata", "adae.csv", package = "reviewerguider"))
keys <- read.csv(system.file("extdata", "dataset_keys.csv", package = "reviewerguider"))
findings <- read.csv(system.file("extdata", "findings.csv", package = "reviewerguider"))
explanations <- read.csv(system.file("extdata", "explanations.csv", package = "reviewerguider"))
progs <- system.file("extdata", "programs", package = "reviewerguider")
guide <- add_section(guide, "Dataset Inventory",
section_datasets(list(adsl = adsl, adae = adae), metadata = keys),
type = "datasets")
guide <- add_section(guide, "Validation Findings", section_findings(findings, explanations),
type = "findings")
guide <- add_section(guide, "Programs Submitted", section_programs(progs), type = "programs")
guide <- add_narrative(
guide, "Introduction",
"This SDRG accompanies the CDISCPILOT01 SDTM submission and describes the datasets, conformance findings, and programs delivered."
)
guide
#> ── CDISCPILOT01 reviewer's guide (SDRG) ──
#> ℹ Sponsor: Acme Pharma
#> ℹ 4 section(s)
#> ✔ [datasets] Dataset Inventory - 2 row(s)
#> ✔ [findings] Validation Findings - 6 row(s)
#> ✔ [programs] Programs Submitted - 2 row(s)
#> ✔ [narrative] Introduction - 128 char(s)
render_guide(guide, "sdrg.html", format = "html")section_datasets(datasets, metadata) – dataset name, label, class/structure, key variables, record and variable counts, and in-memory size, from a named list of data frames or an already-assembled metadata table. Key variables come from metadata when the study supplies declared keys, and from a name-pattern guess otherwise; the key_source column says which, per row. Returns a tibble.section_findings(findings, explanations) – each finding with its severity, records affected, and the sponsor’s explanation where one is supplied. Findings without one are flagged needs_explanation = TRUE, since an unexplained finding is what gets a submission questioned.section_programs(path, pattern) – file name, path relative to path, size, last-modified time, and the program’s leading comment/header block as its description, for every .R/.sas file in a directory.section_findings() takes a plain data frame in the Pinnacle 21 report column schema:
| Column | Meaning |
|---|---|
Dataset |
dataset the finding is against, e.g. "AE"
|
Variable |
variable(s) involved |
Rule ID |
validator rule identifier, e.g. "SD0021"
|
Message |
the rule message |
Records affected |
how many records the finding covers |
Severity |
"Error", "Warning", "Notice", … |
Names are matched case- and separator-insensitively, so Records affected, Records.affected (what read.csv() gives you) and records_affected are the same column; the returned tibble uses the snake_case names. A missing required column is an error that names the column.
Anything producing that shape works – a Pinnacle 21 Excel/CSV export read with readxl::read_excel(), a CDISC CORE run, or your own rule engine. reviewerguider deliberately imports no validator package, so a change in one cannot break the other. The bundled findings.csv is a real Pinnacle 21 run against the CDISCPILOT01 SDTM data.
Generated mechanically, from data you already have:
Left to a human, deliberately:
add_narrative(); the package never writes prose for you.needs_explanation = TRUE) and are never filled in, guessed, or auto-worded. That flag is a to-do list for a human, and it is the one thing in a Reviewer’s Guide that must not be automated.metadata, they are the declared keys, in key_seq order, reproduced as declared – including a key that is not present in the data frame. Without it they are a name-pattern guess (STUDYID, USUBJID, DOMAIN, SUBJID, PARAMCD, VISITNUM, VISIT, AVISIT, plus anything ending SEQ/ID, falling back to the first two variables), which can miss a real key and can invent one. The key_source column tells you which happened for every row – check it before the table goes into a guide.description from section_programs() is the leading comment block (#, *, or /* */) within the first 60 lines, collapsed and truncated at 300 characters. A header below a licence banner or options/library block yields NA; a stale header is reported verbatim; inputs, outputs and macros are never inspected. No R/SAS parser was written, on purpose: submission programs already carry standard headers, and a wrong parse is worse than an honest NA.label attribute and are NA when a data frame does not carry one; read.csv() output never does.render_guide() needs rmarkdown and pandoc, for both "html" and "md". rmarkdown is a Suggests, so it is not installed with the package; if it is missing you get a plain The package "rmarkdown" is required to render a guide file. before any file is written. Every other function works without it – the sections are ordinary tibbles you can render with anything.add_section() / add_narrative() – attach generated sections and hand-written prose to the guide, in order.render_guide(guide, path, format) – assemble the whole guide to HTML or Markdown via rmarkdown, using the sdrg/adrg section skeleton shipped in inst/templates.Every function returns a plain tibble or an S3 list you can inspect, subset, or hand to another tool – there is no hidden state.