diff --git a/DESCRIPTION b/DESCRIPTION index f301a53..f065bd3 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -38,7 +38,8 @@ Suggests: knitr, testthat (>= 3.0.0), readr, - withr (>= 2.5.0) + withr (>= 2.5.0), + purrr Config/testthat/edition: 3 VignetteBuilder: knitr diff --git a/R/fileSummary.R b/R/fileSummary.R index 1486077..d284058 100644 --- a/R/fileSummary.R +++ b/R/fileSummary.R @@ -1,4 +1,4 @@ -#' Summarize revision and QC history for a file +#' Summarize revision and QC history for a file or directory #' #' @description #' Generates a comprehensive summary of the revision and quality control (QC) history @@ -8,12 +8,14 @@ #' * Historical information about previous authors #' * Historical information about previous QC reviewers #' -#' @param .file File path. +#' @param .file File or directory path. #' #' @details #' The function prints a formatted summary to the console and invisibly returns -#' the data for programmatic use. Non-existent files generate warnings and are -#' skipped. Files not found in the repository history are also skipped. +#' the data for programmatic use. If a directory is supplied, each immediate +#' file in that directory is summarized and a named list of results is returned. +#' Non-existent files generate warnings and are skipped. Files not found in the +#' repository history are also skipped. #' #' QC status can be: #' * "QC up to date" - Latest revision has been QC'd @@ -26,19 +28,34 @@ #' fileSummary("script/data-assembly/study-101.R") #' #' # Process all files in a directory -#' purrr::walk(list.files("script/data-assembly", full.names = TRUE), ~ fileSummary(.file = .x)) +#' fileSummary("script/data-assembly") #' } #' #' @export fileSummary <- function(.file) { if (length(.file) != 1) { - stop("'.file' must be a single file path") + stop("'.file' must be a single file or directory path") } if (!file.exists(.file)) { warning(paste0(.file, " does not exist")) return(invisible(NULL)) } + + if (fs::is_dir(.file)) { + files <- list.files(.file, full.names = TRUE) + files <- files[!fs::is_dir(files)] + + if (length(files) == 0) { + warning(paste0(.file, " contains no files")) + return(invisible(list())) + } + + out <- lapply(files, fileSummary_one) + names(out) <- files + + return(invisible(out)) + } log_df <- tryCatch( svnCommand(.file = .file, .command = "log"), @@ -152,3 +169,9 @@ fileSummary <- function(.file) { return(invisible(out)) } + +#' Internal helper for per-file summaries. +#' @noRd +fileSummary_one <- function(.file) { + fileSummary(.file = .file) +} diff --git a/man/fileSummary.Rd b/man/fileSummary.Rd index c946f09..b8411ba 100644 --- a/man/fileSummary.Rd +++ b/man/fileSummary.Rd @@ -2,12 +2,12 @@ % Please edit documentation in R/fileSummary.R \name{fileSummary} \alias{fileSummary} -\title{Summarize revision and QC history for a file} +\title{Summarize revision and QC history for a file or directory} \usage{ fileSummary(.file) } \arguments{ -\item{.file}{File path.} +\item{.file}{File or directory path.} } \description{ Generates a comprehensive summary of the revision and quality control (QC) history @@ -21,8 +21,10 @@ for a file. The function reports: } \details{ The function prints a formatted summary to the console and invisibly returns -the data for programmatic use. Non-existent files generate warnings and are -skipped. Files not found in the repository history are also skipped. +the data for programmatic use. If a directory is supplied, each immediate +file in that directory is summarized and a named list of results is returned. +Non-existent files generate warnings and are skipped. Files not found in the +repository history are also skipped. QC status can be: \itemize{ @@ -37,7 +39,7 @@ QC status can be: fileSummary("script/data-assembly/study-101.R") # Process all files in a directory -purrr::walk(list.files("script/data-assembly", full.names = TRUE), ~ fileSummary(.file = .x)) +fileSummary("script/data-assembly") } } diff --git a/tests/testthat/test-fileSummary.R b/tests/testthat/test-fileSummary.R index dfa0580..dfd3d1b 100644 --- a/tests/testthat/test-fileSummary.R +++ b/tests/testthat/test-fileSummary.R @@ -2,9 +2,44 @@ create_test_svn() logAccept("script/data-assembly/da-functions.R") logAssign("script/model-summary.Rmd") -test_that("Function requires single file input", { +test_that("Function requires single file or directory input", { + da_files <- c( + "script/data-assembly/da-functions.R", + "script/data-assembly/da-study-abc.Rmd" + ) + expect_error(fileSummary(da_files), "must be a single file or directory path") +}) + +test_that("Works correctly for a directory input", { + da_directory <- fileSummary("script/data-assembly") da_files <- list.files("script/data-assembly", full.names = TRUE) - expect_error(fileSummary(da_files), "must be a single file path") + da_files <- da_files[!fs::is_dir(da_files)] + + expect_true(is.list(da_directory)) + expect_setequal(names(da_directory), da_files) + expect_true(all(vapply(da_directory, is.list, logical(1)))) +}) + +test_that("Directory input matches purrr::walk-based file summaries", { + testthat::skip_if_not_installed("purrr") + + da_files <- list.files("script/data-assembly", full.names = TRUE) + da_files <- da_files[!fs::is_dir(da_files)] + + from_directory <- fileSummary("script/data-assembly") + + from_walk <- list() + purrr::walk( + da_files, + ~ { + from_walk[[.x]] <<- fileSummary(.file = .x) + } + ) + + from_directory <- from_directory[order(names(from_directory))] + from_walk <- from_walk[order(names(from_walk))] + + expect_equal(from_directory, from_walk) }) test_that("Works correctly for a single file QC filed", {