From 6a30a561deb5008d2f941518103c85088a3aade0 Mon Sep 17 00:00:00 2001 From: kartikeya kirar Date: Wed, 4 Oct 2023 20:17:00 +0530 Subject: [PATCH] 169 wrapping up the long code in the Report (#218) this Fixes https://github.com/insightsengineering/teal.reporter/issues/169 I've implemented a solution that relies on the formatR package. I've ensured that this solution checks for the package in the namespace; if it's not accessible, the user will be notified. To maintain flexibility, I've included the formatR package in the suggested dependencies, as there are no strict dependencies on it. In response to the feedback provided by @m7pr in [this comment](https://github.com/insightsengineering/teal.reporter/issues/169#issuecomment-1733269638), I've updated the default value of global_knitr to list(echo = TRUE, tidy.opts = list(width.cutoff = 60), tidy = FALSE). Please note that there was an alternative option to set tidy = "styler", but I opted against it due to the additional dependency it would introduce (the styler package). Feel free to review the changes and let me know if any further adjustments are needed. ---- This branch utilizes the functionality provided by the 'formatR' package to automatically add spaces and indentations to the code. However, it does create a strong dependency on the 'formatR' package. If the package is not installed, the output will be generated without any formatting. Additionally, this branch introduces the following changes: - It exposes Knitr options to users through the functions 'download_report_button_srv', 'reporter_previewer_srv,' 'simple_reporter_srv,' and the R6 renderer. - The Roxygen documentation has been updated to reflect these modifications. changes in reports are observed as follows. branch: main image branch: 169_wrap_rcode@main image --------- Signed-off-by: kartikeya kirar Co-authored-by: kartikeya Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Marcin <133694481+m7pr@users.noreply.github.com> Co-authored-by: 27856297+dependabot-preview[bot]@users.noreply.github.com <27856297+dependabot-preview[bot]@users.noreply.github.com> --- .lintr | 3 +- DESCRIPTION | 5 ++-- NEWS.md | 2 ++ R/DownloadModule.R | 9 ++++-- R/Previewer.R | 9 ++++-- R/Renderer.R | 22 ++++++++------- R/ReportCard.R | 2 +- R/SimpleReporter.R | 5 ++++ R/utils.R | 18 ++++++++++++ R/zzz.R | 22 +++++++++++++++ man/Renderer.Rd | 47 ++++++++++++++++++++++++------- man/ReportCard.Rd | 2 +- man/download_report_button_srv.Rd | 13 +++++++++ man/reporter_previewer_srv.Rd | 13 +++++++++ man/simple_reporter_srv.Rd | 12 ++++++++ 15 files changed, 155 insertions(+), 29 deletions(-) create mode 100644 R/zzz.R diff --git a/.lintr b/.lintr index 34473d27..0a0bb22f 100644 --- a/.lintr +++ b/.lintr @@ -1,5 +1,6 @@ linters: linters_with_defaults( line_length_linter = line_length_linter(120), cyclocomp_linter = NULL, - object_usage_linter = NULL + object_usage_linter = NULL, + indentation_linter = NULL ) diff --git a/DESCRIPTION b/DESCRIPTION index 99ec742e..7c0ff475 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -31,6 +31,7 @@ Imports: zip (>= 1.1.0) Suggests: DT (>= 0.13), + formatR (>= 1.5), ggplot2 (>= 3.4.0), lattice (>= 0.18-4), png, @@ -44,8 +45,8 @@ RdMacros: Config/Needs/verdepcheck: rstudio/bslib, mllg/checkmate, rstudio/htmltools, yihui/knitr, r-lib/lifecycle, r-lib/R6, rstudio/rmarkdown, rstudio/shiny, dreamRs/shinyWidgets, - yaml=vubiostat/r-yaml, r-lib/zip, davidgohel/flextable, rstudio/DT, - tidyverse/ggplot2, deepayan/lattice, cran/png, + yaml=vubiostat/r-yaml, r-lib/zip, davidgohel/flextable, rstudio/DT, + yihui/formatR, tidyverse/ggplot2, deepayan/lattice, cran/png, insightsengineering/rtables, r-lib/testthat, rstudio/tinytex Config/Needs/website: insightsengineering/nesttemplate Encoding: UTF-8 diff --git a/NEWS.md b/NEWS.md index a210d1ef..54b61fff 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,8 @@ * `add_card_button_srv` allows to specify `card_fun` with `label` parameter for card's title & content customization. +* Supports automatic `Rcode` formatting using the suggested `formatR` package in reports. + # teal.reporter 0.2.1 ### Miscellaneous diff --git a/R/DownloadModule.R b/R/DownloadModule.R index f3f080a8..147b5269 100644 --- a/R/DownloadModule.R +++ b/R/DownloadModule.R @@ -33,11 +33,16 @@ download_report_button_ui <- function(id) { #' For more details see the vignette: `vignette("simpleReporter", "teal.reporter")`. #' @param id `character(1)` this `shiny` module's id. #' @param reporter [`Reporter`] instance. +#' @param global_knitr `list` a of `knitr` parameters (passed to `knitr::opts_chunk$set`) +#' for customizing the rendering process. #' @inheritParams reporter_download_inputs #' @return `shiny::moduleServer` +#' @details `r global_knitr_details()` +#' #' @export download_report_button_srv <- function(id, reporter, + global_knitr = getOption("teal.reporter.global_knitr"), rmd_output = c( "html" = "html_document", "pdf" = "pdf_document", "powerpoint" = "powerpoint_presentation", "word" = "word_document" @@ -48,6 +53,7 @@ download_report_button_srv <- function(id, toc = FALSE )) { checkmate::assert_class(reporter, "Reporter") + checkmate::assert_subset(names(global_knitr), names(knitr::opts_chunk$get())) checkmate::assert_subset( rmd_output, c( @@ -134,8 +140,7 @@ download_report_button_srv <- function(id, shiny::showNotification("Rendering and Downloading the document.") input_list <- lapply(names(rmd_yaml_args), function(x) input[[x]]) names(input_list) <- names(rmd_yaml_args) - global_knitr <- list() - if (is.logical(input$showrcode)) global_knitr <- list(echo = input$showrcode) + if (is.logical(input$showrcode)) global_knitr[["echo"]] <- input$showrcode report_render_and_compress(reporter, input_list, global_knitr, file) }, contentType = "application/zip" diff --git a/R/Previewer.R b/R/Previewer.R index 7ccaf2ff..41a3d913 100644 --- a/R/Previewer.R +++ b/R/Previewer.R @@ -31,10 +31,15 @@ reporter_previewer_ui <- function(id) { #' For more details see the vignette: `vignette("previewerReporter", "teal.reporter")`. #' @param id `character(1)` this `shiny` module's id. #' @param reporter `Reporter` instance +#' @param global_knitr `list` a of `knitr` parameters (passed to `knitr::opts_chunk$set`) +#' for customizing the rendering process. #' @inheritParams reporter_download_inputs +#' @details `r global_knitr_details()` +#' #' @export reporter_previewer_srv <- function(id, reporter, + global_knitr = getOption("teal.reporter.global_knitr"), rmd_output = c( "html" = "html_document", "pdf" = "pdf_document", "powerpoint" = "powerpoint_presentation", @@ -45,6 +50,7 @@ reporter_previewer_srv <- function(id, toc = FALSE )) { checkmate::assert_class(reporter, "Reporter") + checkmate::assert_subset(names(global_knitr), names(knitr::opts_chunk$get())) checkmate::assert_subset( rmd_output, c( @@ -179,8 +185,7 @@ reporter_previewer_srv <- function(id, shiny::showNotification("Rendering and Downloading the document.") input_list <- lapply(names(rmd_yaml_args), function(x) input[[x]]) names(input_list) <- names(rmd_yaml_args) - global_knitr <- list() - if (is.logical(input$showrcode)) global_knitr <- list(echo = input$showrcode) + if (is.logical(input$showrcode)) global_knitr[["echo"]] <- input$showrcode report_render_and_compress(reporter, input_list, global_knitr, file) }, contentType = "application/zip" diff --git a/R/Renderer.R b/R/Renderer.R index 1ce2be5b..f086cc82 100644 --- a/R/Renderer.R +++ b/R/Renderer.R @@ -22,14 +22,16 @@ Renderer <- R6::R6Class( # nolint: object_name_linter. #' @description getting the `Rmd` text which could be easily rendered later. #' #' @param blocks `list` of `c("TextBlock", "PictureBlock", "NewpageBlock")` objects. - #' @param yaml_header `character` a `rmarkdown` `yaml` header. - #' @param global_knitr `list` a global `knitr` parameters, like echo. - #' But if local parameter is set it will have priority. - #' Defaults to empty `list()`. + #' @param yaml_header `character` an `rmarkdown` `yaml` header. + #' @param global_knitr `list` a of `knitr` parameters (passed to `knitr::opts_chunk$set`) + #' for customizing the rendering process. + #' @details `r global_knitr_details()` #' #' @return `character` a `Rmd` text (`yaml` header + body), ready to be rendered. - renderRmd = function(blocks, yaml_header, global_knitr = list()) { + renderRmd = function(blocks, yaml_header, global_knitr = getOption("teal.reporter.global_knitr")) { checkmate::assert_list(blocks, c("TextBlock", "PictureBlock", "NewpageBlock", "TableBlock", "RcodeBlock")) + checkmate::assert_subset(names(global_knitr), names(knitr::opts_chunk$get())) + if (missing(yaml_header)) { yaml_header <- md_header(yaml::as.yaml(list(title = "Report"))) } @@ -84,13 +86,13 @@ Renderer <- R6::R6Class( # nolint: object_name_linter. #' #' @param blocks `list` of `c("TextBlock", "PictureBlock", "NewpageBlock")` objects. #' @param yaml_header `character` an `rmarkdown` `yaml` header. - #' @param global_knitr `list` a global `knitr` parameters, like echo. - #' But if local parameter is set it will have priority. - #' Defaults to empty `list()`. - #' @param ... `rmarkdown::render` arguments, `input` and `output_dir` should not be updated.z + #' @param global_knitr `list` a of `knitr` parameters (passed to `knitr::opts_chunk$set`) + #' for customizing the rendering process. + #' @param ... `rmarkdown::render` arguments, `input` and `output_dir` should not be updated. + #' @details `r global_knitr_details()` #' #' @return `character` path to the output - render = function(blocks, yaml_header, global_knitr = list(), ...) { + render = function(blocks, yaml_header, global_knitr = getOption("teal.reporter.global_knitr"), ...) { args <- list(...) input_path <- self$renderRmd(blocks, yaml_header, global_knitr) args <- append(args, list( diff --git a/R/ReportCard.R b/R/ReportCard.R index 37f902bf..49352409 100644 --- a/R/ReportCard.R +++ b/R/ReportCard.R @@ -60,7 +60,7 @@ ReportCard <- R6::R6Class( # nolint: object_name_linter. self$append_content(TextBlock$new(text, style)) invisible(self) }, - #' @description Appends a `rmarkdown` R chunk to this `ReportCard`. + #' @description Appends an `rmarkdown` R chunk to this `ReportCard`. #' #' @param text (`character(0)` or `character(1)`) the text #' @param ... any `rmarkdown` R chunk parameter and its value. diff --git a/R/SimpleReporter.R b/R/SimpleReporter.R index a5f0d1bb..6766fab9 100644 --- a/R/SimpleReporter.R +++ b/R/SimpleReporter.R @@ -46,12 +46,16 @@ simple_reporter_ui <- function(id) { #' @param reporter [`Reporter`] instance. #' @param card_fun `function` which returns a [`ReportCard`] instance, #' the function has a `card` argument and an optional `comment` argument. +#' @param global_knitr `list` a global `knitr` parameters for customizing the rendering process. #' @inheritParams reporter_download_inputs +#' @details `r global_knitr_details()` +#' #' @return `shiny::moduleServer` #' @export simple_reporter_srv <- function(id, reporter, card_fun, + global_knitr = getOption("teal.reporter.global_knitr"), rmd_output = c( "html" = "html_document", "pdf" = "pdf_document", "powerpoint" = "powerpoint_presentation", "word" = "word_document" @@ -68,6 +72,7 @@ simple_reporter_srv <- function(id, download_report_button_srv( "download_button_simple", reporter = reporter, + global_knitr = global_knitr, rmd_output = rmd_output, rmd_yaml_args = rmd_yaml_args ) diff --git a/R/utils.R b/R/utils.R index 59064a22..c9b10403 100644 --- a/R/utils.R +++ b/R/utils.R @@ -277,3 +277,21 @@ split_text_block <- function(x, n) { ind <- rep(1:nblocks, each = n)[seq_along(lines)] unname(lapply(split(lines, ind), paste, collapse = "\n")) } + +#' Retrieve text details for global_knitr options +#' This function returns a character string describing the default settings for the global_knitr options. +#' @noRd +#' +#' @keywords internal +global_knitr_details <- function() { + paste0( + c( + " To access the default values for the `global_knitr` parameter,", + " use `getOption('teal.reporter.global_knitr')`. These defaults include:", + " - `echo = TRUE`", + " - `tidy.opts = list(width.cutoff = 60)`", + " - `tidy = TRUE` if `formatR` package is installed, `FALSE` otherwise" + ), + collapse = "\n" + ) +} diff --git a/R/zzz.R b/R/zzz.R new file mode 100644 index 00000000..61c594fc --- /dev/null +++ b/R/zzz.R @@ -0,0 +1,22 @@ +.onLoad <- function(libname, pkgname) { + op <- options() + default_global_knitr <- list(teal.reporter.global_knitr = list( + echo = TRUE, + tidy.opts = list(width.cutoff = 60), + tidy = requireNamespace("formatR", quietly = TRUE) + )) + + if (!("teal.reporter.global_knitr" %in% names(op))) { + options(default_global_knitr) + } + + invisible() +} + +.onAttach <- function(libname, pkgname) { + packageStartupMessage( + if (!requireNamespace("formatR", quietly = TRUE)) { + "For better code formatting, consider installing the formatR package." + } + ) +} diff --git a/man/Renderer.Rd b/man/Renderer.Rd index e2e2610e..990d95c2 100644 --- a/man/Renderer.Rd +++ b/man/Renderer.Rd @@ -53,7 +53,11 @@ Finalizes a \code{Renderer} object. \subsection{Method \code{renderRmd()}}{ getting the \code{Rmd} text which could be easily rendered later. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Renderer$renderRmd(blocks, yaml_header, global_knitr = list())}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{Renderer$renderRmd( + blocks, + yaml_header, + global_knitr = getOption("teal.reporter.global_knitr") +)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -61,14 +65,23 @@ getting the \code{Rmd} text which could be easily rendered later. \describe{ \item{\code{blocks}}{\code{list} of \code{c("TextBlock", "PictureBlock", "NewpageBlock")} objects.} -\item{\code{yaml_header}}{\code{character} a \code{rmarkdown} \code{yaml} header.} +\item{\code{yaml_header}}{\code{character} an \code{rmarkdown} \code{yaml} header.} -\item{\code{global_knitr}}{\code{list} a global \code{knitr} parameters, like echo. -But if local parameter is set it will have priority. -Defaults to empty \code{list()}.} +\item{\code{global_knitr}}{\code{list} a of \code{knitr} parameters (passed to \code{knitr::opts_chunk$set}) +for customizing the rendering process.} } \if{html}{\out{}} } +\subsection{Details}{ +To access the default values for the \code{global_knitr} parameter, +use \code{getOption('teal.reporter.global_knitr')}. These defaults include: +\itemize{ +\item \code{echo = TRUE} +\item \code{tidy.opts = list(width.cutoff = 60)} +\item \code{tidy = TRUE} if \code{formatR} package is installed, \code{FALSE} otherwise +} +} + \subsection{Returns}{ \code{character} a \code{Rmd} text (\code{yaml} header + body), ready to be rendered. } @@ -79,7 +92,12 @@ Defaults to empty \code{list()}.} \subsection{Method \code{render()}}{ Renders the content of this \code{Report} to the output file \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Renderer$render(blocks, yaml_header, global_knitr = list(), ...)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{Renderer$render( + blocks, + yaml_header, + global_knitr = getOption("teal.reporter.global_knitr"), + ... +)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -89,14 +107,23 @@ Renders the content of this \code{Report} to the output file \item{\code{yaml_header}}{\code{character} an \code{rmarkdown} \code{yaml} header.} -\item{\code{global_knitr}}{\code{list} a global \code{knitr} parameters, like echo. -But if local parameter is set it will have priority. -Defaults to empty \code{list()}.} +\item{\code{global_knitr}}{\code{list} a of \code{knitr} parameters (passed to \code{knitr::opts_chunk$set}) +for customizing the rendering process.} -\item{\code{...}}{\code{rmarkdown::render} arguments, \code{input} and \code{output_dir} should not be updated.z} +\item{\code{...}}{\code{rmarkdown::render} arguments, \code{input} and \code{output_dir} should not be updated.} } \if{html}{\out{}} } +\subsection{Details}{ +To access the default values for the \code{global_knitr} parameter, +use \code{getOption('teal.reporter.global_knitr')}. These defaults include: +\itemize{ +\item \code{echo = TRUE} +\item \code{tidy.opts = list(width.cutoff = 60)} +\item \code{tidy = TRUE} if \code{formatR} package is installed, \code{FALSE} otherwise +} +} + \subsection{Returns}{ \code{character} path to the output } diff --git a/man/ReportCard.Rd b/man/ReportCard.Rd index 4a563a17..204ff30f 100644 --- a/man/ReportCard.Rd +++ b/man/ReportCard.Rd @@ -255,7 +255,7 @@ invisibly self \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-ReportCard-append_rcode}{}}} \subsection{Method \code{append_rcode()}}{ -Appends a \code{rmarkdown} R chunk to this \code{ReportCard}. +Appends an \code{rmarkdown} R chunk to this \code{ReportCard}. \subsection{Usage}{ \if{html}{\out{
}}\preformatted{ReportCard$append_rcode(text, ...)}\if{html}{\out{
}} } diff --git a/man/download_report_button_srv.Rd b/man/download_report_button_srv.Rd index 18ced465..28b22b44 100644 --- a/man/download_report_button_srv.Rd +++ b/man/download_report_button_srv.Rd @@ -7,6 +7,7 @@ download_report_button_srv( id, reporter, + global_knitr = getOption("teal.reporter.global_knitr"), rmd_output = c(html = "html_document", pdf = "pdf_document", powerpoint = "powerpoint_presentation", word = "word_document"), rmd_yaml_args = list(author = "NEST", title = "Report", date = @@ -18,6 +19,9 @@ download_report_button_srv( \item{reporter}{\code{\link{Reporter}} instance.} +\item{global_knitr}{\code{list} a of \code{knitr} parameters (passed to \code{knitr::opts_chunk$set}) +for customizing the rendering process.} + \item{rmd_output}{\code{character} vector with \code{rmarkdown} output types, by default all possible \code{c("pdf_document", "html_document", "powerpoint_presentation", "word_document")}. If vector is named then those names will appear in the \code{UI}.} @@ -37,3 +41,12 @@ server for downloading the Report. For more details see the vignette: \code{vignette("simpleReporter", "teal.reporter")}. } +\details{ +To access the default values for the \code{global_knitr} parameter, +use \code{getOption('teal.reporter.global_knitr')}. These defaults include: +\itemize{ +\item \code{echo = TRUE} +\item \code{tidy.opts = list(width.cutoff = 60)} +\item \code{tidy = TRUE} if \code{formatR} package is installed, \code{FALSE} otherwise +} +} diff --git a/man/reporter_previewer_srv.Rd b/man/reporter_previewer_srv.Rd index bc1a15f5..cea7f90c 100644 --- a/man/reporter_previewer_srv.Rd +++ b/man/reporter_previewer_srv.Rd @@ -7,6 +7,7 @@ reporter_previewer_srv( id, reporter, + global_knitr = getOption("teal.reporter.global_knitr"), rmd_output = c(html = "html_document", pdf = "pdf_document", powerpoint = "powerpoint_presentation", word = "word_document"), rmd_yaml_args = list(author = "NEST", title = "Report", date = @@ -18,6 +19,9 @@ reporter_previewer_srv( \item{reporter}{\code{Reporter} instance} +\item{global_knitr}{\code{list} a of \code{knitr} parameters (passed to \code{knitr::opts_chunk$set}) +for customizing the rendering process.} + \item{rmd_output}{\code{character} vector with \code{rmarkdown} output types, by default all possible \code{c("pdf_document", "html_document", "powerpoint_presentation", "word_document")}. If vector is named then those names will appear in the \code{UI}.} @@ -33,3 +37,12 @@ The default value for \code{"output"} has to be in the \code{rmd_output} argumen server supporting the functionalities of the reporter previewer For more details see the vignette: \code{vignette("previewerReporter", "teal.reporter")}. } +\details{ +To access the default values for the \code{global_knitr} parameter, +use \code{getOption('teal.reporter.global_knitr')}. These defaults include: +\itemize{ +\item \code{echo = TRUE} +\item \code{tidy.opts = list(width.cutoff = 60)} +\item \code{tidy = TRUE} if \code{formatR} package is installed, \code{FALSE} otherwise +} +} diff --git a/man/simple_reporter_srv.Rd b/man/simple_reporter_srv.Rd index 741fcf50..dbbec8fe 100644 --- a/man/simple_reporter_srv.Rd +++ b/man/simple_reporter_srv.Rd @@ -8,6 +8,7 @@ simple_reporter_srv( id, reporter, card_fun, + global_knitr = getOption("teal.reporter.global_knitr"), rmd_output = c(html = "html_document", pdf = "pdf_document", powerpoint = "powerpoint_presentation", word = "word_document"), rmd_yaml_args = list(author = "NEST", title = "Report", date = @@ -22,6 +23,8 @@ simple_reporter_srv( \item{card_fun}{\code{function} which returns a \code{\link{ReportCard}} instance, the function has a \code{card} argument and an optional \code{comment} argument.} +\item{global_knitr}{\code{list} a global \code{knitr} parameters for customizing the rendering process.} + \item{rmd_output}{\code{character} vector with \code{rmarkdown} output types, by default all possible \code{c("pdf_document", "html_document", "powerpoint_presentation", "word_document")}. If vector is named then those names will appear in the \code{UI}.} @@ -43,3 +46,12 @@ and the reset module the \code{reset_button_simple} id. For more details see the vignette: \code{vignette("simpleReporter", "teal.reporter")}. } +\details{ +To access the default values for the \code{global_knitr} parameter, +use \code{getOption('teal.reporter.global_knitr')}. These defaults include: +\itemize{ +\item \code{echo = TRUE} +\item \code{tidy.opts = list(width.cutoff = 60)} +\item \code{tidy = TRUE} if \code{formatR} package is installed, \code{FALSE} otherwise +} +}