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}