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

install.packages(
  "https://clincoder.cloud/reviewerguider/reviewerguider_0.0.1.tar.gz",
  repos = NULL, type = "source"
)

Quick start

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

What it does, section by section

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

Findings input: a data contract, not a dependency

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.

Scope

Generated mechanically, from data you already have:

  • the dataset inventory – names, labels, structure, keys, row/variable counts, size;
  • the validation-findings table – findings joined to sponsor explanations, with the gaps flagged;
  • the programs-submitted list – files, sizes, dates, header descriptions;
  • the document assembly – section order, SDRG/ADRG skeleton, HTML or Markdown output.

Left to a human, deliberately:

  • every narrative section – study background, protocol summary, rationale for non-standard decisions, data-handling conventions. Use add_narrative(); the package never writes prose for you.
  • every sponsor explanation of a finding. Findings with no explanation are only flagged (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.
  • reviewing the generated content before it ships. The output is a draft assembled from your metadata, not a submission-ready document.

Limits

  • Key variables are declared or guessed. With 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.
  • Program descriptions are the header comment, not the code. The 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.
  • Labels come from the data frame’s 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.
  • No define.xml is read and no validator is run. Both are other packages’ jobs; this one consumes their output.
  • 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.

License

MIT (c) Bhanoji Duppada