Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

185 fixing table formatting in PPT outputs #211

Merged
merged 45 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
0c6c097
docs: add minimal version of deps
averissimo Jul 25, 2023
1877d51
fix: ggplot2 needs to be bumped higher due to a dependency
averissimo Jul 26, 2023
1e6f3fa
fix: zip and shinyWidgets need to be bumped higher
averissimo Jul 26, 2023
ea981fe
doc: corrects NEWS after rebase
averissimo Sep 1, 2023
b78f92f
docs: remove old working branch
averissimo Sep 1, 2023
49fb444
ci: remove push on branch to prepare merge
averissimo Sep 4, 2023
b3fbfce
updating utils function to convert it into flextable.
Sep 7, 2023
c0b3a4a
updating utils function for flextable.
Sep 7, 2023
602a568
updating description.
Sep 7, 2023
4bdf620
merge back from main
Sep 7, 2023
65e395b
resolved conflict with description
Sep 7, 2023
c204d8e
Merge 65e395b655ca6ed6f5b11b8a5898c10afa15813c into 55e890abb7ba165f6…
kartikeyakirar Sep 7, 2023
1e1f3fb
[skip actions] Restyle files
github-actions[bot] Sep 7, 2023
2ad9352
add reverse_yaml_field function to extract report type
Sep 7, 2023
d0029ab
Merge branch '185_fixing_formating@main' of https://github.com/insigh…
Sep 7, 2023
861ccc1
reversing code split logic
Sep 8, 2023
ec29177
Merge 861ccc1a0f947aedfd37ad9e36085cf66076f260 into 55e890abb7ba165f6…
kartikeyakirar Sep 8, 2023
19bf333
[skip actions] Restyle files
github-actions[bot] Sep 8, 2023
4f04939
updating test and fixing linter
Sep 8, 2023
f9fea46
removing duplicate news due to merge issue.
Sep 8, 2023
92df034
[skip actions] Roxygen Man Pages Auto Update
dependabot-preview[bot] Sep 8, 2023
ca455be
updating Rd and fixing test
Sep 8, 2023
c9f5e69
blank commit
Sep 8, 2023
a1d97b2
removing magritter dependencies
Sep 8, 2023
a6d2e2a
added custom theme
Sep 8, 2023
95d5a49
Merge a6d2e2a2ae9a423a1fdd4566e4da1661287ec0b9 into 55e890abb7ba165f6…
kartikeyakirar Sep 8, 2023
945c377
[skip actions] Restyle files
github-actions[bot] Sep 8, 2023
221abdd
Empty-Commit
Sep 8, 2023
7d935b0
merge back from main
Sep 11, 2023
5f6fc66
fixing preview
Sep 11, 2023
2a7f44f
Merge branch 'main' into 185_fixing_formating@main
kartikeyakirar Sep 11, 2023
b3aab20
updating description for verdepcheck checka
Sep 12, 2023
7a8ccd5
Merge branch 'main' into 185_fixing_formating@main
kartikeyakirar Sep 14, 2023
4a7cfa9
Update R/Renderer.R
kartikeyakirar Sep 15, 2023
9c2c397
Merge 4a7cfa9bcfec912c276b664440246a94edf84eba into 89546d6a806f56fa5…
kartikeyakirar Sep 15, 2023
1705ffd
[skip actions] Restyle files
github-actions[bot] Sep 15, 2023
d550252
Update R/utils.R
kartikeyakirar Sep 15, 2023
6fb45ef
Update R/utils.R
kartikeyakirar Sep 15, 2023
e78f6bd
Update R/utils.R
kartikeyakirar Sep 15, 2023
a2f6613
Update R/utils.R
kartikeyakirar Sep 15, 2023
52a773c
Update R/utils.R
kartikeyakirar Sep 15, 2023
4ca61b2
adding tests
Sep 15, 2023
1bf2cee
Update tests/testthat/test-utils.R
kartikeyakirar Sep 15, 2023
f71c42f
Update tests/testthat/test-utils.R
kartikeyakirar Sep 15, 2023
0e9765b
updating name
Sep 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ repos:
- shinyWidgets
- yaml
- zip
- flextable
- id: spell-check
name: Check spelling with `spelling`
exclude: >
Expand Down
9 changes: 5 additions & 4 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ Imports:
shiny (>= 1.6.0),
shinyWidgets (>= 0.5.1),
yaml (>= 1.1.0),
zip (>= 1.1.0)
zip (>= 1.1.0),
flextable (>= 0.9.2)
Suggests:
DT (>= 0.13),
ggplot2 (>= 3.4.0),
Expand All @@ -43,9 +44,9 @@ 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, rstudio/DT, tidyverse/ggplot2,
deepayan/lattice, cran/png, insightsengineering/rtables,
r-lib/testthat, rstudio/tinytex
yaml=vubiostat/r-yaml, r-lib/zip, davidgohel/flextable,
rstudio/DT, tidyverse/ggplot2, deepayan/lattice, cran/png,
insightsengineering/rtables, r-lib/testthat, rstudio/tinytex
Config/Needs/website: insightsengineering/nesttemplate
Encoding: UTF-8
Language: en-US
Expand Down
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Miscellaneous

* Specify minimal version of dependent packages.
* Updated `TableBlock` to convert tables into `flextables` to show in rendered report.
* Fixed CRAN requirements for the first CRAN submission.
* Removed manual pages for non-exported objects.

Expand Down
2 changes: 1 addition & 1 deletion R/Previewer.R
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ block_to_html <- function(b) {
} else if (inherits(b, "TableBlock")) {
b_table <- readRDS(b_content)
shiny::tags$pre(
paste(utils::capture.output(print(b_table)), collapse = "\n")
flextable::htmltools_value(b_table)
)
} else if (inherits(b, "NewpageBlock")) {
shiny::tags$br()
Expand Down
15 changes: 11 additions & 4 deletions R/Renderer.R
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Renderer <- R6::R6Class( # nolint: object_name_linter.
"\n```{r setup, include=FALSE}\nknitr::opts_chunk$set(%s)\n```\n",
capture.output(dput(global_knitr))
)

parsed_blocks <- paste(
unlist(
lapply(blocks, function(b) private$block2md(b))
Expand Down Expand Up @@ -119,10 +120,16 @@ Renderer <- R6::R6Class( # nolint: object_name_linter.
params <- block$get_params()
params <- lapply(params, function(l) if (is.character(l)) shQuote(l) else l)
block_content <- block$get_content()
sprintf(
"\n```{r, %s}\n%s\n```\n",
paste(names(params), params, sep = "=", collapse = ", "),
block_content
paste(
sep = "\n",
collapse = "\n",
"### ",
sprintf(
"```{r, %s}", paste(names(params), params, sep = "=", collapse = ", ")
),
block_content,
"```",
""
)
},
pictureBlock2md = function(block) {
Expand Down
1 change: 1 addition & 0 deletions R/TableBlock.R
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ TableBlock <- R6::R6Class( # nolint: object_name_linter.
#' @return invisibly self
set_content = function(content) {
checkmate::assert_multi_class(content, private$supported_tables)
content <- to_flextable(content)
path <- tempfile(fileext = ".rds")
saveRDS(content, file = path)
super$set_content(path)
Expand Down
121 changes: 121 additions & 0 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,124 @@ panel_item <- function(title, ..., collapsed = TRUE, input_id = NULL) {
)
})
}

#' Convert content into a `flextable`, merge cells with `colspan` > 1
#' align columns to the center, and row names to the left
#' Indent the row names by 10 times indentation
#'
#' @param content Supported formats: `data.frame`, `rtables`, `TableTree`, `ElementaryTable`

#' @return (`flextable`)
#'
#' @keywords internal
to_flextable <- function(content) {
if (inherits(content, c("rtables", "TableTree", "ElementaryTable"))) {
mf <- rtables::matrix_form(content)
nr_header <- attr(mf, "nrow_header")
non_total_coln <- c(TRUE, !grepl("All Patients", names(content)))
df <- as.data.frame(mf$strings[seq(nr_header + 1, nrow(mf$strings)), , drop = FALSE])
header_df <- as.data.frame(mf$strings[seq_len(nr_header), , drop = FALSE])

ft <- flextable::flextable(df)
ft <- flextable::delete_part(ft, part = "header")
ft <- flextable::add_header(ft, values = header_df)

merge_index_body <- get_merge_index(mf$spans[seq(nr_header + 1, nrow(mf$spans)), , drop = FALSE])
merge_index_header <- get_merge_index(mf$spans[seq_len(nr_header), , drop = FALSE])

ft <- merge_at_indice(ft, lst = merge_index_body, part = "body")
ft <- merge_at_indice(ft, lst = merge_index_header, part = "header")
ft <- flextable::align_text_col(ft, align = "center", header = TRUE)
ft <- flextable::align(ft, i = seq_len(nrow(content)), j = 1, align = "left")
ft <- padding_lst(ft, mf$row_info$indent)
ft <- flextable::padding(ft, padding.top = 1, padding.bottom = 1, part = "all")
ft <- flextable::autofit(ft, add_h = 0)

width_vector <- c(
dim(ft)$widths[1],
rep(sum(dim(ft)$widths[-1]), length(dim(ft)$widths) - 1) / (ncol(mf$strings) - 1)
)
ft <- flextable::width(ft, width = width_vector)
} else if (inherits(content, "data.frame")) {
ft <- flextable::flextable(content)
} else {
stop(paste0("Unsupported class `(", format(class(content)), ")` when exporting table"))
}
kartikeyakirar marked this conversation as resolved.
Show resolved Hide resolved

ft <- custom_theme(ft)
if (flextable::flextable_dim(ft)$widths > 10) {
pgwidth <- 10.5
width_vector <- dim(ft)$widths * pgwidth / flextable::flextable_dim(ft)$widths
ft <- flextable::width(ft, width = width_vector)
}

ft
}

#' Apply a custom theme to a `flextable`
#' @noRd
#'
#' @keywords internal
custom_theme <- function(ft) {
ft <- flextable::fontsize(ft, size = 8, part = "body")
ft <- flextable::bold(ft, part = "header")
ft <- flextable::theme_booktabs(ft)
ft <- flextable::hline(ft, border = flextable::fp_border_default(width = 1, color = "grey"))
ft <- flextable::border_outer(ft)
ft
}

#' Get the merge index for a single span.
#' This function retrieves the merge index for a single span,
#' which is used in merging cells.
#' @noRd
kartikeyakirar marked this conversation as resolved.
Show resolved Hide resolved
#'
#' @keywords internal
get_merge_index_single <- function(span) {
ret <- list()
j <- 1
while (j < length(span)) {
if (span[j] != 1) {
ret <- c(ret, list(seq(j, j + span[j] - 1)))
}
j <- j + span[j]
}
return(ret)
}

#' Get the merge index for multiple spans.
#' This function merges cells in a `flextable` at specified row and column indices.
#' @noRd
#'
#' @keywords internal
get_merge_index <- function(spans) {
kartikeyakirar marked this conversation as resolved.
Show resolved Hide resolved
ret <- lapply(seq_len(nrow(spans)), function(i) {
ri <- spans[i, ]
r <- get_merge_index_single(ri)
lapply(r, function(s) {
list(j = s, i = i)
})
})
unlist(ret, recursive = FALSE, use.names = FALSE)
}

#' Merge cells in a `flextable` at specified indices
#' @noRd
#'
#' @keywords internal
merge_at_indice <- function(ft, lst, part) {
kartikeyakirar marked this conversation as resolved.
Show resolved Hide resolved
Reduce(function(ft, ij) {
flextable::merge_at(ft, i = ij$i, j = ij$j, part = part)
}, lst, ft)
}

#' Apply padding to a `flextable` based on indentation levels.
#' This function applies padding to a `flextable` based on indentation levels provided as a vector.
#' @noRd
#'
#' @keywords internal
padding_lst <- function(ft, indents) {
kartikeyakirar marked this conversation as resolved.
Show resolved Hide resolved
Reduce(function(ft, s) {
flextable::padding(ft, s, 1, padding.left = (indents[s] + 1) * 10)
}, seq_len(length(indents)), ft)
}
22 changes: 22 additions & 0 deletions man/to_flextable.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 46 additions & 0 deletions tests/testthat/test-utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,49 @@ testthat::test_that("get_bs_version", {
testthat::test_that("panel_item", {
testthat::expect_s3_class(panel_item("LABEL", shiny::tags$div()), "shiny.tag")
})

testthat::test_that("Test to_flextable: supported class", {
kartikeyakirar marked this conversation as resolved.
Show resolved Hide resolved
data_frame <- data.frame(A = 1:3, B = 4:6)
flextable_output <- to_flextable(data_frame)
testthat::expect_s3_class(flextable_output, "flextable")
})

testthat::test_that("Test to_flextable: unsupported class", {
unsupported_data <- list(a = 1, b = 2)
expect_error(to_flextable(unsupported_data), "Unsupported class")
})

test_that("custom theme to flextable", {
kartikeyakirar marked this conversation as resolved.
Show resolved Hide resolved
sample_ft <- flextable::qflextable(head(mtcars))
themed_ft <- custom_theme(sample_ft)
expect_is(themed_ft, "flextable")
})

test_that("get_merge_index_single", {
sample_span <- c(1, 2, 1, 3)
merge_index <- get_merge_index_single(sample_span)
expect_is(merge_index, "list")
})

test_that("get_merge_index", {
sample_spans <- matrix(c(1, 2, 1, 3, 2, 1, 1, 1), ncol = 2)
merge_index <- get_merge_index(sample_spans)
expect_is(merge_index, "list")
})

test_that("merge_at_indice", {
sample_ft <- flextable::qflextable(head(mtcars))
merge_indices <- list(
list(i = 1, j = 1:2),
list(i = 2, j = 3:4)
)
merged_ft <- merge_at_indice(sample_ft, lst = merge_indices, part = "body")
expect_is(merged_ft, "flextable")
})

test_that("padding_lst applies padding to a flextable based on indentation levels", {
sample_ft <- flextable::qflextable(head(mtcars))
sample_indents <- c(1, 2, 1, 3, 2)
padded_ft <- padding_lst(sample_ft, sample_indents)
expect_is(padded_ft, "flextable")
})