diff --git a/codecov.yml b/.github/codecov.yml
similarity index 100%
rename from codecov.yml
rename to .github/codecov.yml
diff --git a/DESCRIPTION b/DESCRIPTION
index 89d243a..f15d3dc 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -39,15 +39,17 @@ Imports:
tidyselect,
plotly,
scico,
- magick (>= 2.9.0),
- tinytex (>= 0.58)
+ rmarkdown
Suggests:
covr,
testthat (>= 3.0.0),
withr,
+ spelling,
knitr,
- rmarkdown,
- spelling
+ magick (>= 2.9.0),
+ tinytex (>= 0.58),
+ gridExtra,
+ grid
Config/testthat/edition: 3
URL: https://cabajr.github.io/ClockCyteR.spatial/
Language: en-US
diff --git a/R/modules_foo.R b/R/modules_foo.R
index 4fe86ec..1b480b0 100644
--- a/R/modules_foo.R
+++ b/R/modules_foo.R
@@ -1121,11 +1121,16 @@ validate_params <- function(p) {
#' generate_reports
#'
-#' Renders a per-file HTML or PDF report for each file in the project,
-#' collecting all plot types across intervals and channels.
+#' Renders a per-file report for each file in the project, collecting all plot
+#' types across intervals and channels. Output is HTML by default (no system
+#' dependencies); set \code{pdf = TRUE} for PDF output (requires additional
+#' system dependencies — see Details).
#'
#' @param params A named list of analysis parameters as produced by \code{make_params()}.
#' @param file_rows A tibble of file metadata as produced by \code{index_files()}.
+#' @param pdf Logical; if \code{TRUE} render PDF reports instead of HTML.
+#' Requires \pkg{magick}, \pkg{tinytex}, and a TinyTeX installation
+#' (\code{tinytex::install_tinytex()}). Defaults to \code{FALSE}.
#'
#' @return Called for its side effects (report files written to disk). Returns
#' \code{NULL} invisibly.
@@ -1134,80 +1139,96 @@ validate_params <- function(p) {
#' @examples
#' \dontrun{
#' generate_reports(params, file_rows)
+#' generate_reports(params, file_rows, pdf = TRUE)
#' }
-generate_reports <- function(params, file_rows) {
- intervals <- names(params$time$intervals)
- channels <- names(params$channels)
- plot_types <- c(params$plotting$plot_types, params$plotting$network_plot_types)
- base_dir <- params$paths$base_dir
+generate_reports <- function(params, file_rows, pdf = FALSE) {
+ intervals <- names(params$time$intervals)
+ channels <- names(params$channels)
+ plot_types <- c(params$plotting$plot_types, params$plotting$network_plot_types)
+ base_dir <- params$paths$base_dir
reports_dir <- file.path(base_dir, "reports")
if (!dir.exists(reports_dir)) dir.create(reports_dir)
n_files <- length(file_rows$file_id)
- pb_id <- cli::cli_progress_bar("Generating reports", total = n_files)
+ pb_id <- cli::cli_progress_bar("Generating reports", total = n_files)
- purrr::walk(file_rows$file_id, function(fid){
+ purrr::walk(file_rows$file_id, function(fid) {
generate_file_report(
- file_id =fid,
- params = params,
- base_dir = base_dir,
- intervals = intervals,
- channels = channels,
- plot_types = plot_types
- )
+ file_id = fid,
+ params = params,
+ base_dir = base_dir,
+ intervals = intervals,
+ channels = channels,
+ plot_types = plot_types,
+ pdf = pdf
+ )
cli::cli_progress_update(id = pb_id)
- })
+ })
+
cli::cli_progress_done(id = pb_id)
message("Reports generated.")
}
#' generate_plot_type_reports
#'
-#' Renders one PDF report per plot type, collating the corresponding plot
-#' across all files, intervals, and channels using an Rmd template.
+#' Renders one report per plot type, collating the corresponding plot across
+#' all files, intervals, and channels using an Rmd template. Output is HTML
+#' by default; set \code{pdf = TRUE} for PDF output (requires additional
+#' system dependencies — see Details).
#'
#' @param params A named list of analysis parameters as produced by \code{make_params()}.
#' @param file_rows A tibble of file metadata as produced by \code{index_files()}.
#' @param storage_fold Character string naming the subfolder within each
#' file's results directory where plots are stored. Defaults to
#' \code{"plots"}.
-#' @param plot_report_template Character string giving the filename of the Rmd
-#' template to use. Defaults to \code{"plot_type_report_template.Rmd"}.
#' @param plot_report_dir Character string giving the output directory for
#' rendered reports. Defaults to \code{file.path(params$paths$base_dir,
#' "reports", "plot_reports")}.
+#' @param pdf Logical; if \code{TRUE} render PDF reports instead of HTML.
+#' Requires \pkg{magick}, \pkg{tinytex}, and a TinyTeX installation
+#' (\code{tinytex::install_tinytex()}). Defaults to \code{FALSE}.
#'
-#' @return Called for its side effects (PDF reports written to
+#' @return Called for its side effects (reports written to
#' \code{plot_report_dir}). Returns \code{NULL} invisibly.
#' @export
#'
#' @examples
#' \dontrun{
#' generate_plot_type_reports(params, file_rows)
+#' generate_plot_type_reports(params, file_rows, pdf = TRUE)
#' }
generate_plot_type_reports <- function(
params,
file_rows,
- storage_fold = "plots",
- plot_report_template = "plot_type_report_template.Rmd",
- plot_report_dir = file.path(params$paths$base_dir, "reports", "plot_reports")
+ storage_fold = "plots",
+ plot_report_dir = file.path(params$paths$base_dir, "reports", "plot_reports"),
+ pdf = FALSE
) {
- intervals <- names(params$time$intervals)
- channels <- names(params$channels)
- plot_types <- c(params$plotting$plot_types, params$plotting$network_plot_types)
- base_dir <- params$paths$base_dir
+ if (pdf) {
+ check_pdf_deps()
+ plot_report_template <- "plot_type_report_template_pdf.Rmd"
+ output_ext <- ".pdf"
+ } else {
+ plot_report_template <- "plot_type_report_template.Rmd"
+ output_ext <- ".html"
+ }
+
+ intervals <- names(params$time$intervals)
+ channels <- names(params$channels)
+ plot_types <- c(params$plotting$plot_types, params$plotting$network_plot_types)
+ base_dir <- params$paths$base_dir
if (!dir.exists(plot_report_dir)) dir.create(plot_report_dir, recursive = TRUE)
n_plot_types <- length(plot_types)
- pb_id <- cli::cli_progress_bar("Generating plot-type reports", total = n_plot_types)
+ pb_id <- cli::cli_progress_bar("Generating plot-type reports", total = n_plot_types)
purrr::walk(plot_types, function(pt) {
rmarkdown::render(
- input = system.file("rmd", plot_report_template, package = "ClockCyteR.spatial"),
- output_file = file.path(plot_report_dir, paste0("plot_report_", pt, ".pdf")),
- params = list(
+ input = system.file("rmd", plot_report_template, package = "ClockCyteR.spatial"),
+ output_file = file.path(plot_report_dir, paste0("plot_report_", pt, output_ext)),
+ params = list(
channel_params = params$channels,
file_rows = file_rows,
base_dir = base_dir,
diff --git a/R/utils_foo.R b/R/utils_foo.R
index 66df908..cf31f16 100644
--- a/R/utils_foo.R
+++ b/R/utils_foo.R
@@ -248,6 +248,34 @@ extract_vars <- function(params, file_rows, vars, network = FALSE, individual =
}
+check_pdf_deps <- function() {
+ missing_pkgs <- character(0)
+ if (!requireNamespace("magick", quietly = TRUE)) missing_pkgs <- c(missing_pkgs, "magick")
+ if (!requireNamespace("tinytex", quietly = TRUE)) missing_pkgs <- c(missing_pkgs, "tinytex")
+ if (!requireNamespace("gridExtra", quietly = TRUE)) missing_pkgs <- c(missing_pkgs, "gridExtra")
+
+ if (length(missing_pkgs) > 0) {
+ stop(
+ "PDF report generation requires additional packages: ",
+ paste(missing_pkgs, collapse = ", "), ".\n",
+ "Install them with: install.packages(c(",
+ paste0('"', missing_pkgs, '"', collapse = ", "), "))",
+ call. = FALSE
+ )
+ }
+
+ if (!tinytex::is_tinytex()) {
+ stop(
+ "TinyTeX (LaTeX) is not installed. PDF generation requires it.\n",
+ "Install it with: tinytex::install_tinytex()\n",
+ "Or use the default HTML output by leaving pdf = FALSE.",
+ call. = FALSE
+ )
+ }
+
+ invisible(TRUE)
+}
+
#' Render an Rmd report for a single file
#'
#' @param file_id Character; file identifier.
@@ -259,12 +287,13 @@ extract_vars <- function(params, file_rows, vars, network = FALSE, individual =
#' report.
#' @param storage_fold Character; subdirectory containing the plots. Defaults
#' to \code{"plots"}.
-#' @param report_template Character; filename of the Rmd template. Defaults
-#' to \code{"report_template.Rmd"}.
#' @param output_dir Character; directory where the rendered report is saved.
#' Defaults to a \code{reports} subdirectory inside \code{base_dir}.
+#' @param pdf Logical; if \code{TRUE} render a PDF instead of HTML. Requires
+#' \pkg{magick}, \pkg{tinytex}, and a TinyTeX installation. Defaults to
+#' \code{FALSE}.
#'
-#' @return Called for its side effect of rendering an HTML report; returns
+#' @return Called for its side effect of rendering a report; returns
#' the output file path invisibly.
#' @keywords internal
generate_file_report <- function(
@@ -275,9 +304,17 @@ generate_file_report <- function(
channels,
plot_types,
storage_fold = "plots",
- report_template = "report_template.Rmd",
- output_dir = file.path(base_dir, "reports")
+ output_dir = file.path(base_dir, "reports"),
+ pdf = FALSE
) {
+ if (pdf) {
+ check_pdf_deps()
+ report_template <- "report_template_pdf.Rmd"
+ output_ext <- ".pdf"
+ } else {
+ report_template <- "report_template.Rmd"
+ output_ext <- ".html"
+ }
# Build period summary path
period_summary_path <- file.path(base_dir, paste0(file_id, "_results"),
"rds",
@@ -307,7 +344,7 @@ generate_file_report <- function(
# Render the report
rmarkdown::render(
input = system.file("rmd", report_template, package = "ClockCyteR.spatial"),
- output_file = file.path(output_dir, paste0("report_", file_id, ".pdf")),
+ output_file = file.path(output_dir, paste0("report_", file_id, output_ext)),
params = list(
channel_params = params$channels,
file_id = file_id,
diff --git a/inst/rmd/plot_type_report_template.Rmd b/inst/rmd/plot_type_report_template.Rmd
index cd44f46..f375615 100644
--- a/inst/rmd/plot_type_report_template.Rmd
+++ b/inst/rmd/plot_type_report_template.Rmd
@@ -1,6 +1,10 @@
---
title: "Plot Type Report: `r params$plot_type`"
-output: pdf_document
+output:
+ html_document:
+ self_contained: true
+ toc: true
+ toc_float: true
params:
channel_params: NULL
file_rows: NULL
@@ -11,33 +15,16 @@ params:
save_format: NULL
---
-
```{r setup, include=FALSE}
library(knitr)
-library(magick)
-library(dplyr)
-library(gridExtra)
-library(grid)
-
-knitr::opts_chunk$set(
- echo = FALSE,
- warning = FALSE,
- message = FALSE
-)
-
+knitr::opts_chunk$set(echo = FALSE, warning = FALSE, message = FALSE)
```
----
-
-
-##
-## Plots by Type, Interval and Channel
-
-```{r params$plot_type, results='asis', fig.width=8, fig.height=8}
-
-plot_type <- params$plot_type
-plot_name <- plot_type_to_name(plot_type)
+## Plots by Channel and Interval
+```{r plots, results='asis'}
+plot_type <- params$plot_type
+plot_name <- plot_type_to_name(plot_type)
base_dir <- params$base_dir
intervals <- params$intervals
channels <- params$channels
@@ -45,107 +32,45 @@ file_rows <- params$file_rows
channel_params <- params$channel_params
save_format <- params$save_format
-# Enabled channels only
-enabled_channels <- names(Filter(function(ch) ch$enabled, channel_params))
-channels <- intersect(channels, enabled_channels)
-
-# Check if multiple intervals
+enabled_channels <- names(Filter(function(ch) ch$enabled, channel_params))
+channels <- intersect(channels, enabled_channels)
multiple_intervals <- length(intervals) > 1
-# Loop: channel -> interval -> files
for (channel in channels) {
- cat("\n\n# Channel: ", channel, ": ", params$channel_params[[channel]]$label, "\n\n", sep = "")
-
+ cat("\n\n# Channel:", channel, "—", channel_params[[channel]]$label, "\n\n")
+
for (interval in intervals) {
- # Only show interval header if multiple intervals
if (multiple_intervals) {
- cat("\n\n## Interval: ", interval, "\n\n", sep = "")
+ cat("\n\n## Interval:", interval, "\n\n")
}
-
- # Collect all grobs for this channel and interval across all files
- grobs <- list()
- grob_labels <- character(0)
-
+
+ found_any <- FALSE
+
for (fid in file_rows$file_id) {
- # Conditional path construction based on number of intervals
if (multiple_intervals) {
- ch_id_folder <- file.path(
- base_dir,
- paste0(fid, "_results"),
- "plots",
- interval,
- channel
- )
+ ch_id_folder <- file.path(base_dir, paste0(fid, "_results"),
+ "plots", interval, channel)
} else {
- ch_id_folder <- file.path(
- base_dir,
- paste0(fid, "_results"),
- "plots",
- channel
- )
+ ch_id_folder <- file.path(base_dir, paste0(fid, "_results"),
+ "plots", channel)
}
-
- plot_path <- build_plot_path(
- file_id = fid,
- ch_id = channel,
- ch_id_folder = ch_id_folder,
- plot_name = plot_name,
- save_format = save_format
- )
-
+
+ plot_path <- build_plot_path(file_id = fid,
+ ch_id = channel,
+ ch_id_folder = ch_id_folder,
+ plot_name = plot_name,
+ save_format = save_format)
+
if (file.exists(plot_path)) {
- img <- magick::image_read_svg(plot_path)
- png_path <- tempfile(fileext = ".png")
- magick::image_write(img, path = png_path, format = "png", density = 300)
-
- grobs[[length(grobs) + 1]] <- grid::rasterGrob(magick::image_read(png_path))
- grob_labels <- c(grob_labels, paste0("File: ", fid))
- }
- } # file
-
- # Arrange plots for this interval in 3x4 grids (12 per page)
- n_plots <- length(grobs)
- if (n_plots > 0) {
- for (i in seq(1, n_plots, by = 12)) {
- grid_grobs <- grobs[i:min(i + 11, n_plots)]
- grid_labels <- grob_labels[i:min(i + 11, n_plots)]
-
- # Conditional title based on number of intervals
- if (multiple_intervals) {
- grid_title <- paste0(
- "Plot type: ", plot_type,
- " | Channel: ", channel,
- " | Interval: ", interval,
- "\n\nShowing plots ", i, "-", min(i + 11, n_plots),
- " of ", n_plots
- )
- } else {
- grid_title <- paste0(
- "Plot type: ", plot_type,
- " | Channel: ", channel,
- "\n\nShowing plots ", i, "-", min(i + 11, n_plots),
- " of ", n_plots
- )
- }
-
- gridExtra::grid.arrange(
- grobs = grid_grobs,
- ncol = 3,
- nrow = 4,
- top = grid_title
- )
- cat("\\newpage\n")
- }
- } else {
- if (multiple_intervals) {
- cat("*No plots found for this interval.*\n\n")
- } else {
- cat("*No plots found.*\n\n")
+ found_any <- TRUE
+ cat(paste0("\n\n### File: ", fid, "\n\n"))
+ svg_content <- paste(readLines(plot_path, warn = FALSE), collapse = "\n")
+ cat(svg_content, "\n\n")
}
}
- } # interval
-} # channel
+ if (!found_any) cat("*No plots found.*\n\n")
+ cat("
\n")
+ }
+}
```
-
-
diff --git a/inst/rmd/plot_type_report_template_pdf.Rmd b/inst/rmd/plot_type_report_template_pdf.Rmd
new file mode 100644
index 0000000..4cbbd9f
--- /dev/null
+++ b/inst/rmd/plot_type_report_template_pdf.Rmd
@@ -0,0 +1,101 @@
+---
+title: "Plot Type Report: `r params$plot_type`"
+output: pdf_document
+params:
+ channel_params: NULL
+ file_rows: NULL
+ base_dir: NULL
+ intervals: NULL
+ channels: NULL
+ plot_type: NULL
+ save_format: NULL
+---
+
+```{r setup, include=FALSE}
+library(knitr)
+library(magick)
+library(dplyr)
+library(gridExtra)
+library(grid)
+knitr::opts_chunk$set(echo = FALSE, warning = FALSE, message = FALSE)
+```
+
+## Plots by Channel and Interval
+
+```{r plots, results='asis', fig.width=8, fig.height=8}
+plot_type <- params$plot_type
+plot_name <- plot_type_to_name(plot_type)
+base_dir <- params$base_dir
+intervals <- params$intervals
+channels <- params$channels
+file_rows <- params$file_rows
+channel_params <- params$channel_params
+save_format <- params$save_format
+
+enabled_channels <- names(Filter(function(ch) ch$enabled, channel_params))
+channels <- intersect(channels, enabled_channels)
+multiple_intervals <- length(intervals) > 1
+
+for (channel in channels) {
+ cat("\n\n# Channel:", channel, ":", channel_params[[channel]]$label, "\n\n")
+
+ for (interval in intervals) {
+ if (multiple_intervals) {
+ cat("\n\n## Interval:", interval, "\n\n")
+ }
+
+ grobs <- list()
+ grob_labels <- character(0)
+
+ for (fid in file_rows$file_id) {
+ if (multiple_intervals) {
+ ch_id_folder <- file.path(base_dir, paste0(fid, "_results"),
+ "plots", interval, channel)
+ } else {
+ ch_id_folder <- file.path(base_dir, paste0(fid, "_results"),
+ "plots", channel)
+ }
+
+ plot_path <- build_plot_path(file_id = fid,
+ ch_id = channel,
+ ch_id_folder = ch_id_folder,
+ plot_name = plot_name,
+ save_format = save_format)
+
+ if (file.exists(plot_path)) {
+ img <- magick::image_read_svg(plot_path)
+ png_path <- tempfile(fileext = ".png")
+ magick::image_write(img, path = png_path, format = "png", density = 300)
+ grobs[[length(grobs) + 1]] <- grid::rasterGrob(magick::image_read(png_path))
+ grob_labels <- c(grob_labels, paste0("File: ", fid))
+ }
+ }
+
+ n_plots <- length(grobs)
+ if (n_plots > 0) {
+ for (i in seq(1, n_plots, by = 12)) {
+ grid_grobs <- grobs[i:min(i + 11, n_plots)]
+ grid_labels <- grob_labels[i:min(i + 11, n_plots)]
+
+ if (multiple_intervals) {
+ grid_title <- paste0("Plot type: ", plot_type,
+ " | Channel: ", channel,
+ " | Interval: ", interval,
+ "\n\nShowing plots ", i, "-", min(i + 11, n_plots),
+ " of ", n_plots)
+ } else {
+ grid_title <- paste0("Plot type: ", plot_type,
+ " | Channel: ", channel,
+ "\n\nShowing plots ", i, "-", min(i + 11, n_plots),
+ " of ", n_plots)
+ }
+
+ gridExtra::grid.arrange(grobs = grid_grobs, ncol = 3, nrow = 4, top = grid_title)
+ cat("\\newpage\n")
+ }
+ } else {
+ cat("*No plots found.*\n\n")
+ }
+ }
+}
+```
diff --git a/inst/rmd/report_template.Rmd b/inst/rmd/report_template.Rmd
index eaa630d..b7dd664 100644
--- a/inst/rmd/report_template.Rmd
+++ b/inst/rmd/report_template.Rmd
@@ -1,6 +1,10 @@
---
title: "Analysis Report for `r params$file_id`"
-output: pdf_document
+output:
+ html_document:
+ self_contained: true
+ toc: true
+ toc_float: true
params:
channel_params: NULL
file_id: NULL
@@ -14,118 +18,67 @@ params:
save_format: NULL
---
-
```{r setup, include=FALSE}
library(knitr)
-library(magick)
library(dplyr)
-library(gridExtra)
-library(grid)
knitr::opts_chunk$set(echo = FALSE, warning = FALSE, message = FALSE)
-
```
----
-
-### **B. Period Summary Table (visible output):**
-
-
-## Period Table Summary
+## Period Summary
```{r period-summary}
period_summary <- read.csv(params$period_summary_path)
-knitr::kable(t(period_summary)[-1,], caption = "Period Table Summary")
-
+knitr::kable(t(period_summary)[-1, ], caption = "Period Table Summary")
```
----
-### **C. Plots by Interval and Channel (visible output):**
+---
## Plots by Interval and Channel
-```{r plots-by-interval, results='asis', fig.width=8, fig.height=8}
-# Check if multiple intervals
+```{r plots-by-interval, results='asis'}
multiple_intervals <- length(params$intervals) > 1
for (interval in params$intervals) {
- # Only show interval header if multiple intervals
if (multiple_intervals) {
- cat("\n\n## Interval: ", interval, "\n\n")
+ cat("\n\n## Interval:", interval, "\n\n")
}
-
+
for (channel in params$channels) {
- if(!params$channel_params[[channel]]$enabled) next
- cat("### Channel: ", channel,": ", params$channel_params[[channel]]$label, "\n\n")
-
+ if (!params$channel_params[[channel]]$enabled) next
+ cat("### Channel:", channel, "—", params$channel_params[[channel]]$label, "\n\n")
+
plot_types <- names(params$plot_paths[[interval]][[channel]])
- grobs <- list()
- grob_labels <- c()
-
+ found_any <- FALSE
+
for (plot_type in plot_types) {
plot_name <- plot_type_to_name(plot_type)
-
- # Conditional path construction based on number of intervals
+
if (multiple_intervals) {
- ch_id_folder <- file.path(params$base_dir,
- paste0(params$file_id, "_results"),
- "plots",
- interval,
- channel)
+ ch_id_folder <- file.path(params$base_dir,
+ paste0(params$file_id, "_results"),
+ "plots", interval, channel)
} else {
- ch_id_folder <- file.path(params$base_dir,
- paste0(params$file_id, "_results"),
- "plots",
- channel)
+ ch_id_folder <- file.path(params$base_dir,
+ paste0(params$file_id, "_results"),
+ "plots", channel)
}
-
- plot_path <- build_plot_path(file_id = params$file_id,
- ch_id = channel,
+
+ plot_path <- build_plot_path(file_id = params$file_id,
+ ch_id = channel,
ch_id_folder = ch_id_folder,
- plot_name = plot_name,
- save_format = params$save_format)
-
+ plot_name = plot_name,
+ save_format = params$save_format)
+
if (file.exists(plot_path)) {
- img <- magick::image_read_svg(plot_path)
- png_path <- tempfile(fileext = ".png")
- magick::image_write(img, path = png_path, format = "png", density = 300)
- grobs[[length(grobs) + 1]] <- grid::rasterGrob(magick::image_read(png_path))
- grob_labels <- c(grob_labels, plot_type)
- }
- }
-
- # Arrange in grids of 4
- n_plots <- length(grobs)
- if (n_plots > 0) {
- for (i in seq(1, n_plots, by = 4)) {
- grid_grobs <- grobs[i:min(i+3, n_plots)]
- grid_labels <- grob_labels[i:min(i+3, n_plots)]
-
- # Conditional title
- if (multiple_intervals) {
- grid_title <- paste0("Interval: ", interval, " | Plots: ",
- paste(grid_labels, collapse = ", "), "\n")
- } else {
- grid_title <- paste0("Plots: ", paste(grid_labels, collapse = ", "), "\n")
- }
-
- gridExtra::grid.arrange(grobs = grid_grobs,
- ncol = 2, nrow = 2,
- top = grid_title)
- cat("\\newpage\n")
+ found_any <- TRUE
+ cat(paste0("\n\n#### ", plot_type, "\n\n"))
+ svg_content <- paste(readLines(plot_path, warn = FALSE), collapse = "\n")
+ cat(svg_content, "\n\n")
}
- } else {
- cat("*No plots found for this channel.*\n\n")
}
- }
-
- # Add page break between intervals if multiple
- if (multiple_intervals) {
- cat("\\newpage\n")
+
+ if (!found_any) cat("*No plots found for this channel.*\n\n")
+ cat("
\n")
}
}
-
```
-
-
----
-
diff --git a/inst/rmd/report_template_pdf.Rmd b/inst/rmd/report_template_pdf.Rmd
new file mode 100644
index 0000000..787d124
--- /dev/null
+++ b/inst/rmd/report_template_pdf.Rmd
@@ -0,0 +1,104 @@
+---
+title: "Analysis Report for `r params$file_id`"
+output: pdf_document
+params:
+ channel_params: NULL
+ file_id: NULL
+ period_summary_path: NULL
+ plot_paths: NULL
+ intervals: NULL
+ channels: NULL
+ file_rows: NULL
+ base_dir: NULL
+ plot_types: NULL
+ save_format: NULL
+---
+
+```{r setup, include=FALSE}
+library(knitr)
+library(magick)
+library(dplyr)
+library(gridExtra)
+library(grid)
+knitr::opts_chunk$set(echo = FALSE, warning = FALSE, message = FALSE)
+```
+
+## Period Summary
+
+```{r period-summary}
+period_summary <- read.csv(params$period_summary_path)
+knitr::kable(t(period_summary)[-1, ], caption = "Period Table Summary")
+```
+
+---
+
+## Plots by Interval and Channel
+
+```{r plots-by-interval, results='asis', fig.width=8, fig.height=8}
+multiple_intervals <- length(params$intervals) > 1
+
+for (interval in params$intervals) {
+ if (multiple_intervals) {
+ cat("\n\n## Interval:", interval, "\n\n")
+ }
+
+ for (channel in params$channels) {
+ if (!params$channel_params[[channel]]$enabled) next
+ cat("### Channel:", channel, ":", params$channel_params[[channel]]$label, "\n\n")
+
+ plot_types <- names(params$plot_paths[[interval]][[channel]])
+ grobs <- list()
+ grob_labels <- character(0)
+
+ for (plot_type in plot_types) {
+ plot_name <- plot_type_to_name(plot_type)
+
+ if (multiple_intervals) {
+ ch_id_folder <- file.path(params$base_dir,
+ paste0(params$file_id, "_results"),
+ "plots", interval, channel)
+ } else {
+ ch_id_folder <- file.path(params$base_dir,
+ paste0(params$file_id, "_results"),
+ "plots", channel)
+ }
+
+ plot_path <- build_plot_path(file_id = params$file_id,
+ ch_id = channel,
+ ch_id_folder = ch_id_folder,
+ plot_name = plot_name,
+ save_format = params$save_format)
+
+ if (file.exists(plot_path)) {
+ img <- magick::image_read_svg(plot_path)
+ png_path <- tempfile(fileext = ".png")
+ magick::image_write(img, path = png_path, format = "png", density = 300)
+ grobs[[length(grobs) + 1]] <- grid::rasterGrob(magick::image_read(png_path))
+ grob_labels <- c(grob_labels, plot_type)
+ }
+ }
+
+ n_plots <- length(grobs)
+ if (n_plots > 0) {
+ for (i in seq(1, n_plots, by = 4)) {
+ grid_grobs <- grobs[i:min(i + 3, n_plots)]
+ grid_labels <- grob_labels[i:min(i + 3, n_plots)]
+
+ if (multiple_intervals) {
+ grid_title <- paste0("Interval: ", interval, " | Plots: ",
+ paste(grid_labels, collapse = ", "), "\n")
+ } else {
+ grid_title <- paste0("Plots: ", paste(grid_labels, collapse = ", "), "\n")
+ }
+
+ gridExtra::grid.arrange(grobs = grid_grobs, ncol = 2, nrow = 2, top = grid_title)
+ cat("\\newpage\n")
+ }
+ } else {
+ cat("*No plots found for this channel.*\n\n")
+ }
+ }
+
+ if (multiple_intervals) cat("\\newpage\n")
+}
+```
diff --git a/man/generate_file_report.Rd b/man/generate_file_report.Rd
index d55147a..a6346cb 100644
--- a/man/generate_file_report.Rd
+++ b/man/generate_file_report.Rd
@@ -12,8 +12,8 @@ generate_file_report(
channels,
plot_types,
storage_fold = "plots",
- report_template = "report_template.Rmd",
- output_dir = file.path(base_dir, "reports")
+ output_dir = file.path(base_dir, "reports"),
+ pdf = FALSE
)
}
\arguments{
@@ -33,14 +33,15 @@ report.}
\item{storage_fold}{Character; subdirectory containing the plots. Defaults
to \code{"plots"}.}
-\item{report_template}{Character; filename of the Rmd template. Defaults
-to \code{"report_template.Rmd"}.}
-
\item{output_dir}{Character; directory where the rendered report is saved.
Defaults to a \code{reports} subdirectory inside \code{base_dir}.}
+
+\item{pdf}{Logical; if \code{TRUE} render a PDF instead of HTML. Requires
+\pkg{magick}, \pkg{tinytex}, and a TinyTeX installation. Defaults to
+\code{FALSE}.}
}
\value{
-Called for its side effect of rendering an HTML report; returns
+Called for its side effect of rendering a report; returns
the output file path invisibly.
}
\description{
diff --git a/man/generate_plot_type_reports.Rd b/man/generate_plot_type_reports.Rd
index b5b4e23..9d1bd0e 100644
--- a/man/generate_plot_type_reports.Rd
+++ b/man/generate_plot_type_reports.Rd
@@ -8,8 +8,8 @@ generate_plot_type_reports(
params,
file_rows,
storage_fold = "plots",
- plot_report_template = "plot_type_report_template.Rmd",
- plot_report_dir = file.path(params$paths$base_dir, "reports", "plot_reports")
+ plot_report_dir = file.path(params$paths$base_dir, "reports", "plot_reports"),
+ pdf = FALSE
)
}
\arguments{
@@ -21,23 +21,27 @@ generate_plot_type_reports(
file's results directory where plots are stored. Defaults to
\code{"plots"}.}
-\item{plot_report_template}{Character string giving the filename of the Rmd
-template to use. Defaults to \code{"plot_type_report_template.Rmd"}.}
-
\item{plot_report_dir}{Character string giving the output directory for
rendered reports. Defaults to \code{file.path(params$paths$base_dir,
"reports", "plot_reports")}.}
+
+\item{pdf}{Logical; if \code{TRUE} render PDF reports instead of HTML.
+Requires \pkg{magick}, \pkg{tinytex}, and a TinyTeX installation
+(\code{tinytex::install_tinytex()}). Defaults to \code{FALSE}.}
}
\value{
-Called for its side effects (PDF reports written to
+Called for its side effects (reports written to
\code{plot_report_dir}). Returns \code{NULL} invisibly.
}
\description{
-Renders one PDF report per plot type, collating the corresponding plot
-across all files, intervals, and channels using an Rmd template.
+Renders one report per plot type, collating the corresponding plot across
+all files, intervals, and channels using an Rmd template. Output is HTML
+by default; set \code{pdf = TRUE} for PDF output (requires additional
+system dependencies).
}
\examples{
\dontrun{
generate_plot_type_reports(params, file_rows)
+generate_plot_type_reports(params, file_rows, pdf = TRUE)
}
}
diff --git a/man/generate_reports.Rd b/man/generate_reports.Rd
index 7ff0817..394b5c0 100644
--- a/man/generate_reports.Rd
+++ b/man/generate_reports.Rd
@@ -4,23 +4,30 @@
\alias{generate_reports}
\title{generate_reports}
\usage{
-generate_reports(params, file_rows)
+generate_reports(params, file_rows, pdf = FALSE)
}
\arguments{
\item{params}{A named list of analysis parameters as produced by \code{make_params()}.}
\item{file_rows}{A tibble of file metadata as produced by \code{index_files()}.}
+
+\item{pdf}{Logical; if \code{TRUE} render PDF reports instead of HTML.
+Requires \pkg{magick}, \pkg{tinytex}, and a TinyTeX installation
+(\code{tinytex::install_tinytex()}). Defaults to \code{FALSE}.}
}
\value{
Called for its side effects (report files written to disk). Returns
\code{NULL} invisibly.
}
\description{
-Renders a per-file HTML or PDF report for each file in the project,
-collecting all plot types across intervals and channels.
+Renders a per-file report for each file in the project, collecting all
+plot types across intervals and channels. Output is HTML by default;
+set \code{pdf = TRUE} for PDF output (requires additional system
+dependencies).
}
\examples{
\dontrun{
generate_reports(params, file_rows)
+generate_reports(params, file_rows, pdf = TRUE)
}
}
diff --git a/man/pull_plots_old.Rd b/man/pull_plots_old.Rd
deleted file mode 100644
index 36756e1..0000000
--- a/man/pull_plots_old.Rd
+++ /dev/null
@@ -1,55 +0,0 @@
-% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/utils_foo.R
-\name{pull_plots_old}
-\alias{pull_plots_old}
-\title{Copy plots into a subdirectory (deprecated, use \code{pull_plots()})}
-\usage{
-pull_plots_old(
- base.dir,
- plot.name,
- dir.name,
- file.names,
- mainfold = FALSE,
- second_fold = "",
- channel,
- storage_fold = "plots",
- short = FALSE,
- shortname = "",
- ...
-)
-}
-\arguments{
-\item{base.dir}{Character; base directory of the project.}
-
-\item{plot.name}{Character; plot filename stem.}
-
-\item{dir.name}{Character; name of the destination subdirectory.}
-
-\item{file.names}{Character vector of file identifiers.}
-
-\item{mainfold}{Logical; if \code{TRUE}, looks for plots directly inside
-each file's results folder. Defaults to \code{FALSE}.}
-
-\item{second_fold}{Character; optional intermediate subdirectory path.
-Defaults to \code{""}.}
-
-\item{channel}{Character; channel identifier used in plot filenames.}
-
-\item{storage_fold}{Character; top-level output folder. Defaults to
-\code{"plots"}.}
-
-\item{short}{Logical; if \code{TRUE}, uses \code{shortname} as the
-destination filename. Defaults to \code{FALSE}.}
-
-\item{shortname}{Character; filename used when \code{short = TRUE}.
-Defaults to \code{""}.}
-
-\item{...}{Currently unused.}
-}
-\value{
-Character vector of destination file paths (invisibly).
-}
-\description{
-Copy plots into a subdirectory (deprecated, use \code{pull_plots()})
-}
-\keyword{internal}