insightsengineering / teal.reporter

Create and preview reports with Shiny modules
https://insightsengineering.github.io/teal.reporter/
Other
8 stars 9 forks source link

[Feature Request]: Rcode formating in PPT outputs #212

Closed kartikeyakirar closed 9 months ago

kartikeyakirar commented 10 months ago

Feature description

This task is an extension of the task https://github.com/insightsengineering/teal.reporter/issues/185. When a user includes R code in PPT format, it often follows a Table or Plot, causing the slide to struggle with accommodating a large code chunk. For PPT presentations, I believe it would be beneficial to break the code chunk into a more manageable number of lines that can easily fit on each slide. input_20230908125145071.pptx

Screenshot 2023-09-08 at 6 28 07 PM

I developed a solution during my work on the aforementioned task (https://github.com/insightsengineering/teal.reporter/issues/185). This code essentially checks whether the report_type is set to 'powerpoint_presentation.' If it is, the code breaks the code into a list of chunks based on a specified number of lines and displays each chunk on a separate slide.

Output of below code change sampleppt.pptx

Code to split codeblock into list

#' Split a text block into smaller blocks with a specified number of lines.
#'
#' This function takes a block of text and divides it into smaller blocks, each containing
#' a specified number of lines.
#'
#' @param block_text A character vector containing the input block of text.
#' @param n The number of lines per block.
#'
#' @return A list of character vectors, where each element is a smaller block of text
#'         containing 'n' lines. If the input block of text has fewer lines than 'n', the
#'         entire block is returned as a single element list.
#'
#' @examples
#' block_text <- "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6"
#' n <- 3
#' split_text_into_blocks(block_text, n)
#'
#' @export
split_text_into_blocks <- function(block_text, n) {
  lines <- strsplit(block_text, "\n")[[1]]
  num_lines <- length(lines)

  if (num_lines <= n) {
    return(list(block_text))
  }

  num_blocks <- ceiling(num_lines / n)
  blocks <- vector("list", length = num_blocks)

  for (i in 1:num_blocks) {
    start <- (i - 1) * n + 1
    end <- min(i * n, num_lines)
    block <- paste(lines[start:end], collapse = "\n")
    blocks[[i]] <- block
  }

  return(blocks)
}

Update rcodeBlock2md

rcodeBlock2md = function(block, report_type) {
  params <- block$get_params()
  params <- lapply(params, function(l) if (is.character(l)) shQuote(l) else l)
  block_content <- block$get_content()

  if(report_type == "powerpoint_presentation") {
    block_content_lst <- split_text_into_blocks(block_content, 10)
    paste(
      unlist(
        lapply(block_content_lst, function(b) {
          sprintf(
            "\n```{r, %s}\n%s\n```\n",
            paste(names(params), params, sep = "=", collapse = ", "),
            block_content
          )
        })
      ),
      collapse = "\n\n"
    )
  } else {
    sprintf(
      "\n```{r, %s}\n%s\n```\n",
      paste(names(params), params, sep = "=", collapse = ", "),
      block_content
    )
  }
},

Supporting function to read YAML header to extract report type.

#' Extract a Field from YAML and Optionally Retrieve Names from a List
#'
#' This function parses a YAML text and extracts the specified field. It provides
#' the option to retrieve the names of elements from a list if the field contains a list.
#'
#' @param yaml_text A character vector containing the YAML text.
#' @param field_name The name of the field to extract.
#' @param check_list Logical, indicating whether to check if the result is a list
#'                   and retrieve the names of list elements. Default is TRUE.
#'
#' @return If `check_list` is TRUE and the result is a list, it returns the names of
#'         elements in the list; otherwise, it returns the extracted field.
#'
#' @examples
#' yaml_text <- "\nauthor: NEST\ntitle: Report\noutput:\n  powerpoint_presentation:\n    toc: yes\n"
#' reverse_yaml_field(yaml_text, "output") # Returns a character vector with "Reading" and "Cooking"

#'
#' @export
reverse_yaml_field <- function(yaml_text, field_name, check_list = TRUE) {
  checkmate::assert_multi_class(yaml_text, c("rmd_yaml_header", "character"))
  checkmate::assert_string(field_name)
  checkmate::assert_logical(check_list)
  # Parse the YAML text
  yaml_obj <- yaml::yaml.load(yaml_text)

  # Extract the specified field
  if (field_name %in% names(yaml_obj)) {
    result <- yaml_obj[[field_name]]
    if (check_list && is.list(result)) {
      return(names(result))
    }
    return(result)
  }
}

Code of Conduct

Contribution Guidelines

Security Policy

kartikeyakirar commented 10 months ago

@donyunardi and @cicdguy, I'd like to gather your thoughts on this matter. I want to ensure whether this is a necessity or not. I'm not entirely certain if users prefer to include code in the presentation slides.

lcd2yyz commented 10 months ago

Good questions @kartikeyakirar! I would say it's not necessary by default, especially for word, ppt or pdf. For html, we can leverage the "hide/show" toggle. Can we design this into a configurable option, either by app developer or app user?

kartikeyakirar commented 10 months ago

I would say it's not necessary by default, especially for word, ppt or pdf.

In my opinion, this feature is only necessary for slides. When working with Word, PDF, or HTML formats, there is no need to break or modify format to display the code. This configuration is already available for including/cluding the code when downloading reports.

image
lcd2yyz commented 10 months ago

When working with Word, PDF, or HTML formats, there is no need to break or modify format to display the code. Yes true. But what I meant is that not all users will want to see R codes in the report, they are probably more interested in the analysis results. So we should consider giving the option to users, to choose to include R codes or not, even when output format is word/pdf/html. And so, the checkbox option you designed looks good to me!

And coming back to the original scope of this particular issue (R code formatting in ppt), your proposed solution of split into code blocks looks reasonable to me.

One additional request I would like to make is to drastically reduce the default font size of the R code (for example, maybe use 8 or less as default). Also I'm not sure if it's possible or easy to implement - since the default slide orientation is landscape, it would be nice to display the code in two columns on one slide. Essentially, from an user perspective, I would want to see as much code displayed in one page as possible.

kartikeyakirar commented 10 months ago

One additional request I would like to make is to drastically reduce the default font size of the R code

I initially attempted to reduce the font size for a specific slide, but unfortunately, it didn't work as expected. However, I can try modify the font and other properties using flextable and display the code in two columns. (just for power point presentation )

kartikeyakirar commented 10 months ago

@donyunardi and @lcd2yyz

I've made changes to format the R code within the slides. Below is a sample output with the R code placed on separate slides. I attempted to place the code in two columns as recommended, but rather than displaying the maximum code , it occupied too much space, resulting in an unappealing appearance. Please share your thoughts on this output.

[font size:8 and width : 10(inches)] input_20230919205655718.pptx

Another sample output [font size:7 and width : 8(inches)] input_20230919225802219.pptx

Sample screenshot

image
lcd2yyz commented 10 months ago

Thanks for the updates @kartikeyakirar

Fontsize 7 looks good to me (on a typical 23-inch monitor at 100% scale).

Thanks for testing out the two column layout - I can imagine for codes with very long statement (like the one in example), two columns might be less effective. So I'm fine with sticking to one-column for all.

Two additional suggestions as nice-to-haves

kartikeyakirar commented 10 months ago

Is it possible to switch to "Blank" layout for the code sections in ppt? It should be able to save quite a lot of spaces at the top.

I've attempted this previously, but it's not recognizing the blank layout. I've exhausted my options while utilizing output type "powerpoint_presentation" and the default theme. Even though I haven't specified a title, it's still using the title+content layout.

Can we use a different font just for the code section (all other sections can remain as-is). My preference is Courier New.

this is done.

here is sample input_20230920151748434.pptx

lcd2yyz commented 10 months ago

Thanks @kartikeyakirar! and for exploring the various options. I have no further comments.