diff --git a/.github/actions/setup/action.yaml b/.github/actions/setup/action.yaml index 6bca8ef..fa4f8ba 100644 --- a/.github/actions/setup/action.yaml +++ b/.github/actions/setup/action.yaml @@ -8,5 +8,21 @@ runs: shell: bash run: | echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope + - name: Install Quarto uses: quarto-dev/quarto-actions/setup@v2 + + - name: (Linux) Install jupyter + if: runner.os == 'Linux' + shell: bash + run: python3 -m pip install jupyter + + - name: (macOS) Install jupyter + if: runner.os == 'macOS' + shell: bash + run: python3 -m pip install --break-system-packages jupyter + + - name: (Windows) Install jupyter + if: runner.os == 'Windows' + shell: bash + run: py -m pip install jupyter diff --git a/DESCRIPTION b/DESCRIPTION index 9200260..9d5b429 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: whirl Title: Logging package -Version: 0.1.5 +Version: 0.1.5.9000 Authors@R: c( person("Aksel", "Thomsen", , "oath@novonordisk.com", role = c("aut", "cre")), person("Lovemore", "Gakava", , "lvgk@novonordisk.com", role = "aut"), @@ -41,7 +41,8 @@ Imports: withr, yaml, zephyr (>= 0.0.0.9000), - glue + glue, + reticulate Suggests: testthat (>= 3.0.0), usethis diff --git a/NAMESPACE b/NAMESPACE index 83fe0d8..00e142d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -8,3 +8,4 @@ export(use_whirl) importFrom(R6,R6Class) importFrom(callr,r_session) importFrom(dplyr,.data) +importFrom(reticulate,py) diff --git a/NEWS.md b/NEWS.md index 386882e..3541918 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# whirl dev +* Added support for logging of Python scripts with `run()`. +* Improved unit tests for `run()` + # whirl 0.1.4 (2024-11-01) * Add `use_whirl()` utility function. diff --git a/R/internal_run.R b/R/internal_run.R index c73f50d..ca91738 100644 --- a/R/internal_run.R +++ b/R/internal_run.R @@ -37,11 +37,11 @@ internal_run <- function(input, steps, queue, level, internal_run(input = files, steps = steps, queue = queue, - level = level + 1) + level = level + 1, + verbosity_level = verbosity_level) } else { # Execute the scripts result <- queue$run(files) - cat("\n") } } diff --git a/R/render_summary.R b/R/render_summary.R index c27a41d..f8f4610 100644 --- a/R/render_summary.R +++ b/R/render_summary.R @@ -50,15 +50,12 @@ knit_print.whirl_summary_info <- function(x, path_rel_start, ...) { ncols <- ncol(hold) if (grepl("rstudio_cloud", Sys.getenv("R_CONFIG_ACTIVE"))) { - hold <- hold |> dplyr::mutate(formated = file.path("/file_show?path=", .data[["Hyperlink"]])) + formatted <- file.path("/file_show?path=", hold[["Hyperlink"]]) } else { - hold <- hold |> dplyr::mutate(formated = file.path(path_rel(.data[["Hyperlink"]], start = path_rel_start))) + formatted <- file.path(path_rel(hold[["Hyperlink"]], start = path_rel_start)) } - hold$Hyperlink <- paste0(sprintf('%s', hold$formated, "HTML Log")) - - hold <- hold |> - dplyr::select(-.data[["formated"]]) + hold$Hyperlink <- paste0(sprintf('%s', formatted, "HTML Log")) knitr::kable(hold, format = "html", escape = FALSE) |> kableExtra::column_spec(1:ncols, background = ifelse( diff --git a/R/run.R b/R/run.R index 2692dda..c91f5f8 100644 --- a/R/run.R +++ b/R/run.R @@ -21,20 +21,27 @@ #' @inheritParams options_params #' @return A tibble containing the execution results for all the scripts. #' -#'@examplesIf FALSE + +#'@examples +#' # Start by copying the following three example scripts: +#' file.copy( +#' from = system.file("examples", c("success.R", "warning.R", "error.R"), package = "whirl"), +#' to = "." +#' ) #' #' # Run a single script -#' script <- system.file("examples/simple/success.R", package = "whirl") -#' run(script) +#' run("success.R") #' #' # Run several scripts in parallel on up to 2 workers -#' scripts <- system.file("examples/simple", c("success.R", "warning.R", "error.R"), package = "whirl") -#' run(scripts, n_workers = 2) +#' run(c("success.R", "warning.R", "error.R"), n_workers = 2) #' -#' # Run scripts in several steps -#' step_1 <- system.file("examples/simple", c("success.R", "warning.R"), package = "whirl") -#' step_2 <- system.file("examples/simple", c("error.R"), package = "whirl") -#' run(list(step_1, step_2), n_workers = 2) +#' # Run scripts in two steps by providing them as list elements +#' run( +#' list( +#' c("success.R", "warning.R"), +#' "error.R" +#' ), +#' n_workers = 2) #' #' @export @@ -55,21 +62,31 @@ run <- function(input, approved_pkgs_url = options::opt("approved_pkgs_url", env = "whirl") # Message when initiating - d <- cli::cli_div(theme = list(rule = list( - color = "skyblue3", "line-type" = "double" - ))) - - zephyr::msg("Executing scripts and generating logs", - levels_to_write = c("verbose", "minimal"), + d <- NULL + zephyr::msg(message = "Executing scripts and generating logs", + theme = list( + rule = list(color = "skyblue3", "line-type" = "double") + ), + levels_to_write = c("verbose"), verbosity_level = verbosity_level, - msg_fun = cli::cli_rule) + msg_fun = \(message, theme, .envir) { + d <<- cli::cli_div(theme = theme, .auto_close = FALSE) + cli::cli_rule(message, .envir = .envir) + } + ) # Message when ending - on.exit(zephyr::msg("End of process", - levels_to_write = c("verbose", "minimal"), - verbosity_level = verbosity_level, - msg_fun = cli::cli_rule)) - on.exit(cli::cli_end(d), add = TRUE) + on.exit({ + zephyr::msg(message = "End of process", + div = d, + levels_to_write = c("verbose"), + verbosity_level = verbosity_level, + msg_fun = \(message, div, .envir) { + cli::cli_rule(message, .envir = .envir) + cli::cli_end(div) + } + ) + }) # Constrain the number of workers n_workers <- min(128, n_workers) @@ -96,9 +113,12 @@ run <- function(input, level = 1, verbosity_level = verbosity_level) - # Create the summary log - summary_tibble <- util_queue_summary(result$queue) - render_summary(input = summary_tibble, summary_file = summary_file) + # Create the summary log if required + if (!is.null(summary_file)) { + summary_tibble <- util_queue_summary(result$queue) + render_summary(input = summary_tibble, summary_file = summary_file) + + } invisible(result$queue) diff --git a/R/session.R b/R/session.R index 573c97e..4a5dc75 100644 --- a/R/session.R +++ b/R/session.R @@ -1,10 +1,11 @@ #' Get session info #' #' Retrieve session info and add quarto info if not already there +#' Argument to also add python version and package info #' #' @noRd -session_info <- function(approved_folder_pkgs, approved_url_pkgs) { +session_info <- function(approved_folder_pkgs = NULL, approved_url_pkgs = NULL, python_packages = NULL) { info <- sessioninfo::session_info() if (!is.null(approved_folder_pkgs) | @@ -30,7 +31,7 @@ session_info <- function(approved_folder_pkgs, approved_url_pkgs) { info$options <- info$options[!names(info$options) %in% "rl_word_breaks"] class(info$options) <- c("options_info", class(info$options)) - # TODO: Extend to also cover external and python below in methods. + # TODO: Extend to also cover external. info[!names(info) %in% c("platform", "packages", "environment", "options")] <- NULL if (is.null(info$platform$quarto)) { @@ -44,6 +45,19 @@ session_info <- function(approved_folder_pkgs, approved_url_pkgs) { } } + if (!is.null(python_packages)) { + + # TODO: Get the same information as for R packages (not only name and version) + # TODO: Only show used, and not all installed, packages if possible + + info$python_packages <- python_packages + class(info$python_packages) <- c("packages_info", class(info$python_packages)) + + quarto_python_path <- Sys.getenv("QUARTO_PYTHON") + quarto_python_version <- gsub(".*/([0-9]+\\.[0-9]+\\.[0-9]+)/.*", "\\1", quarto_python_path) + info$platform$python <- quarto_python_version + } + class(info) <- c("whirl_session_info", class(info)) for (i in seq_along(info)) { class(info[[i]]) <- c(paste0("whirl_", class(info[[i]])[[1]]), class(info[[i]])) @@ -52,6 +66,21 @@ session_info <- function(approved_folder_pkgs, approved_url_pkgs) { return(info) } +#' Get Python package info from json file +#' +#' @noRd + +python_package_info <- function(json) { + + json |> + jsonlite::fromJSON() |> + tibble::enframe(name = "Package") |> + tidyr::unnest_wider(col = "value") |> + dplyr::rename( + Version = "version", + Path = "installation_path" + ) +} #' @noRd @@ -81,13 +110,18 @@ knit_print.whirl_platform_info <- function(x, ...) { #' @noRd knit_print.whirl_packages_info <- function(x, ...) { - data.frame( - Package = x$package, - Version = x$loadedversion, - `Date (UTC)` = x$date, - Source = x$source, - check.names = FALSE - ) |> + + if (!is.null(x$package)){ + x <- data.frame( + Package = x$package, + Version = x$loadedversion, + `Date (UTC)` = x$date, + Source = x$source, + check.names = FALSE + ) + } + + x |> knitr::kable() |> kableExtra::kable_styling( bootstrap_options = "striped", @@ -97,7 +131,7 @@ knit_print.whirl_packages_info <- function(x, ...) { } #' @noRd -#' + knit_print.whirl_approved_pkgs <- function(x, ...) { hold <- x |> data.frame( diff --git a/R/status.R b/R/status.R index 1b46ad8..2cd5aa6 100644 --- a/R/status.R +++ b/R/status.R @@ -11,6 +11,10 @@ get_status <- function(md) { x <- x[[1]] + add_python <- x |> + stringr::str_detect("\\{.python .cell-code") |> + any() + # Errors errors <- x |> @@ -18,6 +22,16 @@ get_status <- function(md) { stringr::str_remove_all("\\{[^\\}]*\\}") |> stringr::str_squish() + if (add_python) { + python_errors <- x |> + stringr::str_subset(pattern = "^ *\\{\\.cell-output") |> + stringr::str_remove_all("\\{[^\\}]*\\}") |> + stringr::str_squish() |> + stringr::str_subset("Error:") + + errors <- c(errors, python_errors) + } + # Warnings warnings <- x |> @@ -26,6 +40,16 @@ get_status <- function(md) { stringr::str_squish() |> stringr::str_subset(pattern = "^(W|w)arning") + if (add_python) { + python_warnings <- x |> + stringr::str_subset(pattern = "^ *\\{\\.cell-output") |> + stringr::str_remove_all("\\{[^\\}]*\\}") |> + stringr::str_squish() |> + stringr::str_subset("Warning:") + + warnings <- c(warnings, python_warnings) + } + # Status if (length(errors)) { diff --git a/R/utils.R b/R/utils.R index 56a2536..703d730 100644 --- a/R/utils.R +++ b/R/utils.R @@ -5,7 +5,7 @@ get_file_ext <- function(file_paths) { FUN = function(file_path) { file_name <- basename(file_path) file_parts <- strsplit(file_name, "\\.")[[1]] - file_extension <- ifelse(length(file_parts) == 1, "", tail(file_parts, 1)) + file_extension <- ifelse(length(file_parts) == 1, "", utils::tail(file_parts, 1)) return(file_extension) }, FUN.VALUE = character(1), diff --git a/R/whirl-package.R b/R/whirl-package.R index 49217e0..6fc4021 100644 --- a/R/whirl-package.R +++ b/R/whirl-package.R @@ -1,5 +1,6 @@ ## usethis namespace: start #' @importFrom dplyr .data +#' @importFrom reticulate py ## usethis namespace: end NULL diff --git a/README.Rmd b/README.Rmd index 9d7d408..e54ffb8 100644 --- a/README.Rmd +++ b/README.Rmd @@ -12,7 +12,17 @@ knitr::opts_chunk$set( out.width = "100%" ) +# Minimal printing for shorter readme options(whirl.verbosity_level = "minimal") + +# Use a temporary directory as working directory with all examples available +tmp <- withr::local_tempdir() + +system.file("examples", package = "whirl") |> + list.files(full.names = TRUE) |> + file.copy(to = tmp) + +knitr::opts_knit$set(root.dir = tmp) ``` # whirl @@ -46,14 +56,14 @@ The simplest way is to provide the path to a single script: ```{r ex-script} library(whirl) -script <- system.file("examples/simple/success.R", package = "whirl") -run(script) + +run("success.R") ``` + It is also possible to run several scripts simultaneously: ```{r ex-scripts} -scripts <- system.file("examples/simple", c("success.R", "warning.R"), package = "whirl") -result <- run(scripts, n_workers = 2) +result <- run(c("success.R", "warning.R"), n_workers = 2) ``` Here we are specifying that `run()` can use up to two simultaneous workers to execute the scripts, @@ -77,28 +87,27 @@ This setup is very useful when your projects have several steps that depends on The best way to implement this in your project is use a configuration file for whirl. The configuration file is a `yaml` file that specifies each steps: -```{r ex-config} -config <- system.file("examples/simple/_whirl.yaml", package = "whirl") -cat(readLines(config), sep = "\n") +`_whirl.yaml:` +```yaml +`r paste(readLines("_whirl.yaml"), collapse = "\n")``` ``` Here we are specifying that in the first step we run `succes.R`. And then when this step has been completed we continue to running the scripts in the second steps. ```{r ex-run-config} -result <- run(config, n_workers = 2) +result <- run("_whirl.yaml", n_workers = 2) ``` ```{r, ex-return-config} print(result) ``` - ## Customize run() For more information about how to customize the the execution and the logging for your needs see the following: * `run()`: For further information on how to call it. * `vignette("whirl")`: For a more in depth explanation, and more advanced usage. -* `vignette("example")`: With a simple example, including the created log. +* `vignette("articles/example")`: With a simple example, including the created log. * `options()`: On how to change the default behavior of whirl. diff --git a/README.md b/README.md index a2cc85a..da70a59 100644 --- a/README.md +++ b/README.md @@ -36,22 +36,17 @@ The simplest way is to provide the path to a single script: ``` r library(whirl) -script <- system.file("examples/simple/success.R", package = "whirl") -run(script) -#> ══ Executing scripts and generating logs ═══════════════════════════════════════ + +run("success.R") #> ✔ success.R: Completed succesfully -#> ══ End of process ══════════════════════════════════════════════════════════════ ``` It is also possible to run several scripts simultaneously: ``` r -scripts <- system.file("examples/simple", c("success.R", "warning.R"), package = "whirl") -result <- run(scripts, n_workers = 2) -#> ══ Executing scripts and generating logs ═══════════════════════════════════════ +result <- run(c("success.R", "warning.R"), n_workers = 2) #> ✔ success.R: Completed succesfully #> ⚠ warning.R: Completed with warnings -#> ══ End of process ══════════════════════════════════════════════════════════════ ``` Here we are specifying that `run()` can use up to two simultaneous @@ -77,8 +72,8 @@ print(result) #> # A tibble: 2 × 5 #> id tag script status result #> -#> 1 1 /scer/homedirs/oath/R/x86_64-pc-linux-gnu-lib… succe… -#> 2 2 /scer/homedirs/oath/R/x86_64-pc-linux-gnu-lib… warni… +#> 1 1 /private/var/folders/fx/71by3f551qzb5wkxt82cv… succe… +#> 2 2 /private/var/folders/fx/71by3f551qzb5wkxt82cv… warni… ``` ## Config files @@ -90,17 +85,17 @@ order. The best way to implement this in your project is use a configuration file for whirl. The configuration file is a `yaml` file that specifies each steps: -``` r -config <- system.file("examples/simple/_whirl.yaml", package = "whirl") -cat(readLines(config), sep = "\n") -#> steps: -#> - name: "First step" -#> paths: -#> - "success.R" -#> - name: "Second step" -#> paths: -#> - "warning.R" -#> - "error.R" +`_whirl.yaml:` + +``` yaml +steps: + - name: "First step" + paths: + - "success.R" + - name: "Second step" + paths: + - "warning.R" + - "error.R"`` ``` Here we are specifying that in the first step we run `succes.R`. And @@ -108,12 +103,10 @@ then when this step has been completed we continue to running the scripts in the second steps. ``` r -result <- run(config, n_workers = 2) -#> ══ Executing scripts and generating logs ═══════════════════════════════════════ +result <- run("_whirl.yaml", n_workers = 2) #> ✔ success.R: Completed succesfully #> ⚠ warning.R: Completed with warnings #> ✖ error.R: Completed with errors -#> ══ End of process ══════════════════════════════════════════════════════════════ ``` ``` r @@ -121,9 +114,9 @@ print(result) #> # A tibble: 3 × 5 #> id tag script status result #> -#> 1 1 /scer/homedirs/oath/R/x86_64-pc-linux-gnu-lib… succe… -#> 2 2 /scer/homedirs/oath/R/x86_64-pc-linux-gnu-lib… warni… -#> 3 3 /scer/homedirs/oath/R/x86_64-pc-linux-gnu-lib… error +#> 1 1 /private/var/folders/fx/71by3f551qzb5wkxt82cv… succe… +#> 2 2 /private/var/folders/fx/71by3f551qzb5wkxt82cv… warni… +#> 3 3 /private/var/folders/fx/71by3f551qzb5wkxt82cv… error ``` ## Customize run() @@ -134,6 +127,6 @@ logging for your needs see the following: - `run()`: For further information on how to call it. - `vignette("whirl")`: For a more in depth explanation, and more advanced usage. -- `vignette("example")`: With a simple example, including the created - log. +- `vignette("articles/example")`: With a simple example, including the + created log. - `options()`: On how to change the default behavior of whirl. diff --git a/inst/demo_script.R b/inst/demo_script.R deleted file mode 100644 index c4ad989..0000000 --- a/inst/demo_script.R +++ /dev/null @@ -1,31 +0,0 @@ -out_dir <- "~/atmos/whirl/inst/examples/demo" - - -# Single file ------------------------------------------------------------------- - -#Point to a single script -run("~/atmos/whirl/inst/examples/demo/metadata/mdcol.R", summary_dir = out_dir) - - -# Folders (run in parallel) ---------------------------------------------------- - -# A single folder -run("~/atmos/whirl/inst/examples/demo/tfl", summary_dir = out_dir) - -# Multiple folders -run(c("~/atmos/whirl/inst/examples/demo/metadata", - "~/atmos/whirl/inst/examples/demo/tfl"), summary_dir = out_dir) - - -# Config------------------------------------------------------------------------- - -# Use a config file -run("~/atmos/whirl/inst/examples/demo/demo_whirl.yaml", summary_dir = out_dir) - - -# Use a config file (skipping steps after errors) -run("~/atmos/whirl/inst/examples/demo/demo_skip_whirl.yaml", summary_dir = out_dir) - -run("~/atmos/whirl/inst/examples/sequence_exuction/test.py") - - diff --git a/inst/documents/dummy.qmd b/inst/documents/dummy.qmd index f1f35fc..ff0e52b 100644 --- a/inst/documents/dummy.qmd +++ b/inst/documents/dummy.qmd @@ -8,7 +8,10 @@ params: tmpdir: "." --- -```{r setup, include=FALSE} +```{r setup} +#| label: Setup +#| include: false + knitr::opts_chunk$set( error = TRUE, warning = TRUE, @@ -19,22 +22,56 @@ knitr::opts_chunk$set( .libPaths(c(character(), params$with_library_paths)) ``` -```{r, echo=FALSE, eval=(grepl("\\.R$", params$script))} +```{r} +#| label: Log R +#| eval: !expr grepl("\\.R$", params$script) +#| echo: false knitr::spin_child(params$script) ``` -```{r, child = params$script, eval=(grepl("\\.qmd$", params$script))} +```{r} +#| label: Log Quarto +#| child: !expr params$script +#| eval: !expr grepl("\\.qmd$", params$script) +#| include: !expr grepl("\\.qmd$", params$script) +``` + +```{r} +#| label: Log Rmarkdown +#| child: !expr params$script +#| eval: !expr grepl("\\.Rmd$", params$script) +#| include: !expr grepl("\\.Rmd$", params$script) ``` -```{r, child = params$script, eval=(grepl("\\.Rmd$", params$script))} +```{python} +#| label: Log Python +#| file: !expr params$script +#| eval: !expr grepl("\\.py$", params$script) +#| include: !expr grepl("\\.py$", params$script) ``` -```{r, child = params$script, eval=(grepl("\\.py$", params$script))} +```{python} +#| label: Python modules +#| file: !expr file.path(params$tmpdir, "python_modules.py") +#| eval: !expr grepl("\\.py$", params$script) +#| echo: false ``` -```{r sessioninfo, include=FALSE} +```{r} +#| label: Session info +#| include: false + +python_packages <- NULL +# if (file.exists(file.path(params$tmpdir, "python_imports.json"))){ +# python_packages <- whirl:::python_package_info(file.path(params$tmpdir, "python_imports.json")) +# } + saveRDS( - object = whirl:::session_info(params$check_approved_folder_pkgs, params$check_approved_url_pkgs), + object = whirl:::session_info( + approved_folder_pkgs = params$check_approved_folder_pkgs, + approved_url_pkgs = params$check_approved_url_pkgs, + python_packages = python_packages + ), file = file.path(params$tmpdir, "session_info.rds") ) @@ -44,6 +81,4 @@ if (is.character(params$renv) && params$renv == "yes" | is.logical(params$renv) file = file.path(params$tmpdir, "renv_status.rds") ) } - ``` - diff --git a/inst/documents/log.qmd b/inst/documents/log.qmd index 4e4e7cb..f3dc745 100644 --- a/inst/documents/log.qmd +++ b/inst/documents/log.qmd @@ -19,8 +19,16 @@ format: # Summary -```{r, echo=FALSE, eval=TRUE} +```{r} +#| label: Setup +#| echo: false .libPaths(c(character(), params$with_library_paths)) +``` + + +```{r} +#| label: Status +#| echo: false status <- params$tmpdir |> file.path("doc.md") |> @@ -43,15 +51,19 @@ whirl:::quarto_callout( ) ``` -```{r, echo=FALSE} -if (file.exists(file.path(params$tmpdir, "renv_status.rds"))) { - params$tmpdir |> - file.path("renv_status.rds") |> - readRDS() -} +```{r} +#| label: Renv +#| echo: false +#| eval: !expr file.exists(file.path(params$tmpdir, "renv_status.rds")) + +params$tmpdir |> + file.path("renv_status.rds") |> + readRDS() ``` -```{r, echo=FALSE} +```{r} +#| label: Track files +#| echo: false log_info <- whirl:::read_from_log() use_log_info <- nrow(log_info) > 0 @@ -62,38 +74,57 @@ if (use_log_info) { } ``` -```{r, echo=FALSE, results='asis', eval=use_log_info} +```{r} +#| echo: false +#| results: asis +#| eval: !expr use_log_info cat("## Input", "\n") ``` -```{r, echo=FALSE, eval=use_log_info} +```{r} +#| echo: false +#| eval: !expr use_log_info log_info$read ``` -```{r, echo=FALSE, results='asis', eval=use_log_info} +```{r} +#| echo: false +#| results: asis +#| eval: !expr use_log_info cat("## Output", "\n") ``` -```{r, echo=FALSE, eval=use_log_info} +```{r} +#| echo: false +#| eval: !expr use_log_info log_info$write ``` -```{r, echo=FALSE, results='asis', eval=use_log_info} +```{r} +#| echo: false +#| results: asis +#| eval: !expr use_log_info cat("## Removed", "\n") ``` -```{r, echo=FALSE, eval=use_log_info} +```{r} +#| echo: false +#| eval: !expr use_log_info log_info$delete ``` # Script -```{r, child = file.path(params$tmpdir, "doc.md")} +```{r} +#| child: !expr file.path(params$tmpdir, "doc.md") ``` # Session info -```{r, include=FALSE} +```{r} +#| label: Session info +#| include: false + knitr::opts_chunk$set( error = FALSE, warning = FALSE, @@ -106,14 +137,29 @@ info <- params$tmpdir |> readRDS() ``` -```{r, results="asis"} +```{r} +#| label: Platform +#| results: asis cat("\n## Platform \n") info$platform cat("\n") ``` -```{r, results="asis", fig.height = 0.5} -cat("\n## Packages\n") +```{r} +#| label: Python +#| results: asis +#| eval: !expr grepl("\\.py$", params$title) +#| fig.height: 0.5 +cat("\n## Python Packages\n") + +info$python_packages +``` + +```{r} +#| label: R +#| results: asis +#| fig.height: 0.5 +cat("\n## R Packages\n") if (!is.null(params$check_approved_folder_pkgs) | !is.null(params$check_approved_url_pkgs)) { @@ -123,23 +169,30 @@ if (!is.null(params$check_approved_folder_pkgs) | cat("\n") ``` -```{r, results="asis", fig.height = 0.5} +```{r} +#| results: asis +#| fig.height: 0.5 info$packages ``` -```{r, results="asis"} +```{r} +#| label: Environment +#| results: asis cat("\n## Environment variables \n") info$environment cat("\n") ``` -```{r, results="asis"} +```{r} +#| label: Options +#| results: asis cat("\n## Option settings\n") info$options cat("\n") ``` -```{r collect-objects} +```{r} +#| label: Output whirl_objects <- vector("list") if (exists("log_info")) { diff --git a/inst/documents/python_modules.py b/inst/documents/python_modules.py new file mode 100644 index 0000000..7d63501 --- /dev/null +++ b/inst/documents/python_modules.py @@ -0,0 +1,21 @@ +import json +import sys +import subprocess + +temp_dir = r.params['tmpdir'] + +# Get a list of installed packages using pip list +installed_packages = subprocess.check_output([sys.executable, '-m', 'pip', 'list', '-v']).decode('utf-8').strip().split('\n')[2:] +installed_packages = {package.split()[0]: [package.split()[1], package.split()[2]] for package in installed_packages} + +imported_modules = {} +for module_name, module in sys.modules.items(): + if not (module_name.startswith('_') or module_name.startswith('.') or '.' in module_name): + if module_name in installed_packages: + imported_modules[module_name] = { + "version": installed_packages[module_name][0], + "installation_path": installed_packages[module_name][1] + } + +with open(f'{temp_dir}/python_imports.json', 'w') as json_file: + json.dump(imported_modules, json_file) diff --git a/inst/documents/summary.qmd b/inst/documents/summary.qmd index d6810b5..7ff09f1 100644 --- a/inst/documents/summary.qmd +++ b/inst/documents/summary.qmd @@ -27,7 +27,9 @@ format: smooth-scroll: true --- -```{r, include=FALSE} +```{r} +#| label: Setup +#| include: false knitr::opts_chunk$set( error = TRUE, warning = TRUE, @@ -36,7 +38,9 @@ knitr::opts_chunk$set( ) ``` -```{r, echo = FALSE} +```{r} +#| label: Summary +#| echo: false hold <- params$summary_df |> tibble::tibble() class(hold) <- c("whirl_summary_info", "summary_info", "list") diff --git a/inst/examples/simple/_whirl.yaml b/inst/examples/_whirl.yaml similarity index 100% rename from inst/examples/simple/_whirl.yaml rename to inst/examples/_whirl.yaml diff --git a/inst/examples/demo/adam/mk100adsl.R b/inst/examples/demo/adam/mk100adsl.R deleted file mode 100644 index e2394b1..0000000 --- a/inst/examples/demo/adam/mk100adsl.R +++ /dev/null @@ -1,3 +0,0 @@ -# This script produces no errors or warnings for testing purposes - -message("this script has no errors or warnings") diff --git a/inst/examples/demo/adam/mk200adae.R b/inst/examples/demo/adam/mk200adae.R deleted file mode 100644 index e2394b1..0000000 --- a/inst/examples/demo/adam/mk200adae.R +++ /dev/null @@ -1,3 +0,0 @@ -# This script produces no errors or warnings for testing purposes - -message("this script has no errors or warnings") diff --git a/inst/examples/demo/adam/mk200adcm.R b/inst/examples/demo/adam/mk200adcm.R deleted file mode 100644 index e2394b1..0000000 --- a/inst/examples/demo/adam/mk200adcm.R +++ /dev/null @@ -1,3 +0,0 @@ -# This script produces no errors or warnings for testing purposes - -message("this script has no errors or warnings") diff --git a/inst/examples/demo/adam/mk200adec.R b/inst/examples/demo/adam/mk200adec.R deleted file mode 100644 index e2394b1..0000000 --- a/inst/examples/demo/adam/mk200adec.R +++ /dev/null @@ -1,3 +0,0 @@ -# This script produces no errors or warnings for testing purposes - -message("this script has no errors or warnings") diff --git a/inst/examples/demo/adam/mk200adhypo.R b/inst/examples/demo/adam/mk200adhypo.R deleted file mode 100644 index e2394b1..0000000 --- a/inst/examples/demo/adam/mk200adhypo.R +++ /dev/null @@ -1,3 +0,0 @@ -# This script produces no errors or warnings for testing purposes - -message("this script has no errors or warnings") diff --git a/inst/examples/demo/adam/mk300adecen.R b/inst/examples/demo/adam/mk300adecen.R deleted file mode 100644 index e2394b1..0000000 --- a/inst/examples/demo/adam/mk300adecen.R +++ /dev/null @@ -1,3 +0,0 @@ -# This script produces no errors or warnings for testing purposes - -message("this script has no errors or warnings") diff --git a/inst/examples/demo/adam/mk300adhypoen.R b/inst/examples/demo/adam/mk300adhypoen.R deleted file mode 100644 index e2394b1..0000000 --- a/inst/examples/demo/adam/mk300adhypoen.R +++ /dev/null @@ -1,3 +0,0 @@ -# This script produces no errors or warnings for testing purposes - -message("this script has no errors or warnings") diff --git a/inst/examples/demo/adam/mk400adcgm.R b/inst/examples/demo/adam/mk400adcgm.R deleted file mode 100644 index e2394b1..0000000 --- a/inst/examples/demo/adam/mk400adcgm.R +++ /dev/null @@ -1,3 +0,0 @@ -# This script produces no errors or warnings for testing purposes - -message("this script has no errors or warnings") diff --git a/inst/examples/demo/adam/mk400coreupdate.R b/inst/examples/demo/adam/mk400coreupdate.R deleted file mode 100644 index a462325..0000000 --- a/inst/examples/demo/adam/mk400coreupdate.R +++ /dev/null @@ -1,3 +0,0 @@ -# This script produces no errors or warnings for testing purposes - -stop("Scripts runs with warning") diff --git a/inst/examples/demo/adam/mk500coreupdate.R b/inst/examples/demo/adam/mk500coreupdate.R deleted file mode 100644 index 746b8ac..0000000 --- a/inst/examples/demo/adam/mk500coreupdate.R +++ /dev/null @@ -1,4 +0,0 @@ -# This script produces no errors or warnings for testing purposes - -stop("Scripts runs with warning") - diff --git a/inst/examples/demo/config_to_config.yaml b/inst/examples/demo/config_to_config.yaml deleted file mode 100644 index 0a66ae5..0000000 --- a/inst/examples/demo/config_to_config.yaml +++ /dev/null @@ -1,4 +0,0 @@ -steps: - - name: "Run Metadata folder through config" - paths: - - "tfl/tfl_whirl.yaml" diff --git a/inst/examples/demo/demo_2_whirl.yml b/inst/examples/demo/demo_2_whirl.yml deleted file mode 100644 index bc03b0d..0000000 --- a/inst/examples/demo/demo_2_whirl.yml +++ /dev/null @@ -1,13 +0,0 @@ -steps: - - paths: - - "adam/mk100adsl.R" - - "adam/mk200adae.R" - - "adam/mk300*.r|R" - - paths: - - "metadata/metadata_whirl.yaml" - - name: "Run ADaM mk300 programs" - paths: - - "adam/mk300*.r|R" - - name: "Run TFL folder through config" - paths: - - "tfl/tfl_whirl.yaml" diff --git a/inst/examples/demo/demo_skip_whirl.yaml b/inst/examples/demo/demo_skip_whirl.yaml deleted file mode 100644 index 92a9865..0000000 --- a/inst/examples/demo/demo_skip_whirl.yaml +++ /dev/null @@ -1,7 +0,0 @@ -steps: - - name: "Run ADaM mk400 programs" - paths: - - "adam/mk400*.r|R" - - name: "Run TFL programs" - paths: - - "tfl" diff --git a/inst/examples/demo/demo_whirl.yaml b/inst/examples/demo/demo_whirl.yaml deleted file mode 100644 index 0c07345..0000000 --- a/inst/examples/demo/demo_whirl.yaml +++ /dev/null @@ -1,25 +0,0 @@ -steps: - - name: "Run ADaM mk100 programs" - paths: - - "adam/mk100adsl.R" - - "adam/mk200adae.R" - - "adam/mk300*.r|R" - - name: "Run Metadata folder through config" - paths: - - "metadata/metadata_whirl.yaml" - - name: "Run ADaM mk300 programs" - paths: - - "adam/mk300*.r|R" - - name: "Run TFL folder through config" - paths: - - "tfl/tfl_whirl.yaml" - - # - name: "Run ADaM mk200 programs" - # paths: - # - "adam/mk200.*[.rR]$" - # - name: "Run ADaM mk300 programs" - # paths: - # - "adam/mk300.*[.rR]$" - # - name: "Run TFL programs" - # paths: - # - "tfl" diff --git a/inst/examples/demo/metadata/mdouput.R b/inst/examples/demo/metadata/mdouput.R deleted file mode 100644 index 87cf481..0000000 --- a/inst/examples/demo/metadata/mdouput.R +++ /dev/null @@ -1,4 +0,0 @@ - -# This script produces no errors or warnings for testing purposes - -message("this script has no errors or warnings") diff --git a/inst/examples/demo/metadata/mdoutput.R b/inst/examples/demo/metadata/mdoutput.R deleted file mode 100644 index 87cf481..0000000 --- a/inst/examples/demo/metadata/mdoutput.R +++ /dev/null @@ -1,4 +0,0 @@ - -# This script produces no errors or warnings for testing purposes - -message("this script has no errors or warnings") diff --git a/inst/examples/demo/metadata/mdparam.R b/inst/examples/demo/metadata/mdparam.R deleted file mode 100644 index 87cf481..0000000 --- a/inst/examples/demo/metadata/mdparam.R +++ /dev/null @@ -1,4 +0,0 @@ - -# This script produces no errors or warnings for testing purposes - -message("this script has no errors or warnings") diff --git a/inst/examples/demo/tfl/tfl_1.R b/inst/examples/demo/tfl/tfl_1.R deleted file mode 100644 index 87cf481..0000000 --- a/inst/examples/demo/tfl/tfl_1.R +++ /dev/null @@ -1,4 +0,0 @@ - -# This script produces no errors or warnings for testing purposes - -message("this script has no errors or warnings") diff --git a/inst/examples/demo/tfl/tfl_2.R b/inst/examples/demo/tfl/tfl_2.R deleted file mode 100644 index d022f47..0000000 --- a/inst/examples/demo/tfl/tfl_2.R +++ /dev/null @@ -1,21 +0,0 @@ - -#' Setup - -library(tidyverse) - -#' Data data - -x <- mtcars |> - as_tibble(rownames = "car") - -x |> - print(n = 100) - -message("this is good") - -#' Plot - -ggplot(data = x) + - geom_point(mapping = aes(x = mpg, y = hp, size = wt, colour = as.factor(am))) - -ggsave("plot1.png") diff --git a/inst/examples/demo/tfl/tfl_3.R b/inst/examples/demo/tfl/tfl_3.R deleted file mode 100644 index 87cf481..0000000 --- a/inst/examples/demo/tfl/tfl_3.R +++ /dev/null @@ -1,4 +0,0 @@ - -# This script produces no errors or warnings for testing purposes - -message("this script has no errors or warnings") diff --git a/inst/examples/demo/tfl/tfl_error.R b/inst/examples/demo/tfl/tfl_error.R deleted file mode 100644 index d03e9a4..0000000 --- a/inst/examples/demo/tfl/tfl_error.R +++ /dev/null @@ -1,4 +0,0 @@ - -# This script produces error for testing purposes - -stop("This is an error!") diff --git a/inst/examples/demo/tfl/tfl_whirl.yaml b/inst/examples/demo/tfl/tfl_whirl.yaml deleted file mode 100644 index 7c6d95a..0000000 --- a/inst/examples/demo/tfl/tfl_whirl.yaml +++ /dev/null @@ -1,4 +0,0 @@ -steps: - - name: "Run TFL programs" - paths: - - "tfl_1.R" diff --git a/inst/examples/sequence_exuction/error.R b/inst/examples/error.R similarity index 100% rename from inst/examples/sequence_exuction/error.R rename to inst/examples/error.R diff --git a/inst/examples/simple/prg1.R b/inst/examples/prg1.R similarity index 99% rename from inst/examples/simple/prg1.R rename to inst/examples/prg1.R index 453848e..dd64c98 100644 --- a/inst/examples/simple/prg1.R +++ b/inst/examples/prg1.R @@ -1,4 +1,3 @@ - #' Setup library(dplyr) diff --git a/inst/examples/sequence_exuction/_whirl.yaml b/inst/examples/sequence_exuction/_whirl.yaml deleted file mode 100644 index 4cb59f1..0000000 --- a/inst/examples/sequence_exuction/_whirl.yaml +++ /dev/null @@ -1,16 +0,0 @@ -steps: - - name: "Run scripts for dataflow" - paths: - - "without_whirl/prg1.R" - - "with_whirl_file/prg2.R" - - name: "Prepare adam datasets" - paths: - - "prgQmd.qmd" - - "prgRmd.Rmd" - - "error.R" - - name: "Running files from regexp" - paths: - - "prg.*(\\.R|\\.qmd)" - - name: "Running with whirl file again" - paths: - - "without_whirl/.*\\.R" diff --git a/inst/examples/sequence_exuction/error.yaml b/inst/examples/sequence_exuction/error.yaml deleted file mode 100644 index cf3d9e6..0000000 --- a/inst/examples/sequence_exuction/error.yaml +++ /dev/null @@ -1,5 +0,0 @@ -steps: - - name: "Run scripts for dataflow" - paths: - - "doesnt_exits.R" - diff --git a/inst/examples/sequence_exuction/path_and_regexp.yaml b/inst/examples/sequence_exuction/path_and_regexp.yaml deleted file mode 100644 index 0f1ed82..0000000 --- a/inst/examples/sequence_exuction/path_and_regexp.yaml +++ /dev/null @@ -1,5 +0,0 @@ -steps: - - name: "Run scripts for dataflow" - paths: - - "examples/sequence_exuction/.*\\.R$" - diff --git a/inst/examples/sequence_exuction/prgQmd.qmd b/inst/examples/sequence_exuction/prgQmd.qmd deleted file mode 100644 index 4c7a7a1..0000000 --- a/inst/examples/sequence_exuction/prgQmd.qmd +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: "prgQmd" -format: html -editor: visual ---- - -## Quarto - -Quarto enables you to weave together content and executable code into a finished document. To learn more about Quarto see . - -## Running Code - -When you click the **Render** button a document will be generated that includes both content and the output of embedded code. You can embed code like this: - -```{r} -1 + 1 -``` - -You can add options to executable code like this - -```{r} -#| echo: false -2 * 2 -``` - -The `echo: false` option disables the printing of code (only output is displayed). diff --git a/inst/examples/sequence_exuction/prgQmd_py.qmd b/inst/examples/sequence_exuction/prgQmd_py.qmd deleted file mode 100644 index d941043..0000000 --- a/inst/examples/sequence_exuction/prgQmd_py.qmd +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: "prgQmd" -format: html -editor: visual ---- - -## Quarto - -Quarto enables you to weave together content and executable code into a finished document. To learn more about Quarto see . - -## Running Code - -When you click the **Render** button a document will be generated that includes both content and the output of embedded code. You can embed code like this: - -```{python} -1 + 1 -``` - -You can add options to executable code like this - -```{python} -1+1 - -file_path = 'from_python.txt' - -with open(file_path, 'w') as file: - # Write or save text to the file - file.write("Hello from python ^^") - - -``` - -The `echo: false` option disables the printing of code (only output is displayed). diff --git a/inst/examples/sequence_exuction/prgRmd.Rmd b/inst/examples/sequence_exuction/prgRmd.Rmd deleted file mode 100644 index 75bd8bd..0000000 --- a/inst/examples/sequence_exuction/prgRmd.Rmd +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: "prgRmd" -output: html_document -date: "2024-03-01" ---- - -```{r setup, include=FALSE} -knitr::opts_chunk$set(echo = TRUE) -``` - -## R Markdown - -This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see . - -When you click the **Knit** button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this: - -```{r cars} -summary(cars) -``` - -```{r} -warning("test warning") -``` - - -## Including Plots - -You can also embed plots, for example: - -```{r pressure, echo=FALSE} -plot(pressure) -``` - -Note that the `echo = FALSE` parameter was added to the code chunk to prevent printing of the R code that generated the plot. diff --git a/inst/examples/sequence_exuction/test.py b/inst/examples/sequence_exuction/test.py deleted file mode 100644 index 8965ffb..0000000 --- a/inst/examples/sequence_exuction/test.py +++ /dev/null @@ -1,8 +0,0 @@ -1+1 - -file_path = 'from_python.txt' - -with open(file_path, 'w') as file: - # Write or save text to the file - file.write("Hello from python ^^") - diff --git a/inst/examples/sequence_exuction/with_whirl_file/_whirl.yaml b/inst/examples/sequence_exuction/with_whirl_file/_whirl.yaml deleted file mode 100644 index 56dbb9e..0000000 --- a/inst/examples/sequence_exuction/with_whirl_file/_whirl.yaml +++ /dev/null @@ -1,4 +0,0 @@ -steps: - - name: "Subfolder with config file" - paths: - - "prg1.R" diff --git a/inst/examples/sequence_exuction/with_whirl_file/prg2.R b/inst/examples/sequence_exuction/with_whirl_file/prg2.R deleted file mode 100644 index d022f47..0000000 --- a/inst/examples/sequence_exuction/with_whirl_file/prg2.R +++ /dev/null @@ -1,21 +0,0 @@ - -#' Setup - -library(tidyverse) - -#' Data data - -x <- mtcars |> - as_tibble(rownames = "car") - -x |> - print(n = 100) - -message("this is good") - -#' Plot - -ggplot(data = x) + - geom_point(mapping = aes(x = mpg, y = hp, size = wt, colour = as.factor(am))) - -ggsave("plot1.png") diff --git a/inst/examples/sequence_exuction/without_whirl/prg1.R b/inst/examples/sequence_exuction/without_whirl/prg1.R deleted file mode 100644 index d022f47..0000000 --- a/inst/examples/sequence_exuction/without_whirl/prg1.R +++ /dev/null @@ -1,21 +0,0 @@ - -#' Setup - -library(tidyverse) - -#' Data data - -x <- mtcars |> - as_tibble(rownames = "car") - -x |> - print(n = 100) - -message("this is good") - -#' Plot - -ggplot(data = x) + - geom_point(mapping = aes(x = mpg, y = hp, size = wt, colour = as.factor(am))) - -ggsave("plot1.png") diff --git a/inst/examples/sequence_exuction/without_whirl/prgQmd.qmd b/inst/examples/sequence_exuction/without_whirl/prgQmd.qmd deleted file mode 100644 index 4c7a7a1..0000000 --- a/inst/examples/sequence_exuction/without_whirl/prgQmd.qmd +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: "prgQmd" -format: html -editor: visual ---- - -## Quarto - -Quarto enables you to weave together content and executable code into a finished document. To learn more about Quarto see . - -## Running Code - -When you click the **Render** button a document will be generated that includes both content and the output of embedded code. You can embed code like this: - -```{r} -1 + 1 -``` - -You can add options to executable code like this - -```{r} -#| echo: false -2 * 2 -``` - -The `echo: false` option disables the printing of code (only output is displayed). diff --git a/inst/examples/sequence_exuction/without_whirl/prgRmd.Rmd b/inst/examples/sequence_exuction/without_whirl/prgRmd.Rmd deleted file mode 100644 index 75bd8bd..0000000 --- a/inst/examples/sequence_exuction/without_whirl/prgRmd.Rmd +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: "prgRmd" -output: html_document -date: "2024-03-01" ---- - -```{r setup, include=FALSE} -knitr::opts_chunk$set(echo = TRUE) -``` - -## R Markdown - -This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see . - -When you click the **Knit** button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this: - -```{r cars} -summary(cars) -``` - -```{r} -warning("test warning") -``` - - -## Including Plots - -You can also embed plots, for example: - -```{r pressure, echo=FALSE} -plot(pressure) -``` - -Note that the `echo = FALSE` parameter was added to the code chunk to prevent printing of the R code that generated the plot. diff --git a/inst/examples/simple/error.R b/inst/examples/simple/error.R deleted file mode 100644 index d03e9a4..0000000 --- a/inst/examples/simple/error.R +++ /dev/null @@ -1,4 +0,0 @@ - -# This script produces error for testing purposes - -stop("This is an error!") diff --git a/inst/examples/simple/log_msg.R b/inst/examples/simple/log_msg.R deleted file mode 100644 index 1295e3f..0000000 --- a/inst/examples/simple/log_msg.R +++ /dev/null @@ -1,27 +0,0 @@ - -library(whirl) - -#' This is a test script for user log messages -#' Read, write, and delete operations can be logged the following way: - -log_read("adam.adsl") -log_read("sdtm.vs") - -log_delete("adam.adds") - -Sys.sleep(4) - -log_write("adam.advs") - -log_write("output.table.ext") - -#' Behind the scenes the log messages are stored in a temporary file to be -#' read into the log report. -#' -#' The file can be accessed via the environment variable "WHIRL_LOG_MSG": - -Sys.getenv("WHIRL_LOG_MSG") - -Sys.getenv("WHIRL_LOG_MSG") |> - readLines() |> - cat(sep = "\n") diff --git a/inst/examples/simple/prgQmd.qmd b/inst/examples/simple/prgQmd.qmd deleted file mode 100644 index 4c7a7a1..0000000 --- a/inst/examples/simple/prgQmd.qmd +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: "prgQmd" -format: html -editor: visual ---- - -## Quarto - -Quarto enables you to weave together content and executable code into a finished document. To learn more about Quarto see . - -## Running Code - -When you click the **Render** button a document will be generated that includes both content and the output of embedded code. You can embed code like this: - -```{r} -1 + 1 -``` - -You can add options to executable code like this - -```{r} -#| echo: false -2 * 2 -``` - -The `echo: false` option disables the printing of code (only output is displayed). diff --git a/inst/examples/simple/prgRmd.Rmd b/inst/examples/simple/prgRmd.Rmd deleted file mode 100644 index 75bd8bd..0000000 --- a/inst/examples/simple/prgRmd.Rmd +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: "prgRmd" -output: html_document -date: "2024-03-01" ---- - -```{r setup, include=FALSE} -knitr::opts_chunk$set(echo = TRUE) -``` - -## R Markdown - -This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see . - -When you click the **Knit** button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this: - -```{r cars} -summary(cars) -``` - -```{r} -warning("test warning") -``` - - -## Including Plots - -You can also embed plots, for example: - -```{r pressure, echo=FALSE} -plot(pressure) -``` - -Note that the `echo = FALSE` parameter was added to the code chunk to prevent printing of the R code that generated the plot. diff --git a/inst/examples/simple/sleep_1.R b/inst/examples/simple/sleep_1.R deleted file mode 100644 index 188dea5..0000000 --- a/inst/examples/simple/sleep_1.R +++ /dev/null @@ -1,2 +0,0 @@ -# Dummys cript to test parrallisation of callr -Sys.sleep(1) diff --git a/inst/examples/simple/sleep_2.R b/inst/examples/simple/sleep_2.R deleted file mode 100644 index 4dfa36e..0000000 --- a/inst/examples/simple/sleep_2.R +++ /dev/null @@ -1,2 +0,0 @@ -# Dummys cript to test parrallisation of callr -Sys.sleep(2) diff --git a/inst/examples/simple/sleep_3.R b/inst/examples/simple/sleep_3.R deleted file mode 100644 index 4dfa36e..0000000 --- a/inst/examples/simple/sleep_3.R +++ /dev/null @@ -1,2 +0,0 @@ -# Dummys cript to test parrallisation of callr -Sys.sleep(2) diff --git a/inst/examples/simple/success.R b/inst/examples/success.R similarity index 100% rename from inst/examples/simple/success.R rename to inst/examples/success.R diff --git a/inst/examples/simple/warning.R b/inst/examples/warning.R similarity index 100% rename from inst/examples/simple/warning.R rename to inst/examples/warning.R diff --git a/man/run.Rd b/man/run.Rd index 8d03823..bd12283 100644 --- a/man/run.Rd +++ b/man/run.Rd @@ -51,19 +51,24 @@ The way the execution is logged is configurable through several options for e.g. the verbosity of the logs. See \link{options} on how to configure these. } \examples{ -\dontshow{if (FALSE) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +# Start by copying the following three example scripts: +file.copy( + from = system.file("examples", c("success.R", "warning.R", "error.R"), package = "whirl"), + to = "." + ) # Run a single script -script <- system.file("examples/simple/success.R", package = "whirl") -run(script) +run("success.R") # Run several scripts in parallel on up to 2 workers -scripts <- system.file("examples/simple", c("success.R", "warning.R", "error.R"), package = "whirl") -run(scripts, n_workers = 2) +run(c("success.R", "warning.R", "error.R"), n_workers = 2) + +# Run scripts in two steps by providing them as list elements +run( + list( + c("success.R", "warning.R"), + "error.R" + ), + n_workers = 2) -# Run scripts in several steps -step_1 <- system.file("examples/simple", c("success.R", "warning.R"), package = "whirl") -step_2 <- system.file("examples/simple", c("error.R"), package = "whirl") -run(list(step_1, step_2), n_workers = 2) -\dontshow{\}) # examplesIf} } diff --git a/tests/testthat/_snaps/examples.md b/tests/testthat/_snaps/examples.md new file mode 100644 index 0000000..96dbab9 --- /dev/null +++ b/tests/testthat/_snaps/examples.md @@ -0,0 +1,45 @@ +# All example scripts run with consistent output + + { + "type": "list", + "attributes": { + "names": { + "type": "character", + "attributes": {}, + "value": ["id", "tag", "script", "status"] + }, + "row.names": { + "type": "integer", + "attributes": {}, + "value": [1, 2, 3, 4, 5, 6, 7] + }, + "class": { + "type": "character", + "attributes": {}, + "value": ["tbl_df", "tbl", "data.frame"] + } + }, + "value": [ + { + "type": "double", + "attributes": {}, + "value": [1, 2, 3, 4, 5, 6, 7] + }, + { + "type": "character", + "attributes": {}, + "value": [null, null, null, null, null, null, null] + }, + { + "type": "character", + "attributes": {}, + "value": ["success.R", "warning.R", "error.R", "error.R", "prg1.R", "success.R", "warning.R"] + }, + { + "type": "character", + "attributes": {}, + "value": ["success", "warning", "error", "error", "success", "success", "warning"] + } + ] + } + diff --git a/tests/testthat/helper.R b/tests/testthat/helper.R new file mode 100644 index 0000000..847238a --- /dev/null +++ b/tests/testthat/helper.R @@ -0,0 +1,7 @@ +# Helper function to select test scripts + +test_script <- function(script) { + script <- test_path("scripts", script) |> + normalizePath(winslash = "/", mustWork = TRUE) + return(script) +} diff --git a/tests/testthat/scripts/_whirl.yaml b/tests/testthat/scripts/_whirl.yaml new file mode 100644 index 0000000..15aaa64 --- /dev/null +++ b/tests/testthat/scripts/_whirl.yaml @@ -0,0 +1,8 @@ +steps: + - name: "First step" + paths: + - "success.R" + - name: "Second step" + paths: + - "warning.R" + - "error.R" diff --git a/inst/examples/demo/metadata/metadata_whirl.yaml b/tests/testthat/scripts/_whirl_r_programs.yaml similarity index 50% rename from inst/examples/demo/metadata/metadata_whirl.yaml rename to tests/testthat/scripts/_whirl_r_programs.yaml index 5dc8504..65c42d1 100644 --- a/inst/examples/demo/metadata/metadata_whirl.yaml +++ b/tests/testthat/scripts/_whirl_r_programs.yaml @@ -1,4 +1,4 @@ steps: - - name: "Run metadata programs" + - name: "Run all R programs" paths: - "*.r|R" diff --git a/tests/testthat/scripts/_whirl_to_config.yaml b/tests/testthat/scripts/_whirl_to_config.yaml new file mode 100644 index 0000000..6936d45 --- /dev/null +++ b/tests/testthat/scripts/_whirl_to_config.yaml @@ -0,0 +1,4 @@ +steps: + - name: "Run config file" + paths: + - "_whirl.yaml" diff --git a/tests/testthat/scripts/_whirl_unnamed.yaml b/tests/testthat/scripts/_whirl_unnamed.yaml new file mode 100644 index 0000000..529921d --- /dev/null +++ b/tests/testthat/scripts/_whirl_unnamed.yaml @@ -0,0 +1,8 @@ +steps: + - name: "Named step" + paths: + - "success.R" + - paths: + - "warning.R" + - paths: + - "error.R" diff --git a/inst/examples/sequence_exuction/without_whirl/error/error.R b/tests/testthat/scripts/error.R similarity index 100% rename from inst/examples/sequence_exuction/without_whirl/error/error.R rename to tests/testthat/scripts/error.R diff --git a/tests/testthat/scripts/py_error.py b/tests/testthat/scripts/py_error.py new file mode 100644 index 0000000..66f6580 --- /dev/null +++ b/tests/testthat/scripts/py_error.py @@ -0,0 +1,6 @@ + +1 + "a" + +raise TypeError("This is a type error for testing purposes") + +raise Exception("Error also for testing") diff --git a/tests/testthat/scripts/py_success.py b/tests/testthat/scripts/py_success.py new file mode 100644 index 0000000..60628a7 --- /dev/null +++ b/tests/testthat/scripts/py_success.py @@ -0,0 +1,2 @@ +a = 2 + 2 +print(a) diff --git a/tests/testthat/scripts/py_warning.py b/tests/testthat/scripts/py_warning.py new file mode 100644 index 0000000..e378c26 --- /dev/null +++ b/tests/testthat/scripts/py_warning.py @@ -0,0 +1,3 @@ +import warnings + +warnings.warn("test warning") diff --git a/inst/examples/demo/metadata/mdcol.R b/tests/testthat/scripts/success.R similarity index 100% rename from inst/examples/demo/metadata/mdcol.R rename to tests/testthat/scripts/success.R diff --git a/inst/examples/demo/tfl/tfl_warning.R b/tests/testthat/scripts/warning.R similarity index 100% rename from inst/examples/demo/tfl/tfl_warning.R rename to tests/testthat/scripts/warning.R diff --git a/tests/testthat/setup.R b/tests/testthat/setup.R index 8ec812f..2e8c4db 100644 --- a/tests/testthat/setup.R +++ b/tests/testthat/setup.R @@ -1,7 +1,13 @@ -## whirl have to be installed for tests -pkgs <- installed.packages() |> row.names() -test <- any(pkgs %in% "whirl") +## whirl have to be installed for Quarto to use it -if (!test) { - cli::cli_abort("The package whirl have to be installed for subprocesses") -} +withr::local_envvar( + R_USER_CACHE_DIR = tempfile(), + .local_envir = teardown_env() +) + +# Minimal prints to make it easier to read test output + +withr::local_options( + list(whirl.verbosity_level = "quiet"), + .local_envir = teardown_env() +) diff --git a/tests/testthat/test-enrich_input.R b/tests/testthat/test-enrich_input.R index 3ddf839..6d66445 100644 --- a/tests/testthat/test-enrich_input.R +++ b/tests/testthat/test-enrich_input.R @@ -1,62 +1,68 @@ -test_that("testing enrich_input()", { - file_config <- system.file("examples/demo/metadata/metadata_whirl.yaml", package = "whirl") - incomplete_config <- system.file("examples/demo/demo_2_whirl.yml", package = "whirl") - file <- system.file("examples/demo/adam/mk100adsl.R", package = "whirl") - config_to_prune <- system.file("examples/demo/demo_whirl.yaml", package = "whirl") - directory <- system.file("examples/simple", package = "whirl") - - op <- options(whirl.verbosity_level = "minimal") - - #A config file - withr::with_dir(tempdir(), { - enriched <- enrich_input(input = file_config) - expect_type(enriched, "list") - names(enriched[[1]]) |> expect_equal(c("name", "paths")) - }) - - #Incomplete config - withr::with_dir(tempdir(), { - enriched <- enrich_input(input = incomplete_config) - expect_type(enriched, "list") - names(enriched[[1]]) |> expect_equal(c("name", "paths")) - enriched[[1]]$name |> expect_equal("Step 1: Unnamed chunk") - enriched[[2]]$name |> expect_equal("Step 2: Unnamed chunk") - }) - - #A file - withr::with_dir(tempdir(), { - enriched <- enrich_input(input = file) - expect_type(enriched, "list") - names(enriched[[1]]) |> expect_equal(c("name", "paths")) - enriched[[1]]$name |> expect_equal("Step 1: Unnamed chunk") - enriched[[1]]$paths |> expect_equal(file) - }) - - #Pruning a config file - withr::with_dir(tempdir(), { - enriched <- enrich_input(input = config_to_prune, steps = "Run ADaM mk100 programs") - expect_type(enriched, "list") - names(enriched[[1]]) |> expect_equal(c("name", "paths")) - length(enriched) |> expect_equal(1) - }) - - #Pruning a config file - withr::with_dir(tempdir(), { - enriched <- enrich_input(input = config_to_prune, - steps = c("Run ADaM mk100 programs", - "Run ADaM mk300 programs")) - expect_type(enriched, "list") - names(enriched[[1]]) |> expect_equal(c("name", "paths")) - length(enriched) |> expect_equal(2) - }) - - #Expect error as directory is given as input - withr::with_dir(tempdir(), { - enriched <- enrich_input(input = directory) |> - expect_error() - }) - - on.exit(options(op)) +test_that("Enrich input works as expected", { + + # Find all R programs + + enriched <- test_script("_whirl_r_programs.yaml") |> + enrich_input() |> + expect_type("list") |> + expect_length(1) + + enriched[[1]] |> + expect_type("list") |> + expect_length(2) |> + expect_named(c("name", "paths")) |> + lapply(expect_type, "character") |> + invisible() + + # Unnamed steps + + test_script("_whirl_unnamed.yaml") |> + enrich_input() |> + expect_type("list") |> + expect_length(3) |> + vapply(FUN = \(x) x$name, FUN.VALUE = character(1)) |> + expect_equal(c("Named step", "Step 2: Unnamed chunk", "Step 3: Unnamed chunk")) + + # File input + + enriched <- test_script("success.R") |> + enrich_input() |> + expect_type("list") |> + expect_length(1) + + enriched[[1]] |> + expect_type("list") |> + expect_length(2) |> + expect_named(c("name", "paths")) |> + unlist() |> + expect_equal( + c( + name = "Step 1: Unnamed chunk", + paths = test_script("success.R") + ) + ) + + # Pruning a config file + + test_script("_whirl.yaml") |> + enrich_input(steps = "Second step") |> + expect_type("list") |> + expect_length(1) |> + vapply(FUN = \(x) x$name, FUN.VALUE = character(1)) |> + expect_equal("Second step") + + test_script("_whirl.yaml") |> + enrich_input(steps = c("First step", "Second step")) |> + expect_type("list") |> + expect_length(2) |> + vapply(FUN = \(x) x$name, FUN.VALUE = character(1)) |> + expect_equal(c("First step", "Second step")) + + # Expected error when input is a directory + + test_script("") |> + enrich_input() |> + expect_error() }) diff --git a/tests/testthat/test-examples.R b/tests/testthat/test-examples.R new file mode 100644 index 0000000..34a7502 --- /dev/null +++ b/tests/testthat/test-examples.R @@ -0,0 +1,28 @@ +test_that("All example scripts run with consistent output", { + + withr::with_tempdir({ + + # Copy all example scripts to the temporary working directory + + system.file("examples", package = "whirl") |> + list.files(full.names = TRUE) |> + file.copy(recursive = TRUE, to = ".") + + # Run all examples after in separate steps + + res <- list.files() |> + as.list() |> + run() |> + expect_no_error() |> + expect_no_warning() + + # Unify result to only be about the status of the script and without + # the full path to the script + + res$script <- basename(res$script) + res <- res[c("id", "tag", "script", "status")] + + # Check that the results now are consistent + expect_snapshot_value(res, style = "json2") + }) +}) diff --git a/tests/testthat/test-internal_run.R b/tests/testthat/test-internal_run.R index b407ebc..6500fd0 100644 --- a/tests/testthat/test-internal_run.R +++ b/tests/testthat/test-internal_run.R @@ -1,19 +1,19 @@ test_that("testing internal_run()", { - file_config <- system.file("examples/demo/tfl/tfl_whirl.yaml", package = "whirl") - config_to_config <- system.file("examples/demo/config_to_config.yaml", package = "whirl") - #A config file - withr::with_dir(tempdir(), { - queue <- whirl_queue$new() - internal_run(input = file_config, steps = NULL, level = 1, queue = queue) |> - expect_no_error() - }) + # A config file - #A config file - withr::with_dir(tempdir(), { - queue <- whirl_queue$new() - internal_run(input = config_to_config, steps = NULL, level = 1, queue = queue) |> - expect_no_error() - }) + q <- whirl_queue$new() + + test_script("_whirl.yaml") |> + internal_run(steps = NULL, level = 1, queue = q) |> + expect_no_error() + + # A config file calling another config file + + q <- whirl_queue$new() + + test_script("_whirl_to_config.yaml") |> + internal_run(steps = NULL, level = 1, queue = q) |> + expect_no_error() }) diff --git a/tests/testthat/test-read_regexp.R b/tests/testthat/test-read_regexp.R index 205c1f9..433f313 100644 --- a/tests/testthat/test-read_regexp.R +++ b/tests/testthat/test-read_regexp.R @@ -1,30 +1,25 @@ -test_that("testing read_regexp()", { - file <- system.file("examples/demo/adam/mk100adsl.R", package = "whirl") - dir <- system.file("examples/demo/adam", package = "whirl") +test_that("read_regexp() finds the right files", { - #A config file - withr::with_dir(tempdir(), { - got <- read_regexp(input = file) - expect_identical(got, file) - }) + # A single file - #Using regexp - withr::with_dir(tempdir(), { - all_files <- read_regexp(input = paste0(dir, "/*.R")) - expect_true(length(all_files) > 1) - }) + test_script("success.R") |> + read_regexp() |> + expect_equal(test_script("success.R")) - #Using regexp - withr::with_dir(tempdir(), { - all_files2 <- read_regexp(input = paste0(dir, "/*.r|R")) - expect_true(length(all_files2) > 1) - }) + # All R files in a directory + test_script("") |> + file.path("*.R") |> + read_regexp() |> + expect_match("\\.R$") |> + length() |> + expect_gt(1) - # A non-existing file - withr::with_dir(tempdir(), { - read_regexp(input = paste0(regexp, "/donotexsist.R")) |> + # Error when file does not exist + + test_script("") |> + file.path("fake_program.R") |> + read_regexp() |> expect_error() - }) }) diff --git a/tests/testthat/test-run.R b/tests/testthat/test-run.R index 65da097..78364c0 100644 --- a/tests/testthat/test-run.R +++ b/tests/testthat/test-run.R @@ -1,33 +1,87 @@ -test_that("testing run()", { - file_config <- system.file("examples/demo/tfl/tfl_whirl.yaml", package = "whirl") - directory <- system.file("examples/simple", package = "whirl") - file <- system.file("examples/demo/adam/mk100adsl.R", package = "whirl") - - #A config file - withr::with_dir(tempdir(), { - run(input = file_config) |> - expect_no_error() - }) - - #Pointing to a directory - withr::with_dir(tempdir(), { - run(input = directory) |> - expect_error() - }) - - # A file - withr::with_dir(tempdir(), { - run(input = file, n_workers = 1) |> - expect_no_error() - }) - - # A list - withr::with_dir(tempdir(), { - run(input = list(file)) |> - expect_no_error() - }) +test_that("Run single R script", { + res <- test_script("success.R") |> + run() |> + expect_no_condition() + + res[["status"]] |> + expect_equal("success") + + res[["result"]][[1]] |> + names() |> + expect_equal(c("status", "session_info_rlist", "log_details")) + +}) + +test_that("Run single python script", { + + res <- test_script("py_success.py") |> + run() |> + expect_no_condition() + + res[["status"]] |> + expect_equal("success") + + res[["result"]][[1]] |> + names() |> + expect_equal(c("status", "session_info_rlist", "log_details")) + +}) + +test_that("Run multiple R scripts", { + + res <- test_script(c("success.R", "warning.R", "error.R")) |> + run() |> + expect_no_error() + + res[["status"]] |> + expect_equal(c("success", "warning", "error")) + + res[["result"]][[1]][["status"]][c("error", "warning")] |> + lapply(\(x) length(x) > 0) |> + unlist() |> + expect_equal(c(FALSE, FALSE), ignore_attr = TRUE) + + res[["result"]][[2]][["status"]][c("error", "warning")] |> + lapply(\(x) length(x) > 0) |> + unlist() |> + expect_equal(c(FALSE, TRUE), ignore_attr = TRUE) + + res[["result"]][[3]][["status"]][c("error", "warning")] |> + lapply(\(x) length(x) > 0) |> + unlist() |> + expect_equal(c(TRUE, FALSE), ignore_attr = TRUE) +}) + +test_that("Run multiple python scripts", { + + res <- test_script(c("py_success.py", "py_warning.py", "py_error.py")) |> + run() |> + expect_no_error() + + res[["status"]] |> + expect_equal(c("success", "warning", "error")) + + res[["result"]][[1]][["status"]][c("error", "warning")] |> + lapply(\(x) length(x) > 0) |> + unlist() |> + expect_equal(c(FALSE, FALSE), ignore_attr = TRUE) + + res[["result"]][[2]][["status"]][c("error", "warning")] |> + lapply(\(x) length(x) > 0) |> + unlist() |> + expect_equal(c(FALSE, TRUE), ignore_attr = TRUE) + + res[["result"]][[3]][["status"]][c("error", "warning")] |> + lapply(\(x) length(x) > 0) |> + unlist() |> + expect_equal(c(TRUE, FALSE), ignore_attr = TRUE) }) +test_that("Run yaml config file", { + res <- test_script("_whirl.yaml") |> + run() |> + expect_no_error() +}) diff --git a/tests/testthat/test-strace.R b/tests/testthat/test-strace.R index 1664578..abc33bf 100644 --- a/tests/testthat/test-strace.R +++ b/tests/testthat/test-strace.R @@ -10,11 +10,11 @@ test_that("strace works", { start_strace(pid = p$get_pid(), file = file.path(getwd(), "strace.log")) - cat("============= Initial: =============", "\n") - cat(c("wd:", getwd()), "\n") - cat(c("files:", list.files()), "\n") - cat(c("environment:", ls()), "\n") - cat("====================================", "\n") + # cat("============= Initial: =============", "\n") + # cat(c("wd:", getwd()), "\n") + # cat(c("files:", list.files()), "\n") + # cat(c("environment:", ls()), "\n") + # cat("====================================", "\n") # No output yet @@ -99,11 +99,11 @@ test_that("strace works", { p$kill() p$finalize() - cat("============= final: =============", "\n") - cat(c("wd:", getwd()), "\n") - cat(c("files:", list.files()), "\n") - cat(c("environment:", ls()), "\n") - cat("==================================", "\n") + # cat("============= final: =============", "\n") + # cat(c("wd:", getwd()), "\n") + # cat(c("files:", list.files()), "\n") + # cat(c("environment:", ls()), "\n") + # cat("==================================", "\n") }, tmpdir = getwd() ) diff --git a/tests/testthat/test-use_whirl.R b/tests/testthat/test-use_whirl.R index ddf95be..942f164 100644 --- a/tests/testthat/test-use_whirl.R +++ b/tests/testthat/test-use_whirl.R @@ -2,9 +2,13 @@ test_that("use_whirl", { withr::with_tempdir({ rlang::local_interactive(FALSE) - usethis::create_project(path = ".") + usethis::create_project(path = ".") |> + expect_message() |> + suppressMessages() - use_whirl() + use_whirl() |> + expect_message() |> + suppressMessages() expect_true(file.exists("_whirl.yaml")) diff --git a/tests/testthat/test-util_queue_summary.R b/tests/testthat/test-util_queue_summary.R index 8ac36f2..c9dd6d6 100644 --- a/tests/testthat/test-util_queue_summary.R +++ b/tests/testthat/test-util_queue_summary.R @@ -1,28 +1,34 @@ # Test for existence of 'result' in the queue_table test_that("queue_table must contain a list named 'result'", { - queue_table <- list(dummy = "dummy") - expect_error(util_queue_summary(queue_table), "queue_table must contain a list named 'result'") + + list(dummy = "dummy") |> + util_queue_summary() |> + expect_error("queue_table must contain a list named 'result'") + }) # Test for existence of 'log_details' and 'status' in each result test_that("Each result in queue_table must contain 'log_details' and 'status'", { - queue_table <- list(result = list(list(log_details = "dummy"))) - expect_error(util_queue_summary(queue_table), "Each result in queue_table must contain 'log_details' and 'status'") + + list(result = list(list(log_details = "dummy"))) |> + util_queue_summary() |> + expect_error("Each result in queue_table must contain 'log_details' and 'status'") + }) # Test for successful creation of summary tibble + test_that("Summary tibble is created successfully", { - q <- whirl_queue$new(n_workers = 3) - c( - system.file("examples/simple/prg1.R", package = "whirl"), - system.file("examples/simple/success.R", package = "whirl") - ) |> + q <- whirl_queue$new(n_workers = 2) + + test_script(c("success.R", "py_success.py")) |> q$run() - queue_table <- q$queue - summary_tibble <- util_queue_summary(queue_table) - expect_type(summary_tibble, "list") - expect_equal(ncol(summary_tibble), 5) - expect_equal(nrow(summary_tibble), 2) + q$queue |> + util_queue_summary() |> + expect_s3_class("tbl_df") |> + expect_named(c('Directory', 'Filename', 'Status', 'Hyperlink', 'Information')) |> + nrow() |> + expect_equal(2) }) diff --git a/tests/testthat/test-whirl_r_session.R b/tests/testthat/test-whirl_r_session.R index 6d6181f..ee6f4fe 100644 --- a/tests/testthat/test-whirl_r_session.R +++ b/tests/testthat/test-whirl_r_session.R @@ -2,7 +2,8 @@ test_that("interactive whirl R session components not tested in run", { p <- whirl_r_session$new(verbosity_level = "minimal") p$print() |> - expect_message() + expect_message() |> + suppressMessages() p$get_wd() |> dir.exists() |> diff --git a/vignettes/articles/example.Rmd b/vignettes/articles/example.Rmd index 4cc61be..cca1575 100644 --- a/vignettes/articles/example.Rmd +++ b/vignettes/articles/example.Rmd @@ -2,22 +2,27 @@ title: "Log example" --- - ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) -system.file("examples/simple/prg1.R", package = "whirl") |> - file.copy(to = "example.R") +# Use a temporary directory as working directory with the example program available + +wd <- getwd() +tmp <- withr::local_tempdir() + +system.file("examples/prg1.R", package = "whirl") |> + file.copy(to = file.path(tmp, "example.R")) + +knitr::opts_knit$set(root.dir = tmp) ``` In this example we are going to execute the following script and create a log of it's execution: -```{r, echo = FALSE} -readLines("example.R") |> - cli::cli_code() +`example.R:` +```{r, file = "example.R", eval = FALSE} ``` We are going to use the `run()` function to execute the script, and since this vignette @@ -30,7 +35,7 @@ options(whirl.track_files = TRUE) options(whirl.verbosity_level = "minimal") ``` -The `verbosity_level` is set to `minimal` for nicer printing in ths vignette. +The `verbosity_level` is set to `minimal` for nicer printing in this vignette. Now we are ready to execute the script: ```{r run} @@ -42,8 +47,19 @@ print(result) The script is now executed and you can access the logs below: ```{r copy, include=FALSE} -file.copy(from = "summary.html", to = "../docs/articles") -file.copy(from = "example_log.html", to = "../docs/articles") +# Also check if all outputs were created as expected + +articles_folder <- file.path(wd, "../../docs/articles") + +articles_folder |> + dir.exists() |> + stopifnot() + +c("summary.html", "example_log.html", "plot1.png") |> + lapply(file.copy, to = articles_folder, overwrite = TRUE) |> + unlist() |> + all() |> + stopifnot() ``` * [View summary log](summary.html)