From 971eca811a986e6b81ca0be2ba13f849abffe476 Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Wed, 22 Feb 2023 12:28:17 -0600 Subject: [PATCH 01/22] add log reading functions --- R/read_log_file.R | 251 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 251 insertions(+) create mode 100644 R/read_log_file.R diff --git a/R/read_log_file.R b/R/read_log_file.R new file mode 100644 index 0000000..2b5aceb --- /dev/null +++ b/R/read_log_file.R @@ -0,0 +1,251 @@ +#' Reformat subsections in log lines +#' +#' @param log_txt vector object with log text lines +#' +#' @importFrom stringr str_detect +#' @importFrom stringr str_count +#' @importFrom stringr str_remove +#' +#' @return tibble that ensures formatted subsections +#' +#' @examples +#' \dontrun{ +#' reformat_subsections(readlines(log_file_path)) +#' } +#' +#' @noRd +#' +reformat_subsections <- function(log_txt) { + adj_log_txt <- c() + for (i in log_txt) { + adj_tf <- stringr::str_detect( + i, + "Errors:|Warnings:|Messages:|Output:|Result:" + ) + if (adj_tf) { + nrem <- stringr::str_count(i) + i <- stringr::str_remove(i, ":") + i <- + paste("-", i, paste(rep("-", 54 - nrem), collapse = ""), + collapse = "") + } + adj_log_txt <- c(adj_log_txt, i) + } + return(adj_log_txt) +} + +#' Nest sections in log lines vector +#' +#' @param adj_log_txt vector object with formatted log text lines +#' +#' @importFrom stringr str_remove_all +#' +#' @return list that includes nested log sections +#' +#' @noRd +#' +nest_sections <- function(adj_log_txt) { + sect_headers <- c() + sect_status <- FALSE + sect_info <- list() + for (i in adj_log_txt) { + if (i == paste(rep("-", 80), collapse = "")) { + sect_status <- !sect_status + } else if (sect_status == TRUE) { + sect_headers <- c(sect_headers, i) + } else { + cur_pos <- length(sect_headers) + if (length(sect_info) == cur_pos) { + sect_info[[cur_pos]] <- c(sect_info[[cur_pos]], i) + } else { + sect_info[[cur_pos]] <- i + } + } + } + sect_headers <- + stringr::str_remove_all(sect_headers, "-?\\s{3,}-?") + names(sect_info) <- sect_headers + + return(sect_info) +} + +#' Nest subsections in log lines vector +#' +#' @param adj_log_txt vector object with formatted log text lines +#' @param sect_info vector with nested sections (from `nest_sections()`) +#' +#' @importFrom stringr str_extract +#' @importFrom stringr str_trim +#' @importFrom stringr str_remove_all +#' +#' @return list that includes nested log subsections +#' +#' @noRd +#' +nest_subsections <- function(adj_log_txt, sect_info) { + subsect_headers <- na.omit( + stringr::str_extract(adj_log_txt, "\\-\\s\\w+\\s(\\w+\\s)?\\-{3,70}") + ) + subset_sections <- function(section) { + subsect_status <- FALSE + subsect_info <- list() + for (i in section) { + if (i %in% subsect_headers) { + latest_subsect <- stringr::str_trim( + stringr::str_remove_all(i, "\\-") + ) + subsect_status <- TRUE + } else if (subsect_status) { + subsect_info[[latest_subsect]] <- + c(subsect_info[[latest_subsect]], i) + } else { + subsect_info <- c(subsect_info, i) + } + } + subsect_info + } + nested_log <- lapply(sect_info, subset_sections) + return(nested_log) +} + +#' Nest sections and subsections in log lines vector +#' +#' @param adj_log_txt vector object with formatted log text lines +#' +#' @return list that includes nested log sections and subsections +#' +#' @noRd +#' +nest_log <- function(adj_log_txt) { + nest_subsections(adj_log_txt, + nest_sections(adj_log_txt)) +} + +#' Parse nested log list to tibbles for object where appropriate +#' +#' @param nested_log nested log output (from `nest_log()`) +#' +#' @importFrom tibble tibble +#' @importFrom tidyr separate +#' @importFrom stringr str_replace_all +#' @importFrom dplyr rename_with +#' @importFrom readr read_table +#' @importFrom dplyr mutate +#' +#' @return list with objects coerced as tibbles +#' +#' @noRd +#' +parse_log <- function(nested_log) { + parsed_log <- list() + + if ("logrx Metadata" %in% names(nested_log)) { + parsed_log$`logrx Metadata` <- + nested_log$`logrx Metadata` %>% + unlist() %>% + tibble::tibble() %>% + tidyr::separate(".", + sep = "\\: ", + into = c("Variable", "Value")) + } + + if ("User and File Information" %in% names(nested_log)) { + parsed_log$`User and File Information` <- + nested_log$`User and File Information` %>% + unlist() %>% + tibble::tibble() %>% + tidyr::separate(".", + sep = "\\: ", + into = c("Variable", "Value")) + } + + if ("Session Information" %in% names(nested_log)) { + parsed_log$`Session Information`$`Session info` <- + nested_log$`Session Information`$`Session info` %>% + readr::read_table() + + parsed_log$`Session Information`$`Packages` <- + nested_log$`Session Information`$`Packages` %>% + stringr::str_replace_all("\\*", " ") %>% + readr::read_table(skip = 1, col_names = FALSE) %>% + dplyr::rename_with(~ c( + "package", + "version", + "date", + "lib", + "source", + "lang", + "r_version" + )) %>% + dplyr::mutate( + lang = stringr::str_remove(lang, "\\("), + r_version = stringr::str_remove(r_version, "\\)") + ) + + parsed_log$`Session Information`$`External software` <- + nested_log$`Session Information`$`External software` %>% + readr::read_table() + } + + if ("Masked Functions" %in% names(nested_log)) { + parsed_log$`Masked Functions` <- + nested_log$`Masked Functions` %>% + unlist() %>% + tibble::tibble("Masked Functions" = .) + + } + + if ("Used Package and Functions" %in% names(nested_log)) { + parsed_log$`Used Package and Functions` <- + nested_log$`Used Package and Functions` %>% + unlist() %>% + tibble::tibble() %>% + tidyr::separate(".", + sep = "\\} ", + into = c("library", "function_names")) %>% + dplyr::mutate(library = stringr::str_remove(library, "\\{")) + } + + if ("Program Run Time Information" %in% names(nested_log)) { + parsed_log$`Program Run Time Information` <- + nested_log$`Program Run Time Information` %>% + unlist() %>% + tibble::tibble() %>% + tidyr::separate(".", + sep = "\\: ", + into = c("Variable", "Value")) + } + + if ("Log Output File" %in% names(nested_log)) { + parsed_log$`Log Output File` <- + nested_log$`Log Output File` %>% + unlist() %>% + tibble::tibble() %>% + tidyr::separate(".", + sep = "\\: ", + into = c("Variable", "Value")) + } + + return(parsed_log) +} + +#' Read and parse previous logrx file +#' +#' @param file character path to a logrx file +#' +#' @return tibble that includes nested and parsed content +#' +#' @examples +#' \dontrun{ +#' read_log_file(previous_log_filepath) +#' } +#' +#' @noRd +#' +read_log_file <- function(file) { + parsed_log <- readLines(file) %>% + reformat_subsections() %>% + nest_log() %>% + parse_log() + return(parsed_log) +} From 4c7b760dfbc9bd178089d6f6d69edcfd68795b42 Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Wed, 1 Mar 2023 16:10:58 -0600 Subject: [PATCH 02/22] add readr to DESCRIPTION file --- DESCRIPTION | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 9eed103..0221fd3 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -50,7 +50,8 @@ Imports: waiter, tibble, digest, - lintr + lintr, + readr Suggests: testthat (>= 3.0.0), knitr, From f7684d38d38d0a56508a269f573fe90072c3c1e7 Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Wed, 1 Mar 2023 16:11:47 -0600 Subject: [PATCH 03/22] update documentation and style for tidy --- NAMESPACE | 8 + R/read_log_file.R | 361 +++++++++++++++++++++++-------------------- man/read_log_file.Rd | 23 +++ 3 files changed, 228 insertions(+), 164 deletions(-) create mode 100644 man/read_log_file.Rd diff --git a/NAMESPACE b/NAMESPACE index bc32ecc..6ebcae0 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -16,6 +16,7 @@ importFrom(dplyr,distinct) importFrom(dplyr,filter) importFrom(dplyr,group_by) importFrom(dplyr,mutate) +importFrom(dplyr,rename_with) importFrom(dplyr,select) importFrom(dplyr,ungroup) importFrom(lintr,lint) @@ -32,6 +33,7 @@ importFrom(purrr,map2_dfr) importFrom(purrr,map_chr) importFrom(purrr,safely) importFrom(purrr,set_names) +importFrom(readr,read_table) importFrom(rlang,.data) importFrom(rstudioapi,selectDirectory) importFrom(rstudioapi,selectFile) @@ -52,13 +54,19 @@ importFrom(shiny,uiOutput) importFrom(stats,aggregate) importFrom(stringi,stri_wrap) importFrom(stringr,str_c) +importFrom(stringr,str_count) +importFrom(stringr,str_detect) +importFrom(stringr,str_extract) +importFrom(stringr,str_remove) importFrom(stringr,str_remove_all) importFrom(stringr,str_replace) importFrom(stringr,str_replace_all) importFrom(stringr,str_starts) +importFrom(stringr,str_trim) importFrom(tibble,tibble) importFrom(tidyr,complete) importFrom(tidyr,pivot_wider) +importFrom(tidyr,separate) importFrom(utils,capture.output) importFrom(utils,getParseData) importFrom(utils,lsf.str) diff --git a/R/read_log_file.R b/R/read_log_file.R index 2b5aceb..0fa2810 100644 --- a/R/read_log_file.R +++ b/R/read_log_file.R @@ -1,6 +1,6 @@ #' Reformat subsections in log lines #' -#' @param log_txt vector object with log text lines +#' @param log_txt String vector. Object with log text lines #' #' @importFrom stringr str_detect #' @importFrom stringr str_count @@ -16,27 +16,28 @@ #' @noRd #' reformat_subsections <- function(log_txt) { - adj_log_txt <- c() - for (i in log_txt) { - adj_tf <- stringr::str_detect( - i, - "Errors:|Warnings:|Messages:|Output:|Result:" - ) - if (adj_tf) { - nrem <- stringr::str_count(i) - i <- stringr::str_remove(i, ":") - i <- - paste("-", i, paste(rep("-", 54 - nrem), collapse = ""), - collapse = "") - } - adj_log_txt <- c(adj_log_txt, i) - } - return(adj_log_txt) + adj_log_txt <- c() + for (i in log_txt) { + adj_tf <- stringr::str_detect( + i, + "Errors:|Warnings:|Messages:|Output:|Result:" + ) + if (adj_tf) { + nrem <- stringr::str_count(i) + i <- stringr::str_remove(i, ":") + i <- + paste("-", i, paste(rep("-", 54 - nrem), collapse = ""), + collapse = "" + ) + } + adj_log_txt <- c(adj_log_txt, i) + } + return(adj_log_txt) } #' Nest sections in log lines vector #' -#' @param adj_log_txt vector object with formatted log text lines +#' @param adj_log_txt String vector. Object with formatted log text lines #' #' @importFrom stringr str_remove_all #' @@ -45,34 +46,34 @@ reformat_subsections <- function(log_txt) { #' @noRd #' nest_sections <- function(adj_log_txt) { - sect_headers <- c() - sect_status <- FALSE - sect_info <- list() - for (i in adj_log_txt) { - if (i == paste(rep("-", 80), collapse = "")) { - sect_status <- !sect_status - } else if (sect_status == TRUE) { - sect_headers <- c(sect_headers, i) + sect_headers <- c() + sect_status <- FALSE + sect_info <- list() + for (i in adj_log_txt) { + if (i == paste(rep("-", 80), collapse = "")) { + sect_status <- !sect_status + } else if (sect_status == TRUE) { + sect_headers <- c(sect_headers, i) + } else { + cur_pos <- length(sect_headers) + if (length(sect_info) == cur_pos) { + sect_info[[cur_pos]] <- c(sect_info[[cur_pos]], i) } else { - cur_pos <- length(sect_headers) - if (length(sect_info) == cur_pos) { - sect_info[[cur_pos]] <- c(sect_info[[cur_pos]], i) - } else { - sect_info[[cur_pos]] <- i - } + sect_info[[cur_pos]] <- i } - } - sect_headers <- - stringr::str_remove_all(sect_headers, "-?\\s{3,}-?") - names(sect_info) <- sect_headers + } + } + sect_headers <- + stringr::str_remove_all(sect_headers, "-?\\s{3,}-?") + names(sect_info) <- sect_headers - return(sect_info) + return(sect_info) } #' Nest subsections in log lines vector #' -#' @param adj_log_txt vector object with formatted log text lines -#' @param sect_info vector with nested sections (from `nest_sections()`) +#' @param adj_log_txt String vector. Object with formatted log text lines +#' @param sect_info String vector. Object with nested sections #' #' @importFrom stringr str_extract #' @importFrom stringr str_trim @@ -83,47 +84,50 @@ nest_sections <- function(adj_log_txt) { #' @noRd #' nest_subsections <- function(adj_log_txt, sect_info) { - subsect_headers <- na.omit( - stringr::str_extract(adj_log_txt, "\\-\\s\\w+\\s(\\w+\\s)?\\-{3,70}") - ) - subset_sections <- function(section) { - subsect_status <- FALSE - subsect_info <- list() - for (i in section) { - if (i %in% subsect_headers) { - latest_subsect <- stringr::str_trim( - stringr::str_remove_all(i, "\\-") - ) - subsect_status <- TRUE - } else if (subsect_status) { - subsect_info[[latest_subsect]] <- - c(subsect_info[[latest_subsect]], i) - } else { - subsect_info <- c(subsect_info, i) - } + subsect_headers <- na.omit( + stringr::str_extract(adj_log_txt, "\\-\\s\\w+\\s(\\w+\\s)?\\-{3,70}") + ) + subset_sections <- function(section) { + subsect_status <- FALSE + subsect_info <- list() + for (i in section) { + if (i %in% subsect_headers) { + latest_subsect <- stringr::str_trim( + stringr::str_remove_all(i, "\\-") + ) + subsect_status <- TRUE + } else if (subsect_status) { + subsect_info[[latest_subsect]] <- + c(subsect_info[[latest_subsect]], i) + } else { + subsect_info <- c(subsect_info, i) } - subsect_info - } - nested_log <- lapply(sect_info, subset_sections) - return(nested_log) + } + subsect_info + } + nested_log <- lapply(sect_info, subset_sections) + return(nested_log) } #' Nest sections and subsections in log lines vector #' -#' @param adj_log_txt vector object with formatted log text lines +#' @param adj_log_txt String vector. Object with formatted log text lines #' #' @return list that includes nested log sections and subsections #' #' @noRd #' nest_log <- function(adj_log_txt) { - nest_subsections(adj_log_txt, - nest_sections(adj_log_txt)) + nest_subsections( + adj_log_txt, + nest_sections(adj_log_txt) + ) } #' Parse nested log list to tibbles for object where appropriate #' -#' @param nested_log nested log output (from `nest_log()`) +#' @param nested_log String vector. +#' Object with nested log output (from `nest_log()`) #' #' @importFrom tibble tibble #' @importFrom tidyr separate @@ -137,115 +141,144 @@ nest_log <- function(adj_log_txt) { #' @noRd #' parse_log <- function(nested_log) { - parsed_log <- list() - - if ("logrx Metadata" %in% names(nested_log)) { - parsed_log$`logrx Metadata` <- - nested_log$`logrx Metadata` %>% - unlist() %>% - tibble::tibble() %>% - tidyr::separate(".", - sep = "\\: ", - into = c("Variable", "Value")) - } - - if ("User and File Information" %in% names(nested_log)) { - parsed_log$`User and File Information` <- - nested_log$`User and File Information` %>% - unlist() %>% - tibble::tibble() %>% - tidyr::separate(".", - sep = "\\: ", - into = c("Variable", "Value")) - } - - if ("Session Information" %in% names(nested_log)) { - parsed_log$`Session Information`$`Session info` <- - nested_log$`Session Information`$`Session info` %>% - readr::read_table() - - parsed_log$`Session Information`$`Packages` <- - nested_log$`Session Information`$`Packages` %>% - stringr::str_replace_all("\\*", " ") %>% - readr::read_table(skip = 1, col_names = FALSE) %>% - dplyr::rename_with(~ c( - "package", - "version", - "date", - "lib", - "source", - "lang", - "r_version" - )) %>% - dplyr::mutate( - lang = stringr::str_remove(lang, "\\("), - r_version = stringr::str_remove(r_version, "\\)") - ) - - parsed_log$`Session Information`$`External software` <- - nested_log$`Session Information`$`External software` %>% - readr::read_table() - } - - if ("Masked Functions" %in% names(nested_log)) { - parsed_log$`Masked Functions` <- - nested_log$`Masked Functions` %>% - unlist() %>% - tibble::tibble("Masked Functions" = .) - - } - - if ("Used Package and Functions" %in% names(nested_log)) { - parsed_log$`Used Package and Functions` <- - nested_log$`Used Package and Functions` %>% - unlist() %>% - tibble::tibble() %>% - tidyr::separate(".", - sep = "\\} ", - into = c("library", "function_names")) %>% - dplyr::mutate(library = stringr::str_remove(library, "\\{")) - } - - if ("Program Run Time Information" %in% names(nested_log)) { - parsed_log$`Program Run Time Information` <- - nested_log$`Program Run Time Information` %>% - unlist() %>% - tibble::tibble() %>% - tidyr::separate(".", - sep = "\\: ", - into = c("Variable", "Value")) - } - - if ("Log Output File" %in% names(nested_log)) { - parsed_log$`Log Output File` <- - nested_log$`Log Output File` %>% - unlist() %>% - tibble::tibble() %>% - tidyr::separate(".", - sep = "\\: ", - into = c("Variable", "Value")) - } - - return(parsed_log) + parsed_log <- list() + + if ("logrx Metadata" %in% names(nested_log)) { + parsed_log$`logrx Metadata` <- + nested_log$`logrx Metadata` %>% + unlist() %>% + tibble::tibble() %>% + tidyr::separate(".", + sep = "\\: ", + into = c("Variable", "Value"), + extra = "merge" + ) + } + + if ("User and File Information" %in% names(nested_log)) { + parsed_log$`User and File Information` <- + nested_log$`User and File Information` %>% + unlist() %>% + stringr::str_trim() %>% + tibble::tibble() %>% + tidyr::separate(".", + sep = "\\: ", + into = c("Variable", "Value") + ) + } + + if ("Session Information" %in% names(nested_log)) { + parsed_log$`Session Information`$`Session info` <- + nested_log$`Session Information`$`Session info` %>% + unlist() %>% + stringr::str_trim() %>% + tibble::tibble() %>% + tidyr::separate(".", + sep = "\\s", + into = c("setting", "value"), + extra = "merge", + ) %>% + mutate(across(is.character, stringr::str_trim)) + + parsed_log$`Session Information`$`Packages` <- + nested_log$`Session Information`$`Packages` %>% + # remove indicator whether the package is attached to the search path + stringr::str_replace_all("\\*", " ") %>% + # account for loaded packages due to load_all() + stringr::str_replace_all(" P ", " ") %>% + readr::read_table(skip = 1, col_names = FALSE) %>% + dplyr::rename_with(~ c( + "package", + "version", + "date", + "lib", + "source", + "lang", + "r_version" + )) %>% + dplyr::mutate( + lang = stringr::str_remove(lang, "\\("), + r_version = stringr::str_remove(r_version, "\\)") + ) + + parsed_log$`Session Information`$`External software` <- + nested_log$`Session Information`$`External software` %>% + stringr::str_trim() %>% + tibble::tibble() %>% + tidyr::separate(".", + sep = "\\s", + into = c("setting", "value"), + extra = "merge", + ) %>% + mutate(across(where(is.character), stringr::str_trim)) + } + + if ("Masked Functions" %in% names(nested_log)) { + parsed_log$`Masked Functions` <- + nested_log$`Masked Functions` %>% + unlist() %>% + tibble::tibble("Masked Functions" = .) + } + + if ("Used Package and Functions" %in% names(nested_log)) { + parsed_log$`Used Package and Functions` <- + nested_log$`Used Package and Functions` %>% + unlist() %>% + tibble::tibble() %>% + tidyr::separate(".", + sep = "\\} ", + into = c("library", "function_names") + ) %>% + dplyr::mutate(library = stringr::str_remove(library, "\\{")) + } + + if ("Program Run Time Information" %in% names(nested_log)) { + parsed_log$`Program Run Time Information` <- + nested_log$`Program Run Time Information` %>% + unlist() %>% + tibble::tibble() %>% + tidyr::separate(".", + sep = "\\: ", + into = c("Variable", "Value") + ) + } + + if ("Log Output File" %in% names(nested_log)) { + parsed_log$`Log Output File` <- + nested_log$`Log Output File` %>% + unlist() %>% + tibble::tibble() %>% + tidyr::separate(".", + sep = "\\: ", + into = c("Variable", "Value") + ) + } + + return(parsed_log) } #' Read and parse previous logrx file #' -#' @param file character path to a logrx file +#' @param file String. Path to a logrx log file #' -#' @return tibble that includes nested and parsed content +#' @return Tibble. Object that includes nested and parsed content #' #' @examples #' \dontrun{ #' read_log_file(previous_log_filepath) #' } #' -#' @noRd -#' read_log_file <- function(file) { - parsed_log <- readLines(file) %>% - reformat_subsections() %>% - nest_log() %>% - parse_log() - return(parsed_log) + if (!file.exists(file)) { + stop("Path does not exist:", sQuote(file)) + } + con <- file(file.path(file), "r") + flines <- readLines(con) + close(con) + + parsed_log <- flines %>% + reformat_subsections() %>% + nest_log() %>% + parse_log() + return(parsed_log) } diff --git a/man/read_log_file.Rd b/man/read_log_file.Rd new file mode 100644 index 0000000..5c66c7e --- /dev/null +++ b/man/read_log_file.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/read_log_file.R +\name{read_log_file} +\alias{read_log_file} +\title{Read and parse previous logrx file} +\usage{ +read_log_file(file) +} +\arguments{ +\item{file}{String. Path to a logrx log file} +} +\value{ +Tibble. Object that includes nested and parsed content +} +\description{ +Read and parse previous logrx file +} +\examples{ +\dontrun{ +read_log_file(previous_log_filepath) +} + +} From 8d79eec8fd17459601c74aa84c3c87668c12fe9b Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Wed, 1 Mar 2023 16:12:00 -0600 Subject: [PATCH 04/22] add unit test --- tests/testthat/test-parse.R | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 tests/testthat/test-parse.R diff --git a/tests/testthat/test-parse.R b/tests/testthat/test-parse.R new file mode 100644 index 0000000..5781566 --- /dev/null +++ b/tests/testthat/test-parse.R @@ -0,0 +1,44 @@ +test_that("read_log_file will parse a logrx log file and create the necessary object", { + options("log.rx" = NULL) + scriptPath <- tempfile() + logDir <- tempdir() + writeLines("print('hello logrx')", con = scriptPath) + + # check no log is currently written out + filePath <- file.path(logDir, "log_out_parse") + expect_warning(expect_error(file(filePath, "r"), "cannot open the connection")) + + axecute(scriptPath, log_name = "log_out_parse", log_path = logDir, remove_log_object = FALSE) + + # check that the log file can be parsed + parsedFile <- read_log_file(filePath) + + expect_length(parsedFile, 7) + expect_named( + parsedFile, + c( + "logrx Metadata", + "User and File Information", + "Session Information", + "Masked Functions", + "Used Package and Functions", + "Program Run Time Information", + "Log Output File" + ) + ) + expect_true(all(sapply( + parsedFile[names(parsedFile) != "Session Information"], + is.data.frame + ))) + + expect_true( + all(sapply( + parsedFile[names(parsedFile) != "Session Information"], + nrow + ) > 0) + ) + + # remove all the stuff we added + rm(scriptPath, logDir, parsedFile) + log_remove() +}) From 10d74dc16e606d2c61631b3eb04e9589b8716e31 Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Wed, 1 Mar 2023 16:17:57 -0600 Subject: [PATCH 05/22] Update pkgdown site yml --- _pkgdown.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/_pkgdown.yml b/_pkgdown.yml index 39a1662..46e76eb 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -22,6 +22,10 @@ reference: - write_log_header - write_unapproved_functions - write_used_functions +- title: Read Previous Log + desc: Functionality to Read Previous Log Files +- contents: + - read_log_file - title: Utilities desc: Utility functions - contents: From dd182811c2ea5dee56fe7aeb9194807403c22bcf Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Wed, 1 Mar 2023 16:18:19 -0600 Subject: [PATCH 06/22] Update newsmd with new read_log_file --- NEWS.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS.md b/NEWS.md index 46a8d26..6132e52 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# logrx 0.2.2 + +- Add `read_log_file()` to read previous logrx log file + # logrx 0.2.1 - non-function objects are no longer returned as functions by `get_used_functions` (#154) From 1c63ea3a0b724ac3056f8173630dc3f076a5c678 Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Tue, 7 Mar 2023 15:07:20 -0500 Subject: [PATCH 07/22] Wrap predicate function in `where()` --- R/read_log_file.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/read_log_file.R b/R/read_log_file.R index 0fa2810..77705cd 100644 --- a/R/read_log_file.R +++ b/R/read_log_file.R @@ -178,7 +178,7 @@ parse_log <- function(nested_log) { into = c("setting", "value"), extra = "merge", ) %>% - mutate(across(is.character, stringr::str_trim)) + mutate(across(where(is.character), stringr::str_trim)) parsed_log$`Session Information`$`Packages` <- nested_log$`Session Information`$`Packages` %>% From 6b86210bd77fb7f136b017a81021eb20d0d692a4 Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Tue, 14 Mar 2023 10:10:56 -0400 Subject: [PATCH 08/22] default parsed log to already nested log --- R/read_log_file.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/read_log_file.R b/R/read_log_file.R index 0fa2810..4a3a629 100644 --- a/R/read_log_file.R +++ b/R/read_log_file.R @@ -141,7 +141,7 @@ nest_log <- function(adj_log_txt) { #' @noRd #' parse_log <- function(nested_log) { - parsed_log <- list() + parsed_log <- nested_log if ("logrx Metadata" %in% names(nested_log)) { parsed_log$`logrx Metadata` <- From c25ee9b6a50f84fe40c1d273d08fd1102cfb90b5 Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Mon, 24 Jul 2023 11:38:35 -0500 Subject: [PATCH 09/22] resolve test-parse error --- tests/testthat/test-parse.R | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/testthat/test-parse.R b/tests/testthat/test-parse.R index 5781566..528a4b1 100644 --- a/tests/testthat/test-parse.R +++ b/tests/testthat/test-parse.R @@ -13,7 +13,7 @@ test_that("read_log_file will parse a logrx log file and create the necessary ob # check that the log file can be parsed parsedFile <- read_log_file(filePath) - expect_length(parsedFile, 7) + expect_length(parsedFile, 9) expect_named( parsedFile, c( @@ -23,17 +23,25 @@ test_that("read_log_file will parse a logrx log file and create the necessary ob "Masked Functions", "Used Package and Functions", "Program Run Time Information", + "Errors and Warnings", + "Messages, Output, and Result", "Log Output File" ) ) expect_true(all(sapply( - parsedFile[names(parsedFile) != "Session Information"], + parsedFile[!names(parsedFile) %in% + c("Session Information", + "Messages, Output, and Result", + "Errors and Warnings")], is.data.frame ))) expect_true( all(sapply( - parsedFile[names(parsedFile) != "Session Information"], + parsedFile[!names(parsedFile) %in% + c("Session Information", + "Messages, Output, and Result", + "Errors and Warnings")], nrow ) > 0) ) From 0179b66c7cc0dbdea83d75987824f8f3f1ff155f Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Mon, 21 Aug 2023 12:00:30 -0500 Subject: [PATCH 10/22] Update DESCRIPTION --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index bf8312c..a3355a0 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -51,7 +51,7 @@ Imports: tibble, digest, lintr, - readr + readr, lifecycle Suggests: testthat (>= 3.0.0), From 837c2deb54d52b2efbb228fd32c376135c07067e Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Tue, 22 Aug 2023 13:49:06 -0500 Subject: [PATCH 11/22] add methods for lifecycle condition --- DESCRIPTION | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index a3355a0..e11f9ec 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -52,7 +52,8 @@ Imports: digest, lintr, readr, - lifecycle + lifecycle, + methods Suggests: testthat (>= 3.0.0), knitr, From 84d9e2da70f6487c8583456b6dba371d63342d5b Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Tue, 22 Aug 2023 13:49:22 -0500 Subject: [PATCH 12/22] declare methods pkg --- R/axecute.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/axecute.R b/R/axecute.R index 0b2091a..40f8192 100644 --- a/R/axecute.R +++ b/R/axecute.R @@ -42,7 +42,7 @@ axecute <- function(file, log_name = NA, show_repo_url = FALSE, ...){ # deprecations - if (hasArg(remove_log_object)) { + if (methods::hasArg(remove_log_object)) { lifecycle::deprecate_stop("0.3.0", "axecute(remove_log_object = )", "axecute(include_rds = )") } From 3158aa384ce2be807c19a98e970822f2629ab49a Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Tue, 22 Aug 2023 13:49:43 -0500 Subject: [PATCH 13/22] declare missing pkgs for funcs --- R/read_log_file.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/read_log_file.R b/R/read_log_file.R index 4cb1777..35d1b36 100644 --- a/R/read_log_file.R +++ b/R/read_log_file.R @@ -84,7 +84,7 @@ nest_sections <- function(adj_log_txt) { #' @noRd #' nest_subsections <- function(adj_log_txt, sect_info) { - subsect_headers <- na.omit( + subsect_headers <- stats::na.omit( stringr::str_extract(adj_log_txt, "\\-\\s\\w+\\s(\\w+\\s)?\\-{3,70}") ) subset_sections <- function(section) { @@ -178,7 +178,7 @@ parse_log <- function(nested_log) { into = c("setting", "value"), extra = "merge", ) %>% - mutate(across(where(is.character), stringr::str_trim)) + dplyr::mutate(dplyr::across(dplyr::where(is.character), stringr::str_trim)) parsed_log$`Session Information`$`Packages` <- nested_log$`Session Information`$`Packages` %>% @@ -210,7 +210,7 @@ parse_log <- function(nested_log) { into = c("setting", "value"), extra = "merge", ) %>% - mutate(across(where(is.character), stringr::str_trim)) + dplyr::mutate(dplyr::across(dplyr::where(is.character), stringr::str_trim)) } if ("Masked Functions" %in% names(nested_log)) { From b101ea9174b04e67c91dd638703e8416fc6c00f5 Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Tue, 22 Aug 2023 13:50:04 -0500 Subject: [PATCH 14/22] fix test to not include remove_log_object arg --- tests/testthat/test-parse.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/testthat/test-parse.R b/tests/testthat/test-parse.R index 528a4b1..df2d4fd 100644 --- a/tests/testthat/test-parse.R +++ b/tests/testthat/test-parse.R @@ -8,11 +8,11 @@ test_that("read_log_file will parse a logrx log file and create the necessary ob filePath <- file.path(logDir, "log_out_parse") expect_warning(expect_error(file(filePath, "r"), "cannot open the connection")) - axecute(scriptPath, log_name = "log_out_parse", log_path = logDir, remove_log_object = FALSE) + axecute(scriptPath, log_name = "log_out_parse", log_path = logDir) # check that the log file can be parsed parsedFile <- read_log_file(filePath) - +'' expect_length(parsedFile, 9) expect_named( parsedFile, From 851f64a614929784b523768d8a36c469dd1074fe Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Fri, 8 Sep 2023 15:00:41 -0500 Subject: [PATCH 15/22] Update DESCRIPTION --- DESCRIPTION | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DESCRIPTION b/DESCRIPTION index 1ce758a..13f5be3 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -23,6 +23,10 @@ Authors@R: family = "Masel", email = "nmasel@its.jnj.com", role = "aut"), + person(given = "Sam", + family = "Parmar", + email = "samir.parmar@pfizer.com", + role = "aut"), person(given = "GSK/Atorus JPT", role = c("cph", "fnd")) ) From 163e1d18f0b2249b8480bff3d480102ea26cfb8e Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Tue, 12 Sep 2023 10:56:54 -0500 Subject: [PATCH 16/22] move readr to suggests --- DESCRIPTION | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 13f5be3..b2f937c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: logrx Title: A Logging Utility Focus on Clinical Trial Programming Workflows -Version: 0.3.0 +Version: 0.2.1 Authors@R: c( person(given = "Nathan", @@ -23,10 +23,6 @@ Authors@R: family = "Masel", email = "nmasel@its.jnj.com", role = "aut"), - person(given = "Sam", - family = "Parmar", - email = "samir.parmar@pfizer.com", - role = "aut"), person(given = "GSK/Atorus JPT", role = c("cph", "fnd")) ) @@ -39,7 +35,7 @@ LazyData: true Roxygen: list(markdown = TRUE) RoxygenNote: 7.2.3 Imports: - dplyr (>= 1.0.0), + dplyr, magrittr, purrr, rlang, @@ -54,10 +50,7 @@ Imports: waiter, tibble, digest, - lintr, - readr, - lifecycle, - methods + lintr Suggests: testthat (>= 3.0.0), knitr, @@ -67,9 +60,8 @@ Suggests: pkgdown, Tplyr, haven, - lintr, - xml2, - here + here, + readr VignetteBuilder: knitr Config/testthat/edition: 3 Depends: From 597d3cc6bdc599616bc4a70f8fc74b32ee40faca Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Tue, 12 Sep 2023 10:57:21 -0500 Subject: [PATCH 17/22] ensure readr is checked for in read_ function --- NEWS.md | 2 +- R/read_log_file.R | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 164c92b..2f36141 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,7 +5,7 @@ - Add `show_repo_url` option in `axecute()` to capture repo URL(s) into log file (#167) - Moved website theme to Bootstrap 5, enabled search (#179) - Add `include_rds` argument to `axecute()` to export log as rds file -- Add `read_log_file()` to read previous logrx log file +- Add `read_log_file()` to read previous logrx log file as optional function - Add `library_call_linter()` to ensure all library calls are at the top of the script (#163) - Remove argument for remove_log_object from `axecute()` still accessible via `log_write()` (#182) diff --git a/R/read_log_file.R b/R/read_log_file.R index 35d1b36..9b7f061 100644 --- a/R/read_log_file.R +++ b/R/read_log_file.R @@ -141,6 +141,12 @@ nest_log <- function(adj_log_txt) { #' @noRd #' parse_log <- function(nested_log) { + if (!requireNamespace("readr", quietly = TRUE)) { + warning(strwrap("Install the readr package to use log parsing feature.", + prefix = " ", initial = "")) + return(list()) + } + parsed_log <- nested_log if ("logrx Metadata" %in% names(nested_log)) { @@ -272,6 +278,13 @@ read_log_file <- function(file) { if (!file.exists(file)) { stop("Path does not exist:", sQuote(file)) } + + if (!requireNamespace("readr", quietly = TRUE)) { + warning(strwrap("Install the readr package to use log parsing feature.", + prefix = " ", initial = "")) + return(list()) + } + con <- file(file.path(file), "r") flines <- readLines(con) close(con) From e8f00b6acb2291e9debc151461088777a9e32b69 Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Tue, 12 Sep 2023 11:34:59 -0500 Subject: [PATCH 18/22] update test to check if suggest is available --- tests/testthat/test-parse.R | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/testthat/test-parse.R b/tests/testthat/test-parse.R index df2d4fd..6a33b42 100644 --- a/tests/testthat/test-parse.R +++ b/tests/testthat/test-parse.R @@ -1,4 +1,5 @@ test_that("read_log_file will parse a logrx log file and create the necessary object", { + skip_if_not_installed("readr") options("log.rx" = NULL) scriptPath <- tempfile() logDir <- tempdir() From 27f5c4b81a0e42d5cfd56783028220a4875ee186 Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Tue, 12 Sep 2023 12:10:30 -0500 Subject: [PATCH 19/22] remove readr from roxygen2 header importFrom --- NAMESPACE | 1 - R/read_log_file.R | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 6b7ba44..de6cc1b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -31,7 +31,6 @@ importFrom(purrr,map2_dfr) importFrom(purrr,map_chr) importFrom(purrr,safely) importFrom(purrr,set_names) -importFrom(readr,read_table) importFrom(rlang,.data) importFrom(rstudioapi,selectDirectory) importFrom(rstudioapi,selectFile) diff --git a/R/read_log_file.R b/R/read_log_file.R index 9b7f061..d496e5e 100644 --- a/R/read_log_file.R +++ b/R/read_log_file.R @@ -133,7 +133,6 @@ nest_log <- function(adj_log_txt) { #' @importFrom tidyr separate #' @importFrom stringr str_replace_all #' @importFrom dplyr rename_with -#' @importFrom readr read_table #' @importFrom dplyr mutate #' #' @return list with objects coerced as tibbles @@ -293,5 +292,6 @@ read_log_file <- function(file) { reformat_subsections() %>% nest_log() %>% parse_log() + return(parsed_log) } From 5893100fe01785157dbbf72a9fdfa082e7ceb80e Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Tue, 12 Sep 2023 12:10:56 -0500 Subject: [PATCH 20/22] add lifecycle and methods to imports, xml2 to suggests --- DESCRIPTION | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index b2f937c..22cc6e5 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -50,7 +50,9 @@ Imports: waiter, tibble, digest, - lintr + lintr, + lifecycle, + methods Suggests: testthat (>= 3.0.0), knitr, @@ -61,7 +63,8 @@ Suggests: Tplyr, haven, here, - readr + readr, + xml2 VignetteBuilder: knitr Config/testthat/edition: 3 Depends: From 6826254b13f9c3b6e533736cc97fc3196723252b Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Wed, 13 Sep 2023 14:43:52 -0500 Subject: [PATCH 21/22] reconcile description file --- DESCRIPTION | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 22cc6e5..1a8c11a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: logrx Title: A Logging Utility Focus on Clinical Trial Programming Workflows -Version: 0.2.1 +Version: 0.3.0 Authors@R: c( person(given = "Nathan", @@ -35,7 +35,7 @@ LazyData: true Roxygen: list(markdown = TRUE) RoxygenNote: 7.2.3 Imports: - dplyr, + dplyr (>= 1.0.0), magrittr, purrr, rlang, @@ -50,7 +50,6 @@ Imports: waiter, tibble, digest, - lintr, lifecycle, methods Suggests: @@ -62,9 +61,10 @@ Suggests: pkgdown, Tplyr, haven, + lintr, + xml2, here, - readr, - xml2 + readr VignetteBuilder: knitr Config/testthat/edition: 3 Depends: From f965531f26033fd7d16f05bfeee80ed8434186da Mon Sep 17 00:00:00 2001 From: Sam Parmar <107635309+parmsam-pfizer@users.noreply.github.com> Date: Wed, 13 Sep 2023 14:47:02 -0500 Subject: [PATCH 22/22] remove mention of 'previous' --- NEWS.md | 2 +- R/read_log_file.R | 2 +- _pkgdown.yml | 4 ++-- man/read_log_file.Rd | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/NEWS.md b/NEWS.md index 687e943..82c0b52 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,7 +5,7 @@ - Add `show_repo_url` option in `axecute()` to capture repo URL(s) into log file (#167) - Moved website theme to Bootstrap 5, enabled search (#179) - Add `include_rds` argument to `axecute()` to export log as rds file -- Add `read_log_file()` to read previous logrx log file as optional function +- Add `read_log_file()` to read logrx log file as optional function - Add `library_call_linter()` to ensure all library calls are at the top of the script (#163) - Remove argument for remove_log_object from `axecute()` still accessible via `log_write()` (#182) - Added functionality so `axecute()` works with `.Rmd` files (#140) diff --git a/R/read_log_file.R b/R/read_log_file.R index d496e5e..ca6f5fe 100644 --- a/R/read_log_file.R +++ b/R/read_log_file.R @@ -262,7 +262,7 @@ parse_log <- function(nested_log) { return(parsed_log) } -#' Read and parse previous logrx file +#' Read and parse logrx file #' #' @param file String. Path to a logrx log file #' diff --git a/_pkgdown.yml b/_pkgdown.yml index e99bf82..3352e1f 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -35,8 +35,8 @@ reference: - write_log_header - write_unapproved_functions - write_used_functions -- title: Read Previous Log - desc: Functionality to Read Previous Log Files +- title: Read Log + desc: Functionality to Read Log Files - contents: - read_log_file - title: Utilities diff --git a/man/read_log_file.Rd b/man/read_log_file.Rd index 5c66c7e..6a2d2e6 100644 --- a/man/read_log_file.Rd +++ b/man/read_log_file.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/read_log_file.R \name{read_log_file} \alias{read_log_file} -\title{Read and parse previous logrx file} +\title{Read and parse logrx file} \usage{ read_log_file(file) } @@ -13,7 +13,7 @@ read_log_file(file) Tibble. Object that includes nested and parsed content } \description{ -Read and parse previous logrx file +Read and parse logrx file } \examples{ \dontrun{