--- title: "Generating reports and badges" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Generating reports and badges} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) library(reproducr) # Shared setup: a script with a mix of risk levels script <- tempfile(fileext = ".R") writeLines(c( "set.seed(237)", "x <- dplyr::filter(mtcars, cyl == 4)", "y <- dplyr::summarise(x, mean_mpg = mean(mpg), n = dplyr::n())", "z <- stats::rnorm(10)", "out <- base::sort(unique(x$gear))" ), script) report <- audit_script(script, renv = FALSE, verbose = FALSE) risks <- risk_score(report) cert_file <- tempfile() model <- lm(mpg ~ wt, data = mtcars) certify(list(coefs = coef(model)), tag = "v1", file = cert_file) drift <- check_drift(list(coefs = coef(model)), against = "v1", file = cert_file ) ``` This vignette covers Tier 3 of the `reproducr` workflow: `repro_report()` and `repro_badge()`. These functions turn audit results into documents and status indicators for external consumption. --- ## `repro_report()` — generating reports `repro_report()` combines an `audit_report`, an optional `risk_report`, and an optional `drift_report` into a single human-readable document. It has two orthogonal dimensions: - **`style`** — what the report contains (`"minimal"`, `"academic"`, `"pharma"`) - **`format`** — how it is rendered (`"text"`, `"md"`, `"html"`) ### The verdict Every report leads with a verdict, computed from the risks and drift: | Verdict | Condition | |---|---| | `REPRODUCIBLE` | No risks detected, no drifted outputs | | `CAUTION` | Medium-severity risks only, no drift | | `AT RISK` | Any high-severity risk, or any drifted output | | `UNKNOWN` | No `risks` or `drift` supplied | ```{r verdict-demo} # No risks — REPRODUCIBLE clean_script <- tempfile(fileext = ".R") writeLines("x <- 1 + 1", clean_script) clean_report <- audit_script(clean_script, renv = FALSE, verbose = FALSE) clean_risks <- risk_score(clean_report) cat(repro_report(clean_report, clean_risks, format = "text", style = "minimal")) ``` --- ## Style: `"minimal"` A compact summary covering environment metadata, verdict, and risk table. Use this for internal project documentation, PR descriptions, or quick console review. ```{r minimal-text} cat(repro_report(report, risks, format = "text", style = "minimal")) ``` The minimal style includes: - Generation timestamp and R version - File count, package count, call count - Version source (lockfile or installed library) - Overall verdict - One entry per risk (if any) - Drift summary (if drift is supplied) ```{r minimal-with-drift} cat(repro_report(report, risks, drift = drift, format = "text", style = "minimal" )) ``` --- ## Style: `"academic"` Generates a ready-to-paste methods paragraph for journal submission or a thesis. The paragraph lists every detected package with its version, states the R version and operating system, and summarises the risk findings. ```{r academic} cat(repro_report(report, risks, format = "text", style = "academic")) ``` This is intentionally a single prose paragraph so it can be pasted directly into a "Software and data" or "Computational reproducibility" subsection. If no risks were found, the paragraph says so explicitly. If risks were found, it states the count and severity so reviewers know to look at the supplementary materials. **Example output in a paper:** > All analyses were conducted in R (version 4.3.3) on Linux 6.1.0. > The following packages were used: dplyr (v1.1.4), ggplot2 (v3.5.1), > readr (v2.1.5). Reproducibility auditing (reproducr) identified no risks. > The full audit report and certification records are available in the > supplementary materials. --- ## Style: `"pharma"` A structured QC document with formal sections, a complete package inventory, a risk register, and sign-off fields for analyst and reviewer. This style is designed for pharmaceutical, biotech, and other regulated analytical workflows where the output needs to be reviewed and signed before submission. ```{r pharma-text} cat(repro_report(report, risks, drift = drift, format = "text", style = "pharma" )) ``` The pharma style includes: 1. **Header table** — document version, date, verdict 2. **Execution environment** — R version, platform, OS, locale, timezone, version source 3. **Files audited** — full paths of all scanned files 4. **Package inventory** — every detected package with version 5. **Risk register** — one entry per flagged call with severity, file, line, check method, description, and reference URL 6. **Drift assessment** — table of all certified outputs with status 7. **Sign-off** — analyst and reviewer fields --- ## Format: `"md"` and `"html"` For `"md"` and `"html"` formats, `repro_report()` writes to a file. If `output_file` is not specified, it defaults to `"reproducr_report.md"` or `"reproducr_report.html"` in the working directory. ```{r write-md, results = "hide"} md_file <- tempfile(fileext = ".md") repro_report(report, risks, format = "md", style = "minimal", output_file = md_file ) # Inspect the raw Markdown cat(readLines(md_file, warn = FALSE), sep = "\n") ``` ```{r write-html, results = "hide"} html_file <- tempfile(fileext = ".html") repro_report(report, risks, drift = drift, format = "html", style = "pharma", output_file = html_file ) # The file is self-contained — open it in a browser # browseURL(html_file) ``` The HTML output is a fully self-contained file with embedded CSS — no external dependencies, safe to email or attach to a submission. ### All nine combinations ```{r all-combinations, eval = FALSE} styles <- c("minimal", "academic", "pharma") formats <- c("text", "md", "html") for (sty in styles) { for (fmt in formats) { if (fmt == "text") { repro_report(report, risks, format = fmt, style = sty) } else { out <- tempfile(fileext = paste0(".", fmt)) repro_report(report, risks, format = fmt, style = sty, output_file = out ) message("Written: ", out) } } } ``` --- ## `repro_badge()` — status badges `repro_badge()` generates a [shields.io](https://shields.io) badge reflecting the current reproducibility status of the project. The badge is designed to sit in a README and update automatically via CI. ### Badge colours | Badge | Condition | |---|---| | ![reproducible](https://img.shields.io/badge/reproducibility-reproducible-brightgreen) | No risks, no drift | | ![caution](https://img.shields.io/badge/reproducibility-caution-yellow) | Medium risks only | | ![at risk](https://img.shields.io/badge/reproducibility-at%20risk-red) | High risks or drift | | ![unknown](https://img.shields.io/badge/reproducibility-unknown-lightgrey) | No risk info supplied | ```{r badge-colours} # Reproducible — clean script, no risks clean_badge <- repro_badge(clean_report, clean_risks, output = "markdown") cat(clean_badge, "\n") # Unknown — no risks supplied unknown_badge <- repro_badge(report, output = "markdown") cat(unknown_badge, "\n") # With risks — colour depends on highest severity risk_badge <- repro_badge(report, risks, output = "markdown") cat(risk_badge, "\n") ``` ### Inserting into `README.md` ```{r badge-readme} readme <- tempfile(fileext = ".md") writeLines(c( "# myanalysis", "", "Analysis of the relationship between engine size and fuel efficiency.", "", "## Installation" ), readme) # Insert badge at the top repro_badge(report, risks, output = "README", readme_path = readme) # See the result cat(readLines(readme, warn = FALSE), sep = "\n") ``` The badge is wrapped in HTML comment markers: ```html ![reproducibility](...) ``` This makes subsequent calls idempotent — running `repro_badge()` again replaces the existing badge rather than inserting a second one. It is safe to call on every CI push. ### Removing a badge To remove the badge from a README, delete the line containing ``. The comment markers make it easy to find with any text editor or `grep`. --- ## Full CI pipeline The recommended CI pattern updates the badge automatically on every push: ```yaml name: Reproducibility audit on: push: branches: [main, master] schedule: - cron: '0 6 * * 1' # Weekly on Monday at 06:00 UTC jobs: audit: runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v4 with: token: ${{ secrets.GITHUB_TOKEN }} - uses: r-lib/actions/setup-r@v2 with: r-version: 'release' use-public-rspm: true - name: Install reproducr run: Rscript -e "install.packages('.', repos = NULL, type = 'source')" - name: Run reproducibility audit run: | Rscript -e " library(reproducr) report <- audit_script('vignettes/', renv = FALSE, verbose = FALSE) risks <- risk_score(report) repro_badge(report, risks, output = 'README') n_high <- sum(risks\$risk == 'high', na.rm = TRUE) if (n_high > 0) { message(n_high, ' high-severity reproducibility risk(s) detected.') } " - name: Commit updated badge if: github.event_name == 'push' run: | git config user.name 'github-actions[bot]' git config user.email '41898282+github-actions[bot]@users.noreply.github.com' git add README.md git diff --staged --quiet || \ git commit -m 'chore: update reproducibility badge [skip ci]' git push ``` The workflow template is also available as a file inside the package: ```{r template-path, eval = FALSE} system.file("templates", "github_actions_audit.yml", package = "reproducr") ``` ```{r cleanup, include = FALSE} unlink(c(script, clean_script, md_file, html_file, readme)) unlink(paste0(cert_file, ".rds")) ```