Skip to content

Commit 0fba81a

Browse files
authored
Merge pull request #71 from metrumresearchgroup/patch/trac-mat
Traceability matrix adjustment
2 parents a4ffca8 + f445b89 commit 0fba81a

12 files changed

+365
-119
lines changed

R/format-report.R

Lines changed: 141 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -686,11 +686,48 @@ format_traceability_matrix <- function(
686686
checkmate::assert_logical(wrap_cols)
687687

688688
if(!is.null(exports_df)){
689-
### Exported Functions ###
690-
# Unnest tests and testing directories
689+
if ("exported_function" %in% names(exports_df)) {
690+
# Align internal scoring with external format.
691+
exports_df <- dplyr::rename(exports_df, entrypoint = "exported_function")
692+
}
693+
694+
entry_name <- switch(scorecard_type,
695+
"R" = "Exported Function",
696+
"cli" = "Command",
697+
"Entry Point"
698+
)
699+
exports_df <- dplyr::rename(exports_df, !!entry_name := "entrypoint")
700+
701+
# Unnest and wrap collapsed columns to display
691702
exported_func_df <- exports_df %>%
692-
mutate(
693-
test_files = purrr::map_chr(.data$test_files, ~paste(.x, collapse = "\n"))
703+
dplyr::mutate(
704+
dplyr::across(
705+
all_of(c(entry_name, "code_file", "documentation")),
706+
function(col){
707+
purrr::map_chr(col, function(cell){
708+
# Replace NA values with empty strings for display purposes
709+
cell[is.na(cell)] <- ""
710+
# Wrap text
711+
if(isTRUE(wrap_cols)){
712+
cell <- wrap_text(cell, width = 24, indent = TRUE, strict = TRUE)
713+
}
714+
paste(cell, collapse = "\n")
715+
})
716+
}
717+
),
718+
# Tests can be longer due to page width (pg_width) settings (we make it wider)
719+
test_files = purrr::map_chr(.data$test_files, function(tests){
720+
# Replace NA values with empty strings for display purposes
721+
tests[is.na(tests)] <- ""
722+
# Wrap text
723+
if(isTRUE(wrap_cols)){
724+
# flextable will make its own new line at rendering at 35 characters
725+
# given the current column widths. A width of 34 is therefore the largest
726+
# we can allow if indents are desired (they wont get triggered by flextable's auto newlines)
727+
tests <- wrap_text(tests, width = 34, indent = TRUE, strict = TRUE)
728+
}
729+
paste(tests, collapse = "\n")
730+
})
694731
)
695732

696733
# Get testing directories for caption
@@ -703,40 +740,13 @@ format_traceability_matrix <- function(
703740
test_dirs <- NULL
704741
}
705742

706-
if ("exported_function" %in% names(exported_func_df)) {
707-
# Align internal scoring with external format.
708-
exported_func_df <- dplyr::rename(exported_func_df,
709-
entrypoint = "exported_function"
710-
)
711-
}
712-
713-
entry_name <- switch(scorecard_type,
714-
"R" = "Exported Function",
715-
"cli" = "Command",
716-
"Entry Point"
717-
)
718-
exported_func_df <- dplyr::rename(
719-
exported_func_df,
720-
!!entry_name := "entrypoint"
721-
)
722-
723-
# Format Table
724-
if(isTRUE(wrap_cols)){
725-
exported_func_df <- exported_func_df %>%
726-
dplyr::mutate(
727-
dplyr::across(
728-
all_of(c(entry_name, "code_file", "documentation")),
729-
function(x) wrap_text(x, width = 24, indent = TRUE, strict = TRUE)
730-
),
731-
# Tests can be longer due to page width (pg_width) settings (we make it wider)
732-
test_files = purrr::map_chr(.data$test_files, function(tests){
733-
wrap_text(tests, width = 40, strict = TRUE)
734-
})
735-
)
736-
}
737-
exported_func_df <- exported_func_df %>% format_colnames_to_title()
743+
# For exports that span more than a page (usually due to many tests), split
744+
# the entry into `export` and `export (cont.)`. This is necessary to ensure
745+
# tables do not overflow into the footer
746+
exported_func_df <- split_long_rows(exported_func_df, n = 40)
738747

739748
# Create flextable
749+
exported_func_df <- exported_func_df %>% format_colnames_to_title()
740750
exported_func_flex <- flextable_formatted(exported_func_df, pg_width = 7, font_size = 9) %>%
741751
flextable::set_caption("Traceability Matrix")
742752

@@ -757,6 +767,100 @@ format_traceability_matrix <- function(
757767
}
758768
}
759769

770+
771+
#' Split Long Rows in a Data Frame by Newline Count
772+
#'
773+
#' This function processes a data frame to split rows where the content in
774+
#' specific columns exceeds a specified number of newline characters.
775+
#' Rows with content that spans more than `n` lines will be split into
776+
#' multiple rows, with the excess content carried over into continuation
777+
#' rows. The new rows will be labeled with "(cont.)" to indicate continuation.
778+
#'
779+
#' @param exported_func_df A data frame containing columns `code_file`,
780+
#' `documentation`, and `test_files`, where the contents are character strings
781+
#' that may span multiple lines.
782+
#' @param n An integer specifying the maximum number of newline characters
783+
#' allowed in each row's content. Default is 40.
784+
#'
785+
#' @return A data frame with rows split based on newline character count.
786+
#' Each original row that exceeds the specified number of lines will be split
787+
#' into multiple rows with continuation labels.
788+
#'
789+
#' @note This function assumes the `entry_name`, a column determined early on in
790+
#' `format_traceability_matrix`, is the first column in the dataframe.
791+
#' @examples
792+
#' \dontrun{
793+
#'
794+
#' split_df <- split_long_rows(exported_func_df)
795+
#' }
796+
#' @keywords internal
797+
split_long_rows <- function(exported_func_df, n = 40) {
798+
799+
# Helper function to split content by newlines
800+
split_by_newlines <- function(content, n) {
801+
content_split <- strsplit(content, "\n")[[1]]
802+
split_list <- split(content_split, ceiling(seq_along(content_split) / n))
803+
lapply(split_list, paste, collapse = "\n")
804+
}
805+
806+
entry_name <- names(exported_func_df)[1]
807+
temp_cols <- c("code_file_split", "documentation_split", "test_files_split", "n_lines")
808+
809+
# Split contents and create new rows
810+
exported_func_df %>%
811+
dplyr::mutate(
812+
# Split contents if n_lines > n
813+
code_file_split = purrr::map(.data$code_file, ~ split_by_newlines(.x, n = n)),
814+
documentation_split = purrr::map(.data$documentation, ~ split_by_newlines(.x, n = n)),
815+
test_files_split = purrr::map(.data$test_files, ~ split_by_newlines(.x, n = n))
816+
) %>%
817+
dplyr::rowwise() %>%
818+
# Expand each row if any of the splits are greater than 1
819+
dplyr::group_split() %>%
820+
purrr::map_dfr(function(row_data) {
821+
n_chunks <- max(length(row_data$code_file_split[[1]]),
822+
length(row_data$documentation_split[[1]]),
823+
length(row_data$test_files_split[[1]]))
824+
825+
# Create a list of new rows
826+
purrr::map_dfr(1:n_chunks, function(i) {
827+
new_row <- row_data
828+
829+
# Extract split contents or default to an empty string
830+
new_row$code_file <- ifelse(
831+
length(new_row$code_file_split[[1]]) >= i,
832+
new_row$code_file_split[[1]][[i]],
833+
""
834+
)
835+
new_row$documentation <- ifelse(
836+
length(new_row$documentation_split[[1]]) >= i,
837+
new_row$documentation_split[[1]][[i]],
838+
""
839+
)
840+
new_row$test_files <- ifelse(
841+
length(new_row$test_files_split[[1]]) >= i,
842+
new_row$test_files_split[[1]][[i]],
843+
""
844+
)
845+
846+
# Label the continuation rows
847+
new_row[[entry_name]] <- ifelse(i == 1, new_row[[entry_name]], paste0(new_row[[entry_name]], " (cont.)"))
848+
849+
# Recalculate the n_lines for the new row
850+
new_row$n_lines <- max(
851+
length(strsplit(new_row$code_file, "\n")[[1]]),
852+
length(strsplit(new_row$documentation, "\n")[[1]]),
853+
length(strsplit(new_row$test_files, "\n")[[1]])
854+
)
855+
856+
new_row
857+
})
858+
}) %>%
859+
dplyr::ungroup() %>%
860+
dplyr::select(-all_of(temp_cols))
861+
}
862+
863+
760864
#' Print boiler plate text about the traceability matrix
761865
#' @inheritParams format_traceability_matrix
762866
#' @noRd
@@ -798,7 +902,7 @@ format_appendix <- function(extra_notes_data, return_vals = FALSE, scorecard_typ
798902
cov_results_df <- cov_results_df %>%
799903
dplyr::mutate(
800904
code_file = wrap_text(.data$code_file,
801-
width = 43, indent = TRUE, strict = TRUE
905+
width = 43, indent = TRUE, strict = TRUE
802906
),
803907
test_coverage = sprintf("%.2f%%", .data$test_coverage)
804908
) %>%

0 commit comments

Comments
 (0)