From b82eb8630c38b907cac7004e274c14b4df872e5c Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 5 Sep 2024 10:48:38 +0200 Subject: [PATCH 1/8] Change name of the raw (unfiltered object) `._raw_` to `._raw` --- NEWS.md | 1 + R/module_data_summary.R | 2 +- R/module_filter_data.R | 2 +- R/module_init_data.R | 7 ++- R/teal_data_utils.R | 17 +++-- tests/testthat/test-module_teal.R | 69 ++++++++++----------- tests/testthat/test-shinytest2-show-rcode.R | 2 +- 7 files changed, 56 insertions(+), 44 deletions(-) diff --git a/NEWS.md b/NEWS.md index e7052c1b17..ba28fde34e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -22,6 +22,7 @@ * Easier way of to call `javascript` events by setting `$(document).ready(function() { ... })`. #1114 * Provided progress bar for modules loading and data filtering during teal app startup. * Filter mapping display has a separate icon in the tab. +* Environment of the `data` passed to `teal_module`'s server contains unfiltered datasets names `._raw` # teal 0.15.2 diff --git a/R/module_data_summary.R b/R/module_data_summary.R index 407edc0aba..80adb84d2e 100644 --- a/R/module_data_summary.R +++ b/R/module_data_summary.R @@ -148,7 +148,7 @@ get_filter_overview <- function(teal_data) { ) unfiltered_data_objs <- sapply( datanames, - function(name) teal.code::get_env(teal_data())[[paste0(name, "._raw_")]], + function(name) teal.code::get_env(teal_data())[[sprintf(".%s_raw", name)]], simplify = FALSE ) diff --git a/R/module_filter_data.R b/R/module_filter_data.R index 20bf73dd6a..ad87b1193c 100644 --- a/R/module_filter_data.R +++ b/R/module_filter_data.R @@ -52,7 +52,7 @@ srv_filter_data <- function(id, datasets, active_datanames, data_rv, is_active) #' @rdname module_filter_data .make_filtered_teal_data <- function(modules, data, datasets = NULL, datanames) { - data <- eval_code(data, sprintf("%1$s._raw_ <- %1$s", datanames)) + data <- eval_code(data, sprintf(".%1$s_raw <- %1$s", datanames)) filtered_code <- teal.slice::get_filter_expr(datasets = datasets, datanames = datanames) filtered_teal_data <- .append_evaluated_code(data, filtered_code) filtered_datasets <- sapply(datanames, function(x) datasets$get_data(x, filtered = TRUE), simplify = FALSE) diff --git a/R/module_init_data.R b/R/module_init_data.R index 7990a2bb0a..824f4bf0cc 100644 --- a/R/module_init_data.R +++ b/R/module_init_data.R @@ -134,7 +134,10 @@ srv_init_data <- function(id, data, modules, filter = teal_slices()) { }) # Adds signature protection to the datanames in the data - reactive(.add_signature_to_data(data_validated())) + reactive({ + req(data_validated()) + .add_signature_to_data(data_validated()) + }) }) } @@ -151,7 +154,7 @@ srv_init_data <- function(id, data, modules, filter = teal_slices()) { list(code = trimws(c(teal.code::get_code(data), hashes), which = "right")), list(join_keys = teal.data::join_keys(data)), sapply( - ls(teal.code::get_env(data)), + .teal_data_ls(data), teal.code::get_var, object = data, simplify = FALSE diff --git a/R/teal_data_utils.R b/R/teal_data_utils.R index bb265e0fcd..f3dff781f1 100644 --- a/R/teal_data_utils.R +++ b/R/teal_data_utils.R @@ -42,8 +42,14 @@ NULL .subset_teal_data <- function(data, datanames) { checkmate::assert_class(data, "teal_data") checkmate::assert_class(datanames, "character") - datanames_corrected <- intersect(datanames, ls(data@env)) - dataname_corrected_with_raw <- intersect(c(datanames, sprintf("%s._raw_", datanames)), ls(data@env)) + datanames_corrected <- intersect(datanames, .teal_data_ls(data)) + dataname_corrected_with_raw <- c( + datanames_corrected, + intersect( + sprintf(".%s_raw", datanames_corrected), + ls(data@env, all.names = TRUE) + ) + ) if (!length(datanames)) { return(teal_data()) @@ -71,6 +77,9 @@ NULL #' @rdname teal_data_utilities .teal_data_ls <- function(data) { - checkmate::assert_class(data, "teal_data") - grep("._raw_", ls(teal.code::get_env(data), all.names = FALSE), value = TRUE, invert = TRUE) + datanames <- ls( + teal.code::get_env(data), + all.names = FALSE # doesn't consider objects prefixed by `.` as datanames (including filtered datanames) + ) + include_parent_datanames(datanames, teal.data::join_keys(data)) # for topological sort } diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index b1162c9924..204b8416c8 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -1,6 +1,6 @@ # comment: srv_teal is exported so the tests here are extensive and cover srv_data as well. # testing of srv_data is not needed. -module_output_table <<- function(output, id) { +module_summary_table <<- function(output, id) { testthat::skip_if_not_installed("rvest") table_id <- sprintf("teal_modules-%s-data_summary-table", id) html <- output[[table_id]]$html @@ -405,9 +405,9 @@ testthat::describe("srv_teal teal_modules", { testthat::expect_null(modules_output$module_1()) session$setInputs(`data-teal_data_module-data-dataset` = "iris", `teal_modules-active_tab` = "module_1") - testthat::expect_identical( - ls(teal.code::get_env(modules_output$module_1()())), - c("iris", "iris._raw_") + testthat::expect_setequal( + ls(teal.code::get_env(modules_output$module_1()()), all.names = TRUE), + c("iris", ".iris_raw") ) # comment: can't trigger reactivity in testServer - the change in a reactive input data @@ -415,9 +415,9 @@ testthat::describe("srv_teal teal_modules", { # is sent to another teal_module session$setInputs(`data-teal_data_module-data-dataset` = "mtcars", `teal_modules-active_tab` = "module_2") session$flushReact() - testthat::expect_identical( - ls(teal.code::get_env(modules_output$module_2()())), - c("mtcars", "mtcars._raw_") + testthat::expect_setequal( + ls(teal.code::get_env(modules_output$module_2()()), all.names = TRUE), + c("mtcars", ".mtcars_raw") ) } ) @@ -1228,11 +1228,11 @@ testthat::describe("srv_teal filters", { session$flushReact() # iris is not active testthat::expect_identical(modules_output$module_1()()[["iris"]], iris) - testthat::expect_identical(modules_output$module_1()()[["iris._raw_"]], iris) + testthat::expect_identical(modules_output$module_1()()[[".iris_raw"]], iris) # mtcars has been modified expected_mtcars <- subset(mtcars, cyl == 4) testthat::expect_identical(modules_output$module_1()()[["mtcars"]], expected_mtcars) - testthat::expect_identical(modules_output$module_1()()[["mtcars._raw_"]], mtcars) + testthat::expect_identical(modules_output$module_1()()[[".mtcars_raw"]], mtcars) expected_code <- paste0( c( @@ -1240,8 +1240,8 @@ testthat::describe("srv_teal filters", { "mtcars <- mtcars", sprintf('stopifnot(rlang::hash(iris) == "%s")', rlang::hash(iris)), sprintf('stopifnot(rlang::hash(mtcars) == "%s")', rlang::hash(mtcars)), - "iris._raw_ <- iris", - "mtcars._raw_ <- mtcars", + ".iris_raw <- iris", + ".mtcars_raw <- mtcars", "mtcars <- dplyr::filter(mtcars, cyl == 4)" ), collapse = "\n" @@ -1380,9 +1380,9 @@ testthat::describe("srv_teal teal_module(s) transformer", { expr = { session$setInputs(`teal_modules-active_tab` = "module_1") testthat::expect_identical(modules_output$module_1()()[["iris"]], head(iris)) - testthat::expect_identical(modules_output$module_1()()[["iris._raw_"]], iris) + testthat::expect_identical(modules_output$module_1()()[[".iris_raw"]], iris) testthat::expect_identical(modules_output$module_1()()[["mtcars"]], head(mtcars)) - testthat::expect_identical(modules_output$module_1()()[["mtcars._raw_"]], mtcars) + testthat::expect_identical(modules_output$module_1()()[[".mtcars_raw"]], mtcars) } ) }) @@ -1415,17 +1415,17 @@ testthat::describe("srv_teal teal_module(s) transformer", { rownames(expected_iris) <- NULL expected_iris <- head(expected_iris) testthat::expect_identical(modules_output$module_1()()[["iris"]], expected_iris) - testthat::expect_identical(modules_output$module_1()()[["iris._raw_"]], iris) + testthat::expect_identical(modules_output$module_1()()[[".iris_raw"]], iris) testthat::expect_identical(modules_output$module_1()()[["mtcars"]], head(subset(mtcars, cyl == 6))) - testthat::expect_identical(modules_output$module_1()()[["mtcars._raw_"]], mtcars) + testthat::expect_identical(modules_output$module_1()()[[".mtcars_raw"]], mtcars) expected_code <- paste(collapse = "\n", c( "iris <- iris", "mtcars <- mtcars", sprintf('stopifnot(rlang::hash(iris) == "%s")', rlang::hash(iris)), sprintf('stopifnot(rlang::hash(mtcars) == "%s")', rlang::hash(mtcars)), - "iris._raw_ <- iris", - "mtcars._raw_ <- mtcars", + ".iris_raw <- iris", + ".mtcars_raw <- mtcars", 'iris <- dplyr::filter(iris, Species == "versicolor")', "mtcars <- dplyr::filter(mtcars, cyl == 6)", "iris <- head(iris, n = 6)", @@ -1464,17 +1464,17 @@ testthat::describe("srv_teal teal_module(s) transformer", { session$flushReact() testthat::expect_identical(modules_output$module_1()()[["iris"]], head(iris)) - testthat::expect_identical(modules_output$module_1()()[["iris._raw_"]], iris) + testthat::expect_identical(modules_output$module_1()()[[".iris_raw"]], iris) testthat::expect_identical(modules_output$module_1()()[["mtcars"]], head(subset(mtcars, cyl == 4))) - testthat::expect_identical(modules_output$module_1()()[["mtcars._raw_"]], mtcars) + testthat::expect_identical(modules_output$module_1()()[[".mtcars_raw"]], mtcars) expected_code <- paste(collapse = "\n", c( "iris <- iris", "mtcars <- mtcars", sprintf('stopifnot(rlang::hash(iris) == "%s")', rlang::hash(iris)), sprintf('stopifnot(rlang::hash(mtcars) == "%s")', rlang::hash(mtcars)), - "iris._raw_ <- iris", - "mtcars._raw_ <- mtcars", + ".iris_raw <- iris", + ".mtcars_raw <- mtcars", "mtcars <- dplyr::filter(mtcars, cyl == 4)", "iris <- head(iris, n = 6)", "mtcars <- head(mtcars, n = 6)" @@ -1582,7 +1582,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { expr = { session$setInputs(`teal_modules-active_tab` = "module_1") testthat::expect_identical(modules_output$module_1()()[["iris"]], iris) - testthat::expect_identical(modules_output$module_1()()[["iris._raw_"]], iris) + testthat::expect_identical(modules_output$module_1()()[[".iris_raw"]], iris) } ) }) @@ -1611,7 +1611,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { expr = { session$setInputs(`teal_modules-active_tab` = "module_1") testthat::expect_identical(modules_output$module_1()()[["iris"]], iris) - testthat::expect_identical(modules_output$module_1()()[["iris._raw_"]], iris) + testthat::expect_identical(modules_output$module_1()()[[".iris_raw"]], iris) } ) }) @@ -1696,7 +1696,7 @@ testthat::describe("srv_teal summary table", { session$setInputs("teal_modules-active_tab" = "module_1") session$flushReact() testthat::expect_identical( - module_output_table(output, "module_1"), + module_summary_table(output, "module_1"), data.frame( "Data Name" = c("iris", "mtcars"), Obs = c("150/150", "32/32"), @@ -1727,7 +1727,7 @@ testthat::describe("srv_teal summary table", { session$setInputs("teal_modules-active_tab" = "module_1") session$flushReact() testthat::expect_identical( - module_output_table(output, "module_1"), + module_summary_table(output, "module_1"), data.frame( "Data Name" = c("a", "b"), Obs = c("3/3", "6/6"), @@ -1760,7 +1760,7 @@ testthat::describe("srv_teal summary table", { session$setInputs("teal_modules-active_tab" = "module_1") session$flushReact() testthat::expect_identical( - module_output_table(output, "module_1"), + module_summary_table(output, "module_1"), data.frame( "Data Name" = c("a", "b"), Obs = c("3/3", "6/6"), @@ -1794,7 +1794,7 @@ testthat::describe("srv_teal summary table", { session$setInputs("teal_modules-active_tab" = "module_1") session$flushReact() testthat::expect_identical( - module_output_table(output, "module_1"), + module_summary_table(output, "module_1"), data.frame( "Data Name" = c("a", "b"), Obs = c("3/3", "6/6"), @@ -1829,7 +1829,7 @@ testthat::describe("srv_teal summary table", { session$setInputs("teal_modules-active_tab" = "module_1") session$flushReact() testthat::expect_identical( - module_output_table(output, "module_1"), + module_summary_table(output, "module_1"), data.frame( "Data Name" = c("a", "b"), Obs = c("1/3", "2/6"), @@ -1866,7 +1866,7 @@ testthat::describe("srv_teal summary table", { ) session$flushReact() testthat::expect_identical( - module_output_table(output, "module_1"), + module_summary_table(output, "module_1"), data.frame( "Data Name" = c("a", "b"), Obs = c("1/3", "2/6"), @@ -1906,7 +1906,7 @@ testthat::describe("srv_teal summary table", { session$setInputs("teal_modules-active_tab" = "module_1") session$flushReact() testthat::expect_identical( - module_output_table(output, "module_1"), + module_summary_table(output, "module_1"), data.frame( "Data Name" = c("iris", "new_dataset"), Obs = c("150/150", "3"), @@ -1936,7 +1936,7 @@ testthat::describe("srv_teal summary table", { session$setInputs("teal_modules-active_tab" = "module_1") session$flushReact() testthat::expect_identical( - module_output_table(output, "module_1"), + module_summary_table(output, "module_1"), data.frame( "Data Name" = c("iris"), Obs = c("6/150"), @@ -1961,7 +1961,7 @@ testthat::describe("srv_teal summary table", { session$setInputs("teal_modules-active_tab" = "module_1") session$flushReact() testthat::expect_identical( - module_output_table(output, "module_1"), + module_summary_table(output, "module_1"), data.frame( "Data Name" = c("iris"), Obs = c("150/150"), @@ -1978,7 +1978,6 @@ testthat::describe("srv_teal summary table", { teal.data::join_keys(data) <- teal.data::join_keys( teal.data::join_key("parent", "child", keys = c("am")) ) - shiny::testServer( app = srv_teal, args = list( @@ -1990,7 +1989,7 @@ testthat::describe("srv_teal summary table", { session$setInputs("teal_modules-active_tab" = "module_1") session$flushReact() testthat::expect_identical( - module_output_table(output, "module_1")[["Data Name"]], + module_summary_table(output, "module_1")[["Data Name"]], c("parent", "child") ) } @@ -2010,7 +2009,7 @@ testthat::describe("srv_teal summary table", { session$setInputs("teal_modules-active_tab" = "module_1") session$flushReact() testthat::expect_identical( - module_output_table(output, "module_1"), + module_summary_table(output, "module_1"), data.frame( "Data Name" = c("iris"), Obs = c("150/150"), diff --git a/tests/testthat/test-shinytest2-show-rcode.R b/tests/testthat/test-shinytest2-show-rcode.R index 0e6be7c128..79ef05ca56 100644 --- a/tests/testthat/test-shinytest2-show-rcode.R +++ b/tests/testthat/test-shinytest2-show-rcode.R @@ -45,7 +45,7 @@ testthat::test_that("e2e: teal app initializes with Show R Code modal", { r_code <- app$get_text(app$active_module_element("rcode-verbatim_content")) testthat::expect_match(r_code, "iris <- iris", fixed = TRUE) - testthat::expect_match(r_code, "iris._raw_ <- iris", fixed = TRUE) + testthat::expect_match(r_code, ".iris_raw <- iris", fixed = TRUE) testthat::expect_match(r_code, "stopifnot(rlang::hash(", fixed = TRUE) app$stop() From 97d9081b1d5c3e18793b9e14d05c98f86a6e9d19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Ka=C5=82=C4=99dkowski?= <6959016+gogonzo@users.noreply.github.com> Date: Tue, 17 Sep 2024 07:48:49 +0200 Subject: [PATCH 2/8] Update R/teal_data_utils.R MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Marcin <133694481+m7pr@users.noreply.github.com> Signed-off-by: Dawid Kałędkowski <6959016+gogonzo@users.noreply.github.com> --- R/teal_data_utils.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/teal_data_utils.R b/R/teal_data_utils.R index f3dff781f1..c20d2d2e7a 100644 --- a/R/teal_data_utils.R +++ b/R/teal_data_utils.R @@ -47,7 +47,7 @@ NULL datanames_corrected, intersect( sprintf(".%s_raw", datanames_corrected), - ls(data@env, all.names = TRUE) + ls(teal.code::get_env(data), all.names = TRUE) ) ) From 8034952af201257cb5fd2c11918576241b92177a Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 07:55:17 +0200 Subject: [PATCH 3/8] fix news --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 9ccd3a7cb2..a652c75dca 100644 --- a/NEWS.md +++ b/NEWS.md @@ -22,7 +22,7 @@ * Easier way of to call `javascript` events by setting `$(document).ready(function() { ... })`. #1114 * Provided progress bar for modules loading and data filtering during teal app startup. * Filter mapping display has a separate icon in the tab. -* Environment of the `data` passed to `teal_module`'s server contains unfiltered datasets names `._raw` +* Environment of the `data` passed to the `teal_module`'s server contains unfiltered datasets named following this convention: `._raw` # teal 0.15.2 From 6816cebe3f3904655b4f31cbec4ef13343b59440 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 08:28:13 +0200 Subject: [PATCH 4/8] fix a call to not attempt setting inexisting datanames --- R/teal_data_utils.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/teal_data_utils.R b/R/teal_data_utils.R index c20d2d2e7a..6fe5e42625 100644 --- a/R/teal_data_utils.R +++ b/R/teal_data_utils.R @@ -71,7 +71,7 @@ NULL ) ) new_data@verified <- data@verified - teal.data::datanames(new_data) <- datanames + teal.data::datanames(new_data) <- datanames_corrected new_data } From 6dbb9fba0eedded1b6736b5fc465675fed439aa5 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 22:25:57 +0200 Subject: [PATCH 5/8] @ruckip proposal --- R/module_data_summary.R | 8 +-- R/module_filter_data.R | 10 ++- R/teal_data_utils.R | 15 ++--- tests/testthat/test-module_teal.R | 74 +++++++++++---------- tests/testthat/test-shinytest2-show-rcode.R | 17 +++-- 5 files changed, 65 insertions(+), 59 deletions(-) diff --git a/R/module_data_summary.R b/R/module_data_summary.R index 80adb84d2e..f9c6beaeae 100644 --- a/R/module_data_summary.R +++ b/R/module_data_summary.R @@ -62,7 +62,6 @@ srv_data_summary <- function(id, teal_data) { summary_table <- reactive({ req(inherits(teal_data(), "teal_data")) - if (!length(.teal_data_ls(teal_data()))) { return(NULL) } @@ -141,16 +140,13 @@ srv_data_summary <- function(id, teal_data) { get_filter_overview <- function(teal_data) { datanames <- teal.data::datanames(teal_data()) joinkeys <- teal.data::join_keys(teal_data()) + filtered_data_objs <- sapply( datanames, function(name) teal.code::get_env(teal_data())[[name]], simplify = FALSE ) - unfiltered_data_objs <- sapply( - datanames, - function(name) teal.code::get_env(teal_data())[[sprintf(".%s_raw", name)]], - simplify = FALSE - ) + unfiltered_data_objs <- teal.code::get_env(teal_data())[[".raw_data"]] rows <- lapply( datanames, diff --git a/R/module_filter_data.R b/R/module_filter_data.R index ad87b1193c..c9ea56ddae 100644 --- a/R/module_filter_data.R +++ b/R/module_filter_data.R @@ -52,7 +52,15 @@ srv_filter_data <- function(id, datasets, active_datanames, data_rv, is_active) #' @rdname module_filter_data .make_filtered_teal_data <- function(modules, data, datasets = NULL, datanames) { - data <- eval_code(data, sprintf(".%1$s_raw <- %1$s", datanames)) + data <- eval_code( + data, + paste0( + ".raw_data <- list2env(list(", + toString(sprintf("%1$s = %1$s", datanames)), + "))\n", + "lockEnvironment(.raw_data) #@linksto .raw_data" # this is environment and it is shared by qenvs. CAN'T MODIFY! + ) + ) filtered_code <- teal.slice::get_filter_expr(datasets = datasets, datanames = datanames) filtered_teal_data <- .append_evaluated_code(data, filtered_code) filtered_datasets <- sapply(datanames, function(x) datasets$get_data(x, filtered = TRUE), simplify = FALSE) diff --git a/R/teal_data_utils.R b/R/teal_data_utils.R index 6fe5e42625..1ecac9c3f6 100644 --- a/R/teal_data_utils.R +++ b/R/teal_data_utils.R @@ -33,7 +33,7 @@ NULL checkmate::assert_class(data, "teal_data") checkmate::assert_class(objects, "list") new_env <- list2env(objects, parent = .GlobalEnv) - rlang::env_coalesce(new_env, data@env) + rlang::env_coalesce(new_env, teal.code::get_env(data)) data@env <- new_env data } @@ -43,14 +43,7 @@ NULL checkmate::assert_class(data, "teal_data") checkmate::assert_class(datanames, "character") datanames_corrected <- intersect(datanames, .teal_data_ls(data)) - dataname_corrected_with_raw <- c( - datanames_corrected, - intersect( - sprintf(".%s_raw", datanames_corrected), - ls(teal.code::get_env(data), all.names = TRUE) - ) - ) - + datanames_corrected_with_raw <- c(datanames_corrected, ".raw_data") if (!length(datanames)) { return(teal_data()) } @@ -58,12 +51,12 @@ NULL new_data <- do.call( teal.data::teal_data, args = c( - mget(x = dataname_corrected_with_raw, envir = data@env), + mget(x = datanames_corrected_with_raw, envir = teal.code::get_env(data)), list( code = gsub( "warning('Code was not verified for reproducibility.')\n", "", - teal.data::get_code(data, datanames = dataname_corrected_with_raw), + teal.data::get_code(data, datanames = datanames_corrected_with_raw), fixed = TRUE ), join_keys = teal.data::join_keys(data)[datanames_corrected] diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index 204b8416c8..57bf393b46 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -403,22 +403,10 @@ testthat::describe("srv_teal teal_modules", { ), expr = { testthat::expect_null(modules_output$module_1()) - session$setInputs(`data-teal_data_module-data-dataset` = "iris", `teal_modules-active_tab` = "module_1") - testthat::expect_setequal( - ls(teal.code::get_env(modules_output$module_1()()), all.names = TRUE), - c("iris", ".iris_raw") - ) - - # comment: can't trigger reactivity in testServer - the change in a reactive input data - # is not propagated to the teal_module(data). Instead we test if the modified data - # is sent to another teal_module + testthat::expect_setequal(ls(teal.code::get_env(modules_output$module_1()())), "iris") session$setInputs(`data-teal_data_module-data-dataset` = "mtcars", `teal_modules-active_tab` = "module_2") - session$flushReact() - testthat::expect_setequal( - ls(teal.code::get_env(modules_output$module_2()()), all.names = TRUE), - c("mtcars", ".mtcars_raw") - ) + testthat::expect_setequal(ls(teal.code::get_env(modules_output$module_2()())), "mtcars") } ) }) @@ -524,7 +512,7 @@ testthat::describe("srv_teal teal_modules", { ) }) - testthat::it("receives all objects from @env excluding ._raw_ when module$datanames = \"all\"", { + testthat::it("receives all objects from @env when module$datanames = \"all\"", { shiny::testServer( app = srv_teal, args = list( @@ -646,6 +634,34 @@ testthat::describe("srv_teal teal_modules", { ) }) + testthat::it("receives all raw datasets based on module$datanames", { + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = reactive({ + td <- within(teal_data(), { + iris <- iris + mtcars <- mtcars + swiss <- swiss + }) + td + }), + modules = modules( + module( + label = "module_1", + server = function(id, data) data, + datanames = c("iris", "swiss") + ) + ) + ), + expr = { + session$setInputs(`teal_modules-active_tab` = "module_1") + testthat::expect_setequal(ls(modules_output$module_1()()[[".raw_data"]]), c("iris", "swiss")) + } + ) + }) + testthat::it("combines datanames from transform/module $datanames", { shiny::testServer( app = srv_teal, @@ -1228,20 +1244,17 @@ testthat::describe("srv_teal filters", { session$flushReact() # iris is not active testthat::expect_identical(modules_output$module_1()()[["iris"]], iris) - testthat::expect_identical(modules_output$module_1()()[[".iris_raw"]], iris) # mtcars has been modified expected_mtcars <- subset(mtcars, cyl == 4) testthat::expect_identical(modules_output$module_1()()[["mtcars"]], expected_mtcars) - testthat::expect_identical(modules_output$module_1()()[[".mtcars_raw"]], mtcars) - expected_code <- paste0( c( "iris <- iris", "mtcars <- mtcars", sprintf('stopifnot(rlang::hash(iris) == "%s")', rlang::hash(iris)), sprintf('stopifnot(rlang::hash(mtcars) == "%s")', rlang::hash(mtcars)), - ".iris_raw <- iris", - ".mtcars_raw <- mtcars", + ".raw_data <- list2env(list(iris = iris, mtcars = mtcars))", + "lockEnvironment(.raw_data)", "mtcars <- dplyr::filter(mtcars, cyl == 4)" ), collapse = "\n" @@ -1363,7 +1376,7 @@ testthat::describe("srv_teal data reload", { }) testthat::describe("srv_teal teal_module(s) transformer", { - testthat::it("evaluates custom qenv call and pass update teal_data to the module", { + testthat::it("evaluates custom qenv call and pass updated teal_data to the module", { shiny::testServer( app = srv_teal, args = list( @@ -1380,9 +1393,7 @@ testthat::describe("srv_teal teal_module(s) transformer", { expr = { session$setInputs(`teal_modules-active_tab` = "module_1") testthat::expect_identical(modules_output$module_1()()[["iris"]], head(iris)) - testthat::expect_identical(modules_output$module_1()()[[".iris_raw"]], iris) testthat::expect_identical(modules_output$module_1()()[["mtcars"]], head(mtcars)) - testthat::expect_identical(modules_output$module_1()()[[".mtcars_raw"]], mtcars) } ) }) @@ -1410,22 +1421,18 @@ testthat::describe("srv_teal teal_module(s) transformer", { ), expr = { session$setInputs(`teal_modules-active_tab` = "module_1") - expected_iris <- subset(iris, Species == "versicolor") rownames(expected_iris) <- NULL expected_iris <- head(expected_iris) testthat::expect_identical(modules_output$module_1()()[["iris"]], expected_iris) - testthat::expect_identical(modules_output$module_1()()[[".iris_raw"]], iris) testthat::expect_identical(modules_output$module_1()()[["mtcars"]], head(subset(mtcars, cyl == 6))) - testthat::expect_identical(modules_output$module_1()()[[".mtcars_raw"]], mtcars) - expected_code <- paste(collapse = "\n", c( "iris <- iris", "mtcars <- mtcars", sprintf('stopifnot(rlang::hash(iris) == "%s")', rlang::hash(iris)), sprintf('stopifnot(rlang::hash(mtcars) == "%s")', rlang::hash(mtcars)), - ".iris_raw <- iris", - ".mtcars_raw <- mtcars", + ".raw_data <- list2env(list(iris = iris, mtcars = mtcars))", + "lockEnvironment(.raw_data)", 'iris <- dplyr::filter(iris, Species == "versicolor")', "mtcars <- dplyr::filter(mtcars, cyl == 6)", "iris <- head(iris, n = 6)", @@ -1464,17 +1471,14 @@ testthat::describe("srv_teal teal_module(s) transformer", { session$flushReact() testthat::expect_identical(modules_output$module_1()()[["iris"]], head(iris)) - testthat::expect_identical(modules_output$module_1()()[[".iris_raw"]], iris) testthat::expect_identical(modules_output$module_1()()[["mtcars"]], head(subset(mtcars, cyl == 4))) - testthat::expect_identical(modules_output$module_1()()[[".mtcars_raw"]], mtcars) - expected_code <- paste(collapse = "\n", c( "iris <- iris", "mtcars <- mtcars", sprintf('stopifnot(rlang::hash(iris) == "%s")', rlang::hash(iris)), sprintf('stopifnot(rlang::hash(mtcars) == "%s")', rlang::hash(mtcars)), - ".iris_raw <- iris", - ".mtcars_raw <- mtcars", + ".raw_data <- list2env(list(iris = iris, mtcars = mtcars))", + "lockEnvironment(.raw_data)", "mtcars <- dplyr::filter(mtcars, cyl == 4)", "iris <- head(iris, n = 6)", "mtcars <- head(mtcars, n = 6)" @@ -1582,7 +1586,6 @@ testthat::describe("srv_teal teal_module(s) transformer", { expr = { session$setInputs(`teal_modules-active_tab` = "module_1") testthat::expect_identical(modules_output$module_1()()[["iris"]], iris) - testthat::expect_identical(modules_output$module_1()()[[".iris_raw"]], iris) } ) }) @@ -1611,7 +1614,6 @@ testthat::describe("srv_teal teal_module(s) transformer", { expr = { session$setInputs(`teal_modules-active_tab` = "module_1") testthat::expect_identical(modules_output$module_1()()[["iris"]], iris) - testthat::expect_identical(modules_output$module_1()()[[".iris_raw"]], iris) } ) }) diff --git a/tests/testthat/test-shinytest2-show-rcode.R b/tests/testthat/test-shinytest2-show-rcode.R index 79ef05ca56..20901cd270 100644 --- a/tests/testthat/test-shinytest2-show-rcode.R +++ b/tests/testthat/test-shinytest2-show-rcode.R @@ -42,11 +42,18 @@ testthat::test_that("e2e: teal app initializes with Show R Code modal", { ) # Check R code output. - r_code <- app$get_text(app$active_module_element("rcode-verbatim_content")) - - testthat::expect_match(r_code, "iris <- iris", fixed = TRUE) - testthat::expect_match(r_code, ".iris_raw <- iris", fixed = TRUE) - testthat::expect_match(r_code, "stopifnot(rlang::hash(", fixed = TRUE) + testthat::expect_identical( + app$get_text(app$active_module_element("rcode-verbatim_content")), + paste( + "iris <- iris", + "mtcars <- mtcars", + sprintf('stopifnot(rlang::hash(iris) == "%s")', rlang::hash(iris)), + sprintf('stopifnot(rlang::hash(mtcars) == "%s")', rlang::hash(mtcars)), + ".raw_data <- list2env(list(iris = iris, mtcars = mtcars))", + "lockEnvironment(.raw_data)", + sep = "\n" + ) + ) app$stop() }) From e68519e28efc423a56db29863e005bce40211a4c Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 22:33:16 +0200 Subject: [PATCH 6/8] fix news --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index a652c75dca..123ccfb06b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -22,7 +22,7 @@ * Easier way of to call `javascript` events by setting `$(document).ready(function() { ... })`. #1114 * Provided progress bar for modules loading and data filtering during teal app startup. * Filter mapping display has a separate icon in the tab. -* Environment of the `data` passed to the `teal_module`'s server contains unfiltered datasets named following this convention: `._raw` +* Environment of the `data` passed to the `teal_module`'s server consists unfiltered datasets contained in `.raw_data`. # teal 0.15.2 From 97f0c3ca7d11d3f06f021139063e4ba428c0dd84 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 19:09:11 +0200 Subject: [PATCH 7/8] decouple ls from parent inclusion --- R/dummy_functions.R | 5 +---- R/init.R | 6 +++--- R/module_data_summary.R | 7 +++---- R/module_init_data.R | 6 +++--- R/module_nested_tabs.R | 6 +++--- R/module_teal_data.R | 5 ++++- R/teal_data_utils.R | 13 ++----------- R/utils.R | 19 ++++++++++++++----- man/dot-get_hashes_code.Rd | 2 +- man/teal_data_to_filtered_data.Rd | 2 +- man/teal_data_utilities.Rd | 3 --- 11 files changed, 35 insertions(+), 39 deletions(-) diff --git a/R/dummy_functions.R b/R/dummy_functions.R index 7e6765dfbc..fd3f067699 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -20,10 +20,7 @@ example_module <- function(label = "example teal module", datanames = "all", tra server = function(id, data) { checkmate::assert_class(isolate(data()), "teal_data") moduleServer(id, function(input, output, session) { - datanames_rv <- reactive({ - .teal_data_ls(req(data())) - }) - + datanames_rv <- reactive(ls(teal.code::get_env((req(data()))))) observeEvent(datanames_rv(), { selected <- input$dataname if (identical(selected, "")) { diff --git a/R/init.R b/R/init.R index ca5820e0a0..e6cb66953b 100644 --- a/R/init.R +++ b/R/init.R @@ -207,16 +207,16 @@ init <- function(data, ## `data` - `modules` if (inherits(data, "teal_data")) { - if (length(.teal_data_ls(data)) == 0) { + if (length(ls(teal.code::get_env(data))) == 0) { stop("The environment of `data` is empty.") } - is_modules_ok <- check_modules_datanames(modules, .teal_data_ls(data)) + is_modules_ok <- check_modules_datanames(modules, ls(teal.code::get_env(data))) if (!isTRUE(is_modules_ok) && length(unlist(extract_transformers(modules))) == 0) { lapply(is_modules_ok$string, warning, call. = FALSE) } - is_filter_ok <- check_filter_datanames(filter, .teal_data_ls(data)) + is_filter_ok <- check_filter_datanames(filter, ls(teal.code::get_env(data))) if (!isTRUE(is_filter_ok)) { warning(is_filter_ok) # we allow app to continue if applied filters are outside diff --git a/R/module_data_summary.R b/R/module_data_summary.R index f9c6beaeae..e793c53f70 100644 --- a/R/module_data_summary.R +++ b/R/module_data_summary.R @@ -62,7 +62,7 @@ srv_data_summary <- function(id, teal_data) { summary_table <- reactive({ req(inherits(teal_data(), "teal_data")) - if (!length(.teal_data_ls(teal_data()))) { + if (!length(ls(teal.code::get_env(teal_data())))) { return(NULL) } @@ -143,16 +143,15 @@ get_filter_overview <- function(teal_data) { filtered_data_objs <- sapply( datanames, - function(name) teal.code::get_env(teal_data())[[name]], + function(name) teal.code::get_var(teal_data(), name), simplify = FALSE ) - unfiltered_data_objs <- teal.code::get_env(teal_data())[[".raw_data"]] + unfiltered_data_objs <- teal.code::get_var(teal_data(), ".raw_data") rows <- lapply( datanames, function(dataname) { parent <- teal.data::parent(joinkeys, dataname) - # todo: what should we display for a parent dataset? # - Obs and Subjects # - Obs only diff --git a/R/module_init_data.R b/R/module_init_data.R index 824f4bf0cc..e79909a1f9 100644 --- a/R/module_init_data.R +++ b/R/module_init_data.R @@ -107,7 +107,7 @@ srv_init_data <- function(id, data, modules, filter = teal_slices()) { ) } - is_filter_ok <- check_filter_datanames(filter, .teal_data_ls(data_validated())) + is_filter_ok <- check_filter_datanames(filter, ls(teal.code::get_env(data_validated()))) if (!isTRUE(is_filter_ok)) { showNotification( "Some filters were not applied because of incompatibility with data. Contact app developer.", @@ -154,7 +154,7 @@ srv_init_data <- function(id, data, modules, filter = teal_slices()) { list(code = trimws(c(teal.code::get_code(data), hashes), which = "right")), list(join_keys = teal.data::join_keys(data)), sapply( - .teal_data_ls(data), + ls(teal.code::get_env(data)), teal.code::get_var, object = data, simplify = FALSE @@ -174,7 +174,7 @@ srv_init_data <- function(id, data, modules, filter = teal_slices()) { #' @return A character vector with the code lines. #' @keywords internal #' -.get_hashes_code <- function(data, datanames = .teal_data_ls(data)) { +.get_hashes_code <- function(data, datanames = ls(teal.code::get_env(data))) { vapply( datanames, function(dataname, datasets) { diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index 7bc3a72fbf..76ead65b33 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -301,11 +301,11 @@ srv_teal_module.teal_module <- function(id, .resolve_module_datanames <- function(data, modules) { stopifnot("data_rv must be teal_data object." = inherits(data, "teal_data")) if (is.null(modules$datanames) || identical(modules$datanames, "all")) { - .teal_data_ls(data) + .topologically_sort_datanames(ls(teal.code::get_env(data)), teal.data::join_keys(data)) } else { intersect( - include_parent_datanames(modules$datanames, teal.data::join_keys(data)), - .teal_data_ls(data) + .include_parent_datanames(modules$datanames, teal.data::join_keys(data)), + ls(teal.code::get_env(data)) ) } } diff --git a/R/module_teal_data.R b/R/module_teal_data.R index f1c6087575..943f635136 100644 --- a/R/module_teal_data.R +++ b/R/module_teal_data.R @@ -161,7 +161,10 @@ srv_validate_reactive_teal_data <- function(id, # nolint: object_length output$shiny_warnings <- renderUI({ if (inherits(data_out_r(), "teal_data")) { - is_modules_ok <- check_modules_datanames(modules = modules, datanames = .teal_data_ls(data_validated())) + is_modules_ok <- check_modules_datanames( + modules = modules, + datanames = ls(teal.code::get_env(data_validated())) + ) if (!isTRUE(is_modules_ok)) { tags$div( is_modules_ok$html( diff --git a/R/teal_data_utils.R b/R/teal_data_utils.R index 1ecac9c3f6..f02b3d4bcf 100644 --- a/R/teal_data_utils.R +++ b/R/teal_data_utils.R @@ -42,9 +42,9 @@ NULL .subset_teal_data <- function(data, datanames) { checkmate::assert_class(data, "teal_data") checkmate::assert_class(datanames, "character") - datanames_corrected <- intersect(datanames, .teal_data_ls(data)) + datanames_corrected <- intersect(datanames, ls(teal.code::get_env(data))) datanames_corrected_with_raw <- c(datanames_corrected, ".raw_data") - if (!length(datanames)) { + if (!length(datanames_corrected)) { return(teal_data()) } @@ -67,12 +67,3 @@ NULL teal.data::datanames(new_data) <- datanames_corrected new_data } - -#' @rdname teal_data_utilities -.teal_data_ls <- function(data) { - datanames <- ls( - teal.code::get_env(data), - all.names = FALSE # doesn't consider objects prefixed by `.` as datanames (including filtered datanames) - ) - include_parent_datanames(datanames, teal.data::join_keys(data)) # for topological sort -} diff --git a/R/utils.R b/R/utils.R index 36cd81f564..d2bedef4e3 100644 --- a/R/utils.R +++ b/R/utils.R @@ -44,20 +44,28 @@ get_teal_bs_theme <- function() { #' Return parentnames along with datanames. #' @noRd #' @keywords internal -include_parent_datanames <- function(dataname, join_keys) { - ordered_datanames <- dataname - for (i in dataname) { +.include_parent_datanames <- function(datanames, join_keys) { + ordered_datanames <- datanames + for (i in datanames) { parents <- character(0) while (length(i) > 0) { parent_i <- teal.data::parent(join_keys, i) parents <- c(parent_i, parents) i <- parent_i } - ordered_datanames <- c(parents, dataname, ordered_datanames) + ordered_datanames <- c(parents, ordered_datanames) } unique(ordered_datanames) } +#' Return topologicaly sorted datanames +#' @noRd +#' @keywords internal +.topologically_sort_datanames <- function(datanames, join_keys) { + datanames_with_parents <- .include_parent_datanames(datanames, join_keys) + intersect(datanames, datanames_with_parents) +} + #' Create a `FilteredData` #' #' Create a `FilteredData` object from a `teal_data` object. @@ -66,7 +74,7 @@ include_parent_datanames <- function(dataname, join_keys) { #' @param datanames (`character`) vector of data set names to include; must be subset of `datanames(x)` #' @return A `FilteredData` object. #' @keywords internal -teal_data_to_filtered_data <- function(x, datanames = .teal_data_ls(x)) { +teal_data_to_filtered_data <- function(x, datanames = ls(teal.code::get_env(x))) { checkmate::assert_class(x, "teal_data") checkmate::assert_character(datanames, min.chars = 1L, any.missing = FALSE) # Otherwise, FilteredData will be created in the modules' scope later @@ -79,6 +87,7 @@ teal_data_to_filtered_data <- function(x, datanames = .teal_data_ls(x)) { ) } + #' Template function for `TealReportCard` creation and customization #' #' This function generates a report card with a title, diff --git a/man/dot-get_hashes_code.Rd b/man/dot-get_hashes_code.Rd index 07280ef587..2b6d519312 100644 --- a/man/dot-get_hashes_code.Rd +++ b/man/dot-get_hashes_code.Rd @@ -4,7 +4,7 @@ \alias{.get_hashes_code} \title{Get code that tests the integrity of the reproducible data} \usage{ -.get_hashes_code(data, datanames = .teal_data_ls(data)) +.get_hashes_code(data, datanames = ls(teal.code::get_env(data))) } \arguments{ \item{data}{(\code{teal_data}) object holding the data} diff --git a/man/teal_data_to_filtered_data.Rd b/man/teal_data_to_filtered_data.Rd index 4d7d53e38e..d6eecd90cd 100644 --- a/man/teal_data_to_filtered_data.Rd +++ b/man/teal_data_to_filtered_data.Rd @@ -4,7 +4,7 @@ \alias{teal_data_to_filtered_data} \title{Create a \code{FilteredData}} \usage{ -teal_data_to_filtered_data(x, datanames = .teal_data_ls(x)) +teal_data_to_filtered_data(x, datanames = ls(teal.code::get_env(x))) } \arguments{ \item{x}{(\code{teal_data}) object} diff --git a/man/teal_data_utilities.Rd b/man/teal_data_utilities.Rd index a9fc37eb9a..07f850f124 100644 --- a/man/teal_data_utilities.Rd +++ b/man/teal_data_utilities.Rd @@ -5,7 +5,6 @@ \alias{.append_evaluated_code} \alias{.append_modified_data} \alias{.subset_teal_data} -\alias{.teal_data_ls} \title{\code{teal_data} utils} \usage{ .append_evaluated_code(data, code) @@ -13,8 +12,6 @@ .append_modified_data(data, objects) .subset_teal_data(data, datanames) - -.teal_data_ls(data) } \arguments{ \item{data}{(\code{teal_data})} From 89285ce14de090c0a3a9b215cb56f397afa901a3 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 26 Sep 2024 12:39:19 +0200 Subject: [PATCH 8/8] verification message --- R/teal_data_utils.R | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/R/teal_data_utils.R b/R/teal_data_utils.R index f02b3d4bcf..caedf21e21 100644 --- a/R/teal_data_utils.R +++ b/R/teal_data_utils.R @@ -53,12 +53,7 @@ NULL args = c( mget(x = datanames_corrected_with_raw, envir = teal.code::get_env(data)), list( - code = gsub( - "warning('Code was not verified for reproducibility.')\n", - "", - teal.data::get_code(data, datanames = datanames_corrected_with_raw), - fixed = TRUE - ), + code = teal.data::get_code(data, datanames = datanames_corrected_with_raw), join_keys = teal.data::join_keys(data)[datanames_corrected] ) )