From cae7d4ed14eee3b01ed8e27f51a2190ef41b8204 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Thu, 3 Oct 2024 15:09:39 +0200 Subject: [PATCH 01/23] Run GitHub Actions with `setup-r-dependencies` installation strategy - - - - Bye Bye `staged-dependencies` (#1361) Possible because of https://github.com/insightsengineering/r.pkg.template/pull/252/files --------- Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- .github/workflows/check.yaml | 32 +++++++++++++++++++++++++++++++ .github/workflows/docs.yaml | 8 ++++++++ .github/workflows/release.yaml | 33 ++++++++++++++++++++++++++++++++ .github/workflows/scheduled.yaml | 8 ++++++++ 4 files changed, 81 insertions(+) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index e5060049d9..03297d30fc 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -42,6 +42,14 @@ jobs: checking top-level files .* NOTE unit-test-report-brand: >- https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/thumbs/teal.png + deps-installation-method: setup-r-dependencies + lookup-refs: | + insightsengineering/teal.data + insightsengineering/teal.slice + insightsengineering/teal.code + insightsengineering/teal.logger + insightsengineering/teal.reporter + insightsengineering/teal.widgets r-cmd-non-cran: name: R CMD Check (non-CRAN) 🧬 uses: insightsengineering/r.pkg.template/.github/workflows/build-check-install.yaml@main @@ -63,6 +71,14 @@ jobs: checking Rd .usage sections .* NOTE checking for unstated dependencies in vignettes .* NOTE checking top-level files .* NOTE + deps-installation-method: setup-r-dependencies + lookup-refs: | + insightsengineering/teal.data + insightsengineering/teal.slice + insightsengineering/teal.code + insightsengineering/teal.logger + insightsengineering/teal.reporter + insightsengineering/teal.widgets coverage: name: Coverage 📔 uses: insightsengineering/r.pkg.template/.github/workflows/test-coverage.yaml@main @@ -71,6 +87,14 @@ jobs: with: additional-env-vars: | NOT_CRAN=true + deps-installation-method: setup-r-dependencies + lookup-refs: | + insightsengineering/teal.data + insightsengineering/teal.slice + insightsengineering/teal.code + insightsengineering/teal.logger + insightsengineering/teal.reporter + insightsengineering/teal.widgets linter: if: github.event_name != 'push' name: SuperLinter 🦸‍♀️ @@ -82,6 +106,14 @@ jobs: REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} with: auto-update: true + deps-installation-method: setup-r-dependencies + lookup-refs: | + insightsengineering/teal.data + insightsengineering/teal.slice + insightsengineering/teal.code + insightsengineering/teal.logger + insightsengineering/teal.reporter + insightsengineering/teal.widgets gitleaks: name: gitleaks 💧 uses: insightsengineering/r.pkg.template/.github/workflows/gitleaks.yaml@main diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 57ae800429..7c3e2f9617 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -42,3 +42,11 @@ jobs: with: default-landing-page: latest-tag additional-unit-test-report-directories: unit-test-report-non-cran + deps-installation-method: setup-r-dependencies + lookup-refs: | + insightsengineering/teal.data + insightsengineering/teal.slice + insightsengineering/teal.code + insightsengineering/teal.logger + insightsengineering/teal.reporter + insightsengineering/teal.widgets diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index aa3e7bb457..1f5cb83933 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -16,12 +16,29 @@ jobs: REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} with: default-landing-page: latest-tag + deps-installation-method: setup-r-dependencies + lookup-refs: | + insightsengineering/teal.data + insightsengineering/teal.slice + insightsengineering/teal.code + insightsengineering/teal.logger + insightsengineering/teal.reporter + insightsengineering/teal.widgets validation: name: R Package Validation report 📃 needs: release uses: insightsengineering/r.pkg.template/.github/workflows/validation.yaml@main secrets: REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} + with: + deps-installation-method: setup-r-dependencies + lookup-refs: | + insightsengineering/teal.data + insightsengineering/teal.slice + insightsengineering/teal.code + insightsengineering/teal.logger + insightsengineering/teal.reporter + insightsengineering/teal.widgets release: name: Create release 🎉 uses: insightsengineering/r.pkg.template/.github/workflows/release.yaml@main @@ -46,6 +63,14 @@ jobs: checking top-level files .* NOTE unit-test-report-brand: >- https://raw.githubusercontent.com/insightsengineering/hex-stickers/main/thumbs/teal.png + deps-installation-method: setup-r-dependencies + lookup-refs: | + insightsengineering/teal.data + insightsengineering/teal.slice + insightsengineering/teal.code + insightsengineering/teal.logger + insightsengineering/teal.reporter + insightsengineering/teal.widgets coverage: name: Coverage 📔 needs: [release, docs] @@ -55,6 +80,14 @@ jobs: with: additional-env-vars: | NOT_CRAN=true + deps-installation-method: setup-r-dependencies + lookup-refs: | + insightsengineering/teal.data + insightsengineering/teal.slice + insightsengineering/teal.code + insightsengineering/teal.logger + insightsengineering/teal.reporter + insightsengineering/teal.widgets wasm: name: Build WASM packages 🧑‍🏭 needs: release diff --git a/.github/workflows/scheduled.yaml b/.github/workflows/scheduled.yaml index c4233673e5..104b05f4b0 100644 --- a/.github/workflows/scheduled.yaml +++ b/.github/workflows/scheduled.yaml @@ -56,6 +56,14 @@ jobs: ) name: revdepcheck ↩️ uses: insightsengineering/r.pkg.template/.github/workflows/revdepcheck.yaml@main + with: + lookup-refs: | + insightsengineering/teal.data + insightsengineering/teal.slice + insightsengineering/teal.code + insightsengineering/teal.logger + insightsengineering/teal.reporter + insightsengineering/teal.widgets rhub: if: > github.event_name == 'schedule' || ( From 21673f858b0423235413eeca81efa6aab861d266 Mon Sep 17 00:00:00 2001 From: m7pr Date: Thu, 3 Oct 2024 13:10:37 +0000 Subject: [PATCH 02/23] [skip actions] Bump version to 0.15.2.9068 --- DESCRIPTION | 2 +- NEWS.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 0fbab7e2c9..f4b3e6746c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: teal Title: Exploratory Web Apps for Analyzing Clinical Trials Data -Version: 0.15.2.9067 +Version: 0.15.2.9068 Date: 2024-10-03 Authors@R: c( person("Dawid", "Kaledkowski", , "dawid.kaledkowski@roche.com", role = c("aut", "cre"), diff --git a/NEWS.md b/NEWS.md index 81ab605728..39c8a0a457 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# teal 0.15.2.9067 +# teal 0.15.2.9068 ### New features From 1e3eaf2215d6aa8e46a674866742d02af00985c9 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Fri, 4 Oct 2024 13:58:54 +0200 Subject: [PATCH 03/23] 1315 fix failed pipelines (#1337) Close #1315 --- DESCRIPTION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index f4b3e6746c..8c03872d75 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -61,10 +61,10 @@ Suggests: R6, renv (>= 1.0.7), rmarkdown (>= 2.23), - rvest, + rvest (>= 1.0.0), shinytest2, shinyvalidate, - testthat (>= 3.1.5), + testthat (>= 3.2.0), withr (>= 2.1.0), yaml (>= 1.1.0) VignetteBuilder: From e6db5cafbab56d8ac9c4dd55b2c1200f00e7a968 Mon Sep 17 00:00:00 2001 From: m7pr Date: Fri, 4 Oct 2024 11:59:48 +0000 Subject: [PATCH 04/23] [skip actions] Bump version to 0.15.2.9069 --- DESCRIPTION | 4 ++-- NEWS.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 8c03872d75..05ae82a051 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Type: Package Package: teal Title: Exploratory Web Apps for Analyzing Clinical Trials Data -Version: 0.15.2.9068 -Date: 2024-10-03 +Version: 0.15.2.9069 +Date: 2024-10-04 Authors@R: c( person("Dawid", "Kaledkowski", , "dawid.kaledkowski@roche.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-9533-457X")), diff --git a/NEWS.md b/NEWS.md index 39c8a0a457..f5490154ee 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# teal 0.15.2.9068 +# teal 0.15.2.9069 ### New features From b809259182d5622bd9f124a57b8ecc688c6cfacd Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Wed, 9 Oct 2024 12:59:53 +0200 Subject: [PATCH 05/23] 1362 increase minimal version of `renv` (#1372) Fixes #1362 Dependency Test in here https://github.com/insightsengineering/teal/actions/runs/11251792020 Max strategy is satisfied image --- DESCRIPTION | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 05ae82a051..43bc5c007f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -59,7 +59,7 @@ Suggests: mirai (>= 1.1.1), MultiAssayExperiment, R6, - renv (>= 1.0.7), + renv (>= 1.0.10.9000), rmarkdown (>= 2.23), rvest (>= 1.0.0), shinytest2, @@ -81,6 +81,7 @@ Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, rstudio/rmarkdown, tidyverse/rvest, rstudio/shinytest2, rstudio/shinyvalidate, r-lib/testthat, r-lib/withr, yaml=vubiostat/r-yaml +Remotes: rstudio/renv Config/Needs/website: insightsengineering/nesttemplate Encoding: UTF-8 Language: en-US From d82ce144da212d763971e6dc2bb6e5094b6d2b5b Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 9 Oct 2024 11:00:55 +0000 Subject: [PATCH 06/23] [skip actions] Bump version to 0.15.2.9070 --- .pre-commit-config.yaml | 2 +- DESCRIPTION | 7 ++++--- NEWS.md | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0952df50b1..b21cbeaed7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ --- # All available hooks: https://pre-commit.com/hooks.html # R specific hooks: https://github.com/lorenzwalthert/precommit -default_stages: [commit] +default_stages: [pre-commit] default_language_version: python: python3 repos: diff --git a/DESCRIPTION b/DESCRIPTION index 43bc5c007f..8c783d47ae 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Type: Package Package: teal Title: Exploratory Web Apps for Analyzing Clinical Trials Data -Version: 0.15.2.9069 -Date: 2024-10-04 +Version: 0.15.2.9070 +Date: 2024-10-09 Authors@R: c( person("Dawid", "Kaledkowski", , "dawid.kaledkowski@roche.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-9533-457X")), @@ -71,6 +71,8 @@ VignetteBuilder: knitr RdMacros: lifecycle +Remotes: + rstudio/renv Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, insightsengineering/teal.slice, mllg/checkmate, jeroen/jsonlite, r-lib/lifecycle, daroczig/logger, shikokuchuo/mirai, @@ -81,7 +83,6 @@ Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, rstudio/rmarkdown, tidyverse/rvest, rstudio/shinytest2, rstudio/shinyvalidate, r-lib/testthat, r-lib/withr, yaml=vubiostat/r-yaml -Remotes: rstudio/renv Config/Needs/website: insightsengineering/nesttemplate Encoding: UTF-8 Language: en-US diff --git a/NEWS.md b/NEWS.md index f5490154ee..22c0ef9ed8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# teal 0.15.2.9069 +# teal 0.15.2.9070 ### New features From 68be75569f7168d9b16792d31f6bf12e89492327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Mon, 14 Oct 2024 10:30:19 +0200 Subject: [PATCH 07/23] Delays transform modules reactivity until tab is active (#1373) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Pull Request Fixes #1303 ### Changes description - [x] Unifying function for delayed trigger of module and transformations - [x] Filter manager crash when clicked with an app that has module specific filters - [x] Fix bug detected when app is called with `teal_data_module` - One of my testing apps is failing (see below) - [x] Add tests ### Topics to discuss - **Functionality change**: this PR will delay the first module reactivity execution until data is pulled from `teal_data_module`
Sample app for bug ```R options( teal.log_level = "INFO", teal.show_js_log = TRUE, # teal.bs_theme = bslib::bs_theme(version = 5), shiny.bookmarkStore = "server" ) # pkgload::load_all("../teal.data") # pkgload::load_all("../teal.slice") pkgload::load_all("../teal") my_transformers <- list( teal_transform_module( label = "Keep first 6 from IRIS", ui = function(id) { ns <- NS(id) div( checkboxInput(ns("check"), label = "Toggle `head(iris)`"), ) }, server = function(id, data) { moduleServer(id, function(input, output, session) { eventReactive(input$check, { print("Check triggered") req(data()) if (input$check) { within(data(), iris <- head(iris, 6)) } else { data() } }) }) } ) ) data <- teal::teal_data_module( ui = function(id) { ns <- shiny::NS(id) shiny::tagList( shiny::tags$head( shiny::tags$style(shiny::HTML(" .teal-data-module { border: 1px solid rgba(0, 0, 0, .5); border-radius: 4px; padding: 1em; margin: .2em; } .teal-data-module .shiny-options-group { display: flex; flex-wrap: wrap; column-gap: 1em; } .teal-data-module .shiny-options-group .checkbox { margin-top: 1em; margin-bottom: 0; } ")) ), shiny::tags$h2("Data Module"), shiny::div( class = "teal-data-module", shiny::checkboxGroupInput( ns("datasets"), "Datasets", choices = c("ADSL", "ADTTE", "iris", "CO2", "miniACC"), selected = c("ADSL", "ADTTE", "iris", "CO2") ), shiny::actionButton(ns("submit"), label = "Submit") ) ) }, server = function(id, ...) { shiny::moduleServer(id, function(input, output, session) { code <- list( ADSL = expression(ADSL <- teal.data::rADSL), ADTTE = expression({ ADTTE <- teal.data::rADTTE ADTTE$CNSRL <- as.logical(ADTTE$CNSR) }), iris = expression(iris <- iris), CO2 = expression({ CO2 <- CO2 factors <- names(Filter(isTRUE, vapply(CO2, is.factor, logical(1L)))) CO2[factors] <- lapply(CO2[factors], as.character) }), miniACC = expression({ data( "miniACC", package = "MultiAssayExperiment", envir = environment(), overwrite = TRUE ) miniACC <- miniACC }) ) datasets <- reactive(input$datasets) shiny::eventReactive(input$submit, { code_to_eval <- do.call(c, code[datasets()]) data <- teal.code::eval_code(teal.data::teal_data(), code_to_eval) join_keys(data) <- default_cdisc_join_keys[datasets()] teal.data::datanames(data) <- datasets() data }) }) }, once = FALSE ) teal::init( data = data, modules = teal::modules( teal::example_module(label = "A", datanames = NULL, transformers = my_transformers), teal::example_module(label = "B", transformers = my_transformers) ), filter = teal::teal_slices( # # FilterRange teal.slice::teal_slice("ADSL", "AGE", selected = c(18L, 65L)), # # FilterExpr teal_slice( dataname = "ADSL", id = "Female adults", expr = "SEX == 'F' & AGE >= 18", title = "Female adults" ), # # FilterDatetime teal_slice( dataname = "ADTTE", varname = "ADTM", id = "Analysis DTM", selected = c("2019-03-25 07:06:18", "2020-01-22 15:03:58"), title = "Female adults" ), # # FilterDate with LSTALVDT teal_slice( dataname = "ADSL", varname = "LSTALVDT", id = "Last Alive Date", selected = c("2022-02-14", "2022-11-24"), title = "Last Alive Date" ), # FilterEmpty # FilterLogical with CNSRL teal_slice( dataname = "ADTTE", varname = "CNSRL", id = "Censored", selected = TRUE, title = "Censored" ), module_specific = FALSE, teal.slice::teal_slice("ADSL", "SEX") ), title = "yada" ) |> shiny::runApp() ```
--------- Signed-off-by: André Veríssimo <211358+averissimo@users.noreply.github.com> Co-authored-by: m7pr Co-authored-by: Marcin <133694481+m7pr@users.noreply.github.com> Co-authored-by: 27856297+dependabot-preview[bot]@users.noreply.github.com <27856297+dependabot-preview[bot]@users.noreply.github.com> Co-authored-by: Dawid Kałędkowski --- R/module_nested_tabs.R | 174 ++++++++++++------- R/teal_data_module.R | 16 ++ inst/WORDLIST | 19 +- man/call_once_when.Rd | 44 +++++ man/teal_transform_module.Rd | 6 +- tests/testthat/test-module_teal.R | 80 ++++++++- vignettes/data-transform-as-shiny-module.Rmd | 4 + 7 files changed, 262 insertions(+), 81 deletions(-) create mode 100644 man/call_once_when.Rd diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index cae7d9acca..c51e6138ec 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -214,7 +214,11 @@ srv_teal_module.teal_modules <- function(id, datasets = datasets, slices_global = slices_global, reporter = reporter, - is_active = reactive(is_active() && input$active_tab == module_id) + is_active = reactive( + is_active() && + input$active_tab == module_id && + identical(data_load_status(), "ok") + ) ) }, simplify = FALSE @@ -236,6 +240,8 @@ srv_teal_module.teal_module <- function(id, is_active = reactive(TRUE)) { logger::log_debug("srv_teal_module.teal_module initializing the module: { deparse1(modules$label) }.") moduleServer(id = id, module = function(input, output, session) { + module_out <- reactiveVal() + active_datanames <- reactive({ .resolve_module_datanames(data = data_rv(), modules = modules) }) @@ -253,77 +259,77 @@ srv_teal_module.teal_module <- function(id, # Because available_teal_slices is used in FilteredData$srv_available_slices (via srv_filter_panel) # and if it is not set, then it won't be available in the srv_filter_panel srv_module_filter_manager(modules$label, module_fd = datasets, slices_global = slices_global) - filtered_teal_data <- srv_filter_data( - "filter_panel", - datasets = datasets, - active_datanames = active_datanames, - data_rv = data_rv, - is_active = is_active - ) - is_transformer_failed <- reactiveValues() - transformed_teal_data <- srv_transform_data( - "data_transform", - data = filtered_teal_data, - transforms = modules$transformers, - modules = modules, - is_transformer_failed = is_transformer_failed - ) - any_transformer_failed <- reactive({ - any(unlist(reactiveValuesToList(is_transformer_failed))) - }) - observeEvent(any_transformer_failed(), { - if (isTRUE(any_transformer_failed())) { - shinyjs::hide("teal_module_ui") - shinyjs::hide("validate_datanames") - shinyjs::show("transformer_failure_info") - } else { - shinyjs::show("teal_module_ui") - shinyjs::show("validate_datanames") - shinyjs::hide("transformer_failure_info") - } - }) + call_once_when(is_active(), { + filtered_teal_data <- srv_filter_data( + "filter_panel", + datasets = datasets, + active_datanames = active_datanames, + data_rv = data_rv, + is_active = is_active + ) + is_transformer_failed <- reactiveValues() + transformed_teal_data <- srv_transform_data( + "data_transform", + data = filtered_teal_data, + transforms = modules$transformers, + modules = modules, + is_transformer_failed = is_transformer_failed + ) + any_transformer_failed <- reactive({ + any(unlist(reactiveValuesToList(is_transformer_failed))) + }) - module_teal_data <- reactive({ - req(inherits(transformed_teal_data(), "teal_data")) - all_teal_data <- transformed_teal_data() - module_datanames <- .resolve_module_datanames(data = all_teal_data, modules = modules) - .subset_teal_data(all_teal_data, module_datanames) - }) + observeEvent(any_transformer_failed(), { + if (isTRUE(any_transformer_failed())) { + shinyjs::hide("teal_module_ui") + shinyjs::hide("validate_datanames") + shinyjs::show("transformer_failure_info") + } else { + shinyjs::show("teal_module_ui") + shinyjs::show("validate_datanames") + shinyjs::hide("transformer_failure_info") + } + }) - srv_validate_reactive_teal_data( - "validate_datanames", - data = module_teal_data, - modules = modules - ) + module_teal_data <- reactive({ + req(inherits(transformed_teal_data(), "teal_data")) + all_teal_data <- transformed_teal_data() + module_datanames <- .resolve_module_datanames(data = all_teal_data, modules = modules) + .subset_teal_data(all_teal_data, module_datanames) + }) - summary_table <- srv_data_summary("data_summary", module_teal_data) - - # Call modules. - module_out <- reactiveVal(NULL) - if (!inherits(modules, "teal_module_previewer")) { - obs_module <- observeEvent( - # wait for module_teal_data() to be not NULL but only once: - ignoreNULL = TRUE, - once = TRUE, - eventExpr = module_teal_data(), - handlerExpr = { - module_out(.call_teal_module(modules, datasets, module_teal_data, reporter)) - } + srv_validate_reactive_teal_data( + "validate_datanames", + data = module_teal_data, + modules = modules ) - } else { - # Report previewer must be initiated on app start for report cards to be included in bookmarks. - # When previewer is delayed, cards are bookmarked only if previewer has been initiated (visited). - module_out(.call_teal_module(modules, datasets, module_teal_data, reporter)) - } - # todo: (feature request) add a ReporterCard to the reporter as an output from the teal_module - # how to determine if module returns a ReporterCard so that reportPreviewer is needed? - # Should we insertUI of the ReportPreviewer then? - # What about attr(module, "reportable") - similar to attr(module, "bookmarkable") - if ("report" %in% names(module_out)) { - # (reactively) add card to the reporter - } + summary_table <- srv_data_summary("data_summary", module_teal_data) + + # Call modules. + if (!inherits(modules, "teal_module_previewer")) { + obs_module <- call_once_when( + !is.null(module_teal_data()), + ignoreNULL = TRUE, + handlerExpr = { + module_out(.call_teal_module(modules, datasets, module_teal_data, reporter)) + } + ) + } else { + # Report previewer must be initiated on app start for report cards to be included in bookmarks. + # When previewer is delayed, cards are bookmarked only if previewer has been initiated (visited). + module_out(.call_teal_module(modules, datasets, module_teal_data, reporter)) + } + + # todo: (feature request) add a ReporterCard to the reporter as an output from the teal_module + # how to determine if module returns a ReporterCard so that reportPreviewer is needed? + # Should we insertUI of the ReportPreviewer then? + # What about attr(module, "reportable") - similar to attr(module, "bookmarkable") + if ("report" %in% names(module_out)) { + # (reactively) add card to the reporter + } + }) module_out }) @@ -368,3 +374,39 @@ srv_teal_module.teal_module <- function(id, ) } } + +#' Calls expression when condition is met +#' +#' Function postpones `handlerExpr` to the moment when `eventExpr` (condition) returns `TRUE`, +#' otherwise nothing happens. +#' @param eventExpr A (quoted or unquoted) logical expression that represents the event; +#' this can be a simple reactive value like input$click, a call to a reactive expression +#' like dataset(), or even a complex expression inside curly braces. +#' @param ... additional arguments passed to `observeEvent` with the exception of `eventExpr` that is not allowed. +#' @inheritParams shiny::observeEvent +#' +#' @return An observer. +#' +#' @keywords internal +call_once_when <- function(eventExpr, # nolint: object_name. + handlerExpr, # nolint: object_name. + event.env = parent.frame(), # nolint: object_name. + handler.env = parent.frame(), # nolint: object_name. + ...) { + event_quo <- rlang::new_quosure(substitute(eventExpr), env = event.env) + handler_quo <- rlang::new_quosure(substitute(handlerExpr), env = handler.env) + + # When `condExpr` is TRUE, then `handlerExpr` is evaluated once. + activator <- reactive({ + if (isTRUE(rlang::eval_tidy(event_quo))) { + TRUE + } + }) + + observeEvent( + eventExpr = activator(), + once = TRUE, + handlerExpr = rlang::eval_tidy(handler_quo), + ... + ) +} diff --git a/R/teal_data_module.R b/R/teal_data_module.R index 8051b604b4..5017be1432 100644 --- a/R/teal_data_module.R +++ b/R/teal_data_module.R @@ -104,6 +104,10 @@ teal_data_module <- function(ui, server, label = "data module", once = TRUE) { #' `shiny` module server function; that takes `id` and `data` argument, #' where the `id` is the module id and `data` is the reactive `teal_data` input. #' The server function must return reactive expression containing `teal_data` object. +#' +#' The server function definition should not use `eventReactive` as it may lead to +#' unexpected behavior. +#' See `vignettes("data-transform-as-shiny-module")` for more information. #' @param datanames (`character`) #' Names of the datasets that are relevant for the module. The #' filter panel will only display filters for specified `datanames`. The keyword `"all"` will show @@ -149,6 +153,18 @@ teal_transform_module <- function(ui = function(id) NULL, ui = ui, server = function(id, data) { data_out <- server(id, data) + + if (inherits(data_out, "reactive.event")) { + # This warning message partially detects when `eventReactive` is used in `data_module`. + warning( + "teal_transform_module() ", + "Using eventReactive in teal_transform module server code should be avoided as it ", + "may lead to unexpected behavior. See the vignettes for more information ", + "(`vignette(\"data-transform-as-shiny-module\", package = \"teal\")`).", + call. = FALSE + ) + } + decorate_err_msg( assert_reactive(data_out), pre = sprintf("From: 'teal_transform_module()':\nA 'teal_transform_module' with \"%s\" label:", label), diff --git a/inst/WORDLIST b/inst/WORDLIST index cf9153ad3f..5e1ef7cab6 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -1,28 +1,29 @@ Biomarker -CDISC -Forkers -Hoffmann -MAEs -ORCID -Reproducibility -TLG -UI -UX bookmarkable +CDISC cloneable customizable favicon favicons +Forkers funder +Hoffmann lockfile +MAEs omics +ORCID pre programmatically +quosure reactively repo +Reproducibility reproducibility summarization tabset themer theming +TLG +UI uncheck +UX diff --git a/man/call_once_when.Rd b/man/call_once_when.Rd new file mode 100644 index 0000000000..fbc196a4f0 --- /dev/null +++ b/man/call_once_when.Rd @@ -0,0 +1,44 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/module_nested_tabs.R +\name{call_once_when} +\alias{call_once_when} +\title{Calls expression when condition is met} +\usage{ +call_once_when( + eventExpr, + handlerExpr, + event.env = parent.frame(), + handler.env = parent.frame(), + ... +) +} +\arguments{ +\item{eventExpr}{A (quoted or unquoted) logical expression that represents the event; +this can be a simple reactive value like input$click, a call to a reactive expression +like dataset(), or even a complex expression inside curly braces.} + +\item{handlerExpr}{The expression to call whenever \code{eventExpr} is +invalidated. This should be a side-effect-producing action (the return +value will be ignored). It will be executed within an \code{\link[shiny:isolate]{isolate()}} +scope.} + +\item{event.env}{The parent environment for the reactive expression. By default, +this is the calling environment, the same as when defining an ordinary +non-reactive expression. If \code{eventExpr} is a quosure and \code{event.quoted} is \code{TRUE}, +then \code{event.env} is ignored.} + +\item{handler.env}{The parent environment for the reactive expression. By default, +this is the calling environment, the same as when defining an ordinary +non-reactive expression. If \code{handlerExpr} is a quosure and \code{handler.quoted} is \code{TRUE}, +then \code{handler.env} is ignored.} + +\item{...}{additional arguments passed to \code{observeEvent} with the exception of \code{eventExpr} that is not allowed.} +} +\value{ +An observer. +} +\description{ +Function postpones \code{handlerExpr} to the moment when \code{eventExpr} (condition) returns \code{TRUE}, +otherwise nothing happens. +} +\keyword{internal} diff --git a/man/teal_transform_module.Rd b/man/teal_transform_module.Rd index 0f424b329f..e64e0ca25f 100644 --- a/man/teal_transform_module.Rd +++ b/man/teal_transform_module.Rd @@ -18,7 +18,11 @@ teal_transform_module( \item{server}{(\verb{function(id, data)}) \code{shiny} module server function; that takes \code{id} and \code{data} argument, where the \code{id} is the module id and \code{data} is the reactive \code{teal_data} input. -The server function must return reactive expression containing \code{teal_data} object.} +The server function must return reactive expression containing \code{teal_data} object. + +The server function definition should not use \code{eventReactive} as it may lead to +unexpected behavior. +See \code{vignettes("data-transform-as-shiny-module")} for more information.} \item{label}{(\code{character(1)}) Label of the module.} diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index e863a1325f..e18c975e13 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -302,6 +302,36 @@ testthat::describe("srv_teal teal_modules", { ) }) + testthat::it("are called only after teal_data_module is resolved", { + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = teal_data_module( + ui = function(id) actionButton("submit", "click me"), + server = function(id) { + moduleServer(id, function(input, output, session) { + eventReactive(input$submit, teal_data(iris = iris)) + }) + } + ), + modules = modules( + module("module_1", server = function(id, data) 101L) + ) + ), + expr = { + session$setInputs(`teal_modules-active_tab` = "module_1") + session$flushReact() + testthat::expect_null(modules_output$module_1()) + + + session$setInputs("data-teal_data_module-submit" = "1") + session$flushReact() + testthat::expect_identical(modules_output$module_1(), 101L) + } + ) + }) + testthat::it("are called with data argument being `teal_data`", { shiny::testServer( app = srv_teal, @@ -1587,8 +1617,8 @@ testthat::describe("srv_teal teal_module(s) transformer", { ) }) - testthat::it("fails when transformer doesn't return reactive", { - testthat::expect_error( + testthat::it("throws warning when transformer return reactive.event", { + testthat::expect_warning( testServer( app = srv_teal, args = list( @@ -1599,14 +1629,54 @@ testthat::describe("srv_teal teal_module(s) transformer", { server = function(id, data) data, transformers = list( teal_transform_module( - ui = function(id) NULL, - server = function(id, data) "whatever" + ui = function(id) textInput("a", "an input"), + server = function(id, data) eventReactive(input$a, data()) ) ) ) ) ), - expr = {} + expr = { + session$setInputs("teal_modules-active_tab" = "module") + session$flushReact() + } + ), + "Using eventReactive in teal_transform module server code should be avoided" + ) + }) + + testthat::it("fails when transformer doesn't return reactive", { + testthat::expect_warning( + # error decorator is mocked to avoid showing the trace error during the + # test. + # This tests works without the mocking, but it's more verbose. + testthat::with_mocked_bindings( + testServer( + app = srv_teal, + args = list( + id = "test", + data = teal.data::teal_data(iris = iris), + modules = modules( + module( + server = function(id, data) data, + transformers = list( + teal_transform_module( + ui = function(id) NULL, + server = function(id, data) "whatever" + ) + ) + ) + ) + ), + expr = { + session$setInputs("teal_modules-active_tab" = "module") + session$flushReact() + } + ), + decorate_err_msg = function(x, ...) { + testthat::expect_error(x, "Must be a reactive") + warning(tryCatch(x, error = function(e) e$message)) + }, ), "Must be a reactive" ) diff --git a/vignettes/data-transform-as-shiny-module.Rmd b/vignettes/data-transform-as-shiny-module.Rmd index 306dd3217c..854b507b66 100644 --- a/vignettes/data-transform-as-shiny-module.Rmd +++ b/vignettes/data-transform-as-shiny-module.Rmd @@ -94,6 +94,10 @@ if (interactive()) { } ``` +_Note_: It is recommended to return `reactive()` with `teal_data()` in `server` code of a `teal_transform_module` as this is more robust for maintaining the reactivity of Shiny. +If you are planning on using `eventReactive()` in the server, the event should include `data()` _(example `eventReactive(list(input$a, data()), {...})`)_. +More in [this discussion](https://github.com/insightsengineering/teal/issues/1303#issuecomment-2286239832). + ### Multiple Transformers Note that we can add multiple `teal` transformers by including `teal_transform_module` in a list. From 97a8758d0645e72104621eaaf7c26c24cd93c208 Mon Sep 17 00:00:00 2001 From: averissimo Date: Mon, 14 Oct 2024 08:31:13 +0000 Subject: [PATCH 08/23] [skip actions] Bump version to 0.15.2.9071 --- DESCRIPTION | 4 ++-- NEWS.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 8c783d47ae..8df6334aac 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Type: Package Package: teal Title: Exploratory Web Apps for Analyzing Clinical Trials Data -Version: 0.15.2.9070 -Date: 2024-10-09 +Version: 0.15.2.9071 +Date: 2024-10-14 Authors@R: c( person("Dawid", "Kaledkowski", , "dawid.kaledkowski@roche.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-9533-457X")), diff --git a/NEWS.md b/NEWS.md index 22c0ef9ed8..0e5649fa94 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# teal 0.15.2.9070 +# teal 0.15.2.9071 ### New features From 9c183f8c474698e20ec5842299cc8e189c28e7ee Mon Sep 17 00:00:00 2001 From: averissimo Date: Mon, 14 Oct 2024 08:32:11 +0000 Subject: [PATCH 09/23] [skip ci] Update WORDLIST --- inst/WORDLIST | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/inst/WORDLIST b/inst/WORDLIST index 5e1ef7cab6..5bb4a5bf45 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -1,29 +1,29 @@ Biomarker -bookmarkable CDISC +Forkers +Hoffmann +MAEs +ORCID +Reproducibility +TLG +UI +UX +bookmarkable cloneable customizable favicon favicons -Forkers funder -Hoffmann lockfile -MAEs omics -ORCID pre programmatically quosure reactively repo -Reproducibility reproducibility summarization tabset themer theming -TLG -UI uncheck -UX From a3829c4093b40c6152cf1285dfa9506efcb28b8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Ka=C5=82=C4=99dkowski?= Date: Thu, 17 Oct 2024 14:59:22 +0200 Subject: [PATCH 10/23] forbid "all" in teal_transform_module (#1381) Closes #1347 Additionally, changed `transforms` to `transformers` in the `ui/srv_transform_data()` documentation as there was a param-name mismatch with `module()` --- R/module_nested_tabs.R | 6 +- R/module_transform_data.R | 30 ++++---- R/modules.R | 57 ++++++++------- R/teal_data_module.R | 14 ++-- man/example_module.Rd | 19 ++--- man/module_transform_data.Rd | 8 ++- man/teal_modules.Rd | 58 +++++++++------- man/teal_transform_module.Rd | 7 +- tests/testthat/test-modules.R | 20 ------ tests/testthat/test-teal_data_module.R | 8 +++ vignettes/data-transform-as-shiny-module.Rmd | 73 -------------------- 11 files changed, 119 insertions(+), 181 deletions(-) diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R index c51e6138ec..deb83cc59f 100644 --- a/R/module_nested_tabs.R +++ b/R/module_nested_tabs.R @@ -125,9 +125,7 @@ ui_teal_module.teal_module <- function(id, modules, depth = 0L) { width = 3, ui_data_summary(ns("data_summary")), ui_filter_data(ns("filter_panel")), - if (length(modules$transformers) > 0 && !isTRUE(attr(modules$transformers, "custom_ui"))) { - ui_transform_data(ns("data_transform"), transforms = modules$transformers, class = "well") - }, + ui_transform_data(ns("data_transform"), transformers = modules$transformers, class = "well"), class = "teal_secondary_col" ) ) @@ -272,7 +270,7 @@ srv_teal_module.teal_module <- function(id, transformed_teal_data <- srv_transform_data( "data_transform", data = filtered_teal_data, - transforms = modules$transformers, + transformers = modules$transformers, modules = modules, is_transformer_failed = is_transformer_failed ) diff --git a/R/module_transform_data.R b/R/module_transform_data.R index 418c81d06d..1d3607b4f3 100644 --- a/R/module_transform_data.R +++ b/R/module_transform_data.R @@ -13,18 +13,19 @@ NULL #' @rdname module_transform_data -ui_transform_data <- function(id, transforms, class = "well") { +ui_transform_data <- function(id, transformers = list(), class = "well") { checkmate::assert_string(id) - checkmate::assert_list(transforms, "teal_transform_module", null.ok = TRUE) + checkmate::assert_list(transformers, "teal_transform_module") + ns <- NS(id) - labels <- lapply(transforms, function(x) attr(x, "label")) + labels <- lapply(transformers, function(x) attr(x, "label")) ids <- get_unique_labels(labels) - names(transforms) <- ids + names(transformers) <- ids lapply( - names(transforms), + names(transformers), function(name) { - data_mod <- transforms[[name]] + data_mod <- transformers[[name]] wrapper_id <- ns(sprintf("wrapper_%s", name)) div( # todo: accordion? # class .teal_validated changes the color of the boarder on error in ui_validate_reactive_teal_data @@ -44,7 +45,7 @@ ui_transform_data <- function(id, transforms, class = "well") { ), div( id = wrapper_id, - ui_teal_data(id = ns(name), data_module = transforms[[name]]$ui) + ui_teal_data(id = ns(name), data_module = transformers[[name]]$ui) ) ) } @@ -52,29 +53,26 @@ ui_transform_data <- function(id, transforms, class = "well") { } #' @rdname module_transform_data -srv_transform_data <- function(id, data, transforms, modules, is_transformer_failed = reactiveValues()) { +srv_transform_data <- function(id, data, transformers = list(), modules, is_transformer_failed = reactiveValues()) { checkmate::assert_string(id) assert_reactive(data) - checkmate::assert_list(transforms, "teal_transform_module", null.ok = TRUE) + checkmate::assert_list(transformers, "teal_transform_module") checkmate::assert_class(modules, "teal_module") - if (length(transforms) == 0L) { - return(data) - } - labels <- lapply(transforms, function(x) attr(x, "label")) + labels <- lapply(transformers, function(x) attr(x, "label")) ids <- get_unique_labels(labels) - names(transforms) <- ids + names(transformers) <- ids moduleServer(id, function(input, output, session) { logger::log_debug("srv_teal_data_modules initializing.") Reduce( function(previous_result, name) { srv_teal_data( id = name, - data_module = function(id) transforms[[name]]$server(id, previous_result), + data_module = function(id) transformers[[name]]$server(id, previous_result), modules = modules, is_transformer_failed = is_transformer_failed ) }, - x = names(transforms), + x = names(transformers), init = data ) }) diff --git a/R/modules.R b/R/modules.R index 76dea53d68..59ad3e5d97 100644 --- a/R/modules.R +++ b/R/modules.R @@ -20,6 +20,29 @@ setOldClass("teal_modules") #' because they are used by the `mapping` argument of [teal_slices()] #' and the report previewer module [reporter_previewer_module()], respectively. #' +#' # Restricting datasets used by `teal_module`: +#' The `datanames` argument controls which datasets are used by the module’s server. These datasets, +#' passed via server's `data` argument, are the only ones shown in the module's tab. +#' +#' When `datanames` is set to `"all"`, all datasets in the data object are treated as relevant. +#' However, this may include unnecessary datasets, such as: +#' - Proxy variables for column modifications +#' - Temporary datasets used to create final versions +#' - Connection objects +#' +#' To exclude irrelevant datasets, use the [set_datanames()] function to change `datanames` from +#' `"all"` to specific names. Trying to modify non-`"all"` values with [set_datanames()] will result +#' in a warning. Datasets with names starting with . are ignored globally unless explicitly listed +#' in `datanames`. +#' +#' # `datanames` with `transformers` +#' When transformers are specified, their `datanames` are added to the module’s `datanames`, which +#' changes the behavior as follows: +#' - If `module(datanames)` is `NULL` and the `transformers` have defined `datanames`, the sidebar +#' will appear showing the `transformers`' datasets, instead of being hidden. +#' - If `module(datanames)` is set to specific values and any `transformer` has `datanames = "all"`, +#' the module may receive extra datasets that could be unnecessary +#' #' @param label (`character(1)`) Label shown in the navigation item for the module or module group. #' For `modules()` defaults to `"root"`. See `Details`. #' @param server (`function`) `shiny` module with following arguments: @@ -42,40 +65,24 @@ setOldClass("teal_modules") #' - `...` (optional) When provided, `ui_args` elements will be passed to the module named argument #' or to the `...`. #' @param filters (`character`) Deprecated. Use `datanames` instead. -#' @param datanames (`character`) Names of the datasets that are relevant for the item. -#' The keyword `"all"` provides all datasets available in `data` passed to `teal` application. -#' `NULL` will hide the filter panel. +#' @param datanames (`character`) Names of the datasets relevant to the item. +#' There are 2 reserved values that have specific behaviors: +#' - The keyword `"all"` includes all datasets available in the data passed to the teal application. +#' - `NULL` hides the sidebar panel completely. +#' - If `transformers` are specified, their `datanames` are automatically added to this `datanames` +#' argument. #' @param server_args (named `list`) with additional arguments passed on to the server function. #' @param ui_args (named `list`) with additional arguments passed on to the UI function. #' @param x (`teal_module` or `teal_modules`) Object to format/print. #' @param indent (`integer(1)`) Indention level; each nested element is indented one level more. #' @param transformers (`list` of `teal_data_module`) that will be applied to transform the data. -#' Each transform module UI will appear in the `teal` application, unless the `custom_ui` attribute is set on the list. -#' If so, the module developer is responsible to display the UI in the module itself. `datanames` of the `transformers` -#' will be added to the `datanames`. +#' Each transform module UI will appear in the `teal`'s sidebar panel. +#' Transformers' `datanames` are added to the `datanames`. See [teal_transform_module()]. #' -#' When the transformation does not have sufficient input data, the resulting data will fallback -#' to the last successful transform or, in case there are none, to the filtered data. #' @param ... #' - For `modules()`: (`teal_module` or `teal_modules`) Objects to wrap into a tab. #' - For `format()` and `print()`: Arguments passed to other methods. #' -#' @section `datanames`: -#' The module's `datanames` argument determines a subset of datasets from the `data` object, as specified in the -#' server function argument, to be presented in the module. Datasets displayed in the filter panel will be limited -#' to this subset. -#' When `datanames` is set to `"all"`, all available datasets in the `data` object are considered relevant for the -#' module. However, setting `datanames` argument to `"all"` might include datasets that are irrelevant for the module, -#' for example: -#' - Proxy variables used for modifying columns. -#' - Modified copies of datasets used to create a final dataset. -#' - Connection objects. -#' To prevent these irrelevant datasets from appearing in the module, use the [set_datanames()] function on the -#' [module] or [modules()] to change the `datanames` from `"all"` to specific dataset names. Attempting to change -#' `datanames` values that was not set to `"all"` using [set_datanames()] will be ignored with a warning. -#' -#' Additionally, datasets with names starting with `.` are ignored when `datanames` is set to `"all"`. -#' #' @return #' `module()` returns an object of class `teal_module`. #' @@ -264,7 +271,7 @@ module <- function(label = "module", } checkmate::assert_list(transformers, types = "teal_transform_module") transformer_datanames <- unlist(lapply(transformers, attr, "datanames")) - combined_datanames <- if (identical(datanames, "all") || any(sapply(transformer_datanames, identical, "all"))) { + combined_datanames <- if (identical(datanames, "all")) { "all" } else { union(datanames, transformer_datanames) diff --git a/R/teal_data_module.R b/R/teal_data_module.R index 5017be1432..99dec6bc01 100644 --- a/R/teal_data_module.R +++ b/R/teal_data_module.R @@ -109,9 +109,8 @@ teal_data_module <- function(ui, server, label = "data module", once = TRUE) { #' unexpected behavior. #' See `vignettes("data-transform-as-shiny-module")` for more information. #' @param datanames (`character`) -#' Names of the datasets that are relevant for the module. The -#' filter panel will only display filters for specified `datanames`. The keyword `"all"` will show -#' filters of all datasets. `datanames` will be automatically appended to the [modules()] `datanames`. +#' Names of the datasets that are relevant for this module to evaluate. If set to `character(0)` +#' then module would receive [modules()] `datanames`. #' @examples #' my_transformers <- list( #' teal_transform_module( @@ -144,10 +143,17 @@ teal_data_module <- function(ui, server, label = "data module", once = TRUE) { teal_transform_module <- function(ui = function(id) NULL, server = function(id, data) data, label = "transform module", - datanames = "all") { + datanames = character(0)) { checkmate::assert_function(ui, args = "id", nargs = 1) checkmate::assert_function(server, args = c("id", "data"), nargs = 2) checkmate::assert_string(label) + checkmate::assert_character(datanames) + if (identical(datanames, "all")) { + stop( + "teal_transform_module can't have datanames property equal to 'all'. Set `datanames = character(0)` instead.", + call. = FALSE + ) + } structure( list( ui = ui, diff --git a/man/example_module.Rd b/man/example_module.Rd index 26558f8650..7f59bc01b6 100644 --- a/man/example_module.Rd +++ b/man/example_module.Rd @@ -14,17 +14,18 @@ example_module( \item{label}{(\code{character(1)}) Label shown in the navigation item for the module or module group. For \code{modules()} defaults to \code{"root"}. See \code{Details}.} -\item{datanames}{(\code{character}) Names of the datasets that are relevant for the item. -The keyword \code{"all"} provides all datasets available in \code{data} passed to \code{teal} application. -\code{NULL} will hide the filter panel.} +\item{datanames}{(\code{character}) Names of the datasets relevant to the item. +There are 2 reserved values that have specific behaviors: +\itemize{ +\item The keyword \code{"all"} includes all datasets available in the data passed to the teal application. +\item \code{NULL} hides the sidebar panel completely. +\item If \code{transformers} are specified, their \code{datanames} are automatically added to this \code{datanames} +argument. +}} \item{transformers}{(\code{list} of \code{teal_data_module}) that will be applied to transform the data. -Each transform module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the list. -If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the \code{transformers} -will be added to the \code{datanames}. - -When the transformation does not have sufficient input data, the resulting data will fallback -to the last successful transform or, in case there are none, to the filtered data.} +Each transform module UI will appear in the \code{teal}'s sidebar panel. +Transformers' \code{datanames} are added to the \code{datanames}. See \code{\link[=teal_transform_module]{teal_transform_module()}}.} } \value{ A \code{teal} module which can be included in the \code{modules} argument to \code{\link[=init]{init()}}. diff --git a/man/module_transform_data.Rd b/man/module_transform_data.Rd index 2a4a351062..5ae155632e 100644 --- a/man/module_transform_data.Rd +++ b/man/module_transform_data.Rd @@ -6,12 +6,12 @@ \alias{srv_transform_data} \title{Module to transform \code{reactive} \code{teal_data}} \usage{ -ui_transform_data(id, transforms, class = "well") +ui_transform_data(id, transformers = list(), class = "well") srv_transform_data( id, data, - transforms, + transformers = list(), modules, is_transformer_failed = reactiveValues() ) @@ -19,6 +19,10 @@ srv_transform_data( \arguments{ \item{id}{(\code{character(1)}) Module id} +\item{transformers}{(\code{list} of \code{teal_data_module}) that will be applied to transform the data. +Each transform module UI will appear in the \code{teal}'s sidebar panel. +Transformers' \code{datanames} are added to the \code{datanames}. See \code{\link[=teal_transform_module]{teal_transform_module()}}.} + \item{data}{(\verb{reactive teal_data})} \item{modules}{(\code{teal_modules} or \code{teal_module}) For \code{datanames} validation purpose} diff --git a/man/teal_modules.Rd b/man/teal_modules.Rd index 33865d976c..b763c01513 100644 --- a/man/teal_modules.Rd +++ b/man/teal_modules.Rd @@ -67,21 +67,22 @@ or to the \code{...}. \item{filters}{(\code{character}) Deprecated. Use \code{datanames} instead.} -\item{datanames}{(\code{character}) Names of the datasets that are relevant for the item. -The keyword \code{"all"} provides all datasets available in \code{data} passed to \code{teal} application. -\code{NULL} will hide the filter panel.} +\item{datanames}{(\code{character}) Names of the datasets relevant to the item. +There are 2 reserved values that have specific behaviors: +\itemize{ +\item The keyword \code{"all"} includes all datasets available in the data passed to the teal application. +\item \code{NULL} hides the sidebar panel completely. +\item If \code{transformers} are specified, their \code{datanames} are automatically added to this \code{datanames} +argument. +}} \item{server_args}{(named \code{list}) with additional arguments passed on to the server function.} \item{ui_args}{(named \code{list}) with additional arguments passed on to the UI function.} \item{transformers}{(\code{list} of \code{teal_data_module}) that will be applied to transform the data. -Each transform module UI will appear in the \code{teal} application, unless the \code{custom_ui} attribute is set on the list. -If so, the module developer is responsible to display the UI in the module itself. \code{datanames} of the \code{transformers} -will be added to the \code{datanames}. - -When the transformation does not have sufficient input data, the resulting data will fallback -to the last successful transform or, in case there are none, to the filtered data.} +Each transform module UI will appear in the \code{teal}'s sidebar panel. +Transformers' \code{datanames} are added to the \code{datanames}. See \code{\link[=teal_transform_module]{teal_transform_module()}}.} \item{...}{\itemize{ \item For \code{modules()}: (\code{teal_module} or \code{teal_modules}) Objects to wrap into a tab. @@ -121,24 +122,33 @@ The labels \code{"global_filters"} and \code{"Report previewer"} are reserved because they are used by the \code{mapping} argument of \code{\link[=teal_slices]{teal_slices()}} and the report previewer module \code{\link[=reporter_previewer_module]{reporter_previewer_module()}}, respectively. } -\section{\code{datanames}}{ - -The module's \code{datanames} argument determines a subset of datasets from the \code{data} object, as specified in the -server function argument, to be presented in the module. Datasets displayed in the filter panel will be limited -to this subset. -When \code{datanames} is set to \code{"all"}, all available datasets in the \code{data} object are considered relevant for the -module. However, setting \code{datanames} argument to \code{"all"} might include datasets that are irrelevant for the module, -for example: +\section{Restricting datasets used by \code{teal_module}:}{ +The \code{datanames} argument controls which datasets are used by the module’s server. These datasets, +passed via server's \code{data} argument, are the only ones shown in the module's tab. + +When \code{datanames} is set to \code{"all"}, all datasets in the data object are treated as relevant. +However, this may include unnecessary datasets, such as: \itemize{ -\item Proxy variables used for modifying columns. -\item Modified copies of datasets used to create a final dataset. -\item Connection objects. -To prevent these irrelevant datasets from appearing in the module, use the \code{\link[=set_datanames]{set_datanames()}} function on the -\link{module} or \code{\link[=modules]{modules()}} to change the \code{datanames} from \code{"all"} to specific dataset names. Attempting to change -\code{datanames} values that was not set to \code{"all"} using \code{\link[=set_datanames]{set_datanames()}} will be ignored with a warning. +\item Proxy variables for column modifications +\item Temporary datasets used to create final versions +\item Connection objects } -Additionally, datasets with names starting with \code{.} are ignored when \code{datanames} is set to \code{"all"}. +To exclude irrelevant datasets, use the \code{\link[=set_datanames]{set_datanames()}} function to change \code{datanames} from +\code{"all"} to specific names. Trying to modify non-\code{"all"} values with \code{\link[=set_datanames]{set_datanames()}} will result +in a warning. Datasets with names starting with . are ignored globally unless explicitly listed +in \code{datanames}. +} + +\section{\code{datanames} with \code{transformers}}{ +When transformers are specified, their \code{datanames} are added to the module’s \code{datanames}, which +changes the behavior as follows: +\itemize{ +\item If \code{module(datanames)} is \code{NULL} and the \code{transformers} have defined \code{datanames}, the sidebar +will appear showing the \code{transformers}' datasets, instead of being hidden. +\item If \code{module(datanames)} is set to specific values and any \code{transformer} has \code{datanames = "all"}, +the module may receive extra datasets that could be unnecessary +} } \examples{ diff --git a/man/teal_transform_module.Rd b/man/teal_transform_module.Rd index e64e0ca25f..931329ab6c 100644 --- a/man/teal_transform_module.Rd +++ b/man/teal_transform_module.Rd @@ -8,7 +8,7 @@ teal_transform_module( ui = function(id) NULL, server = function(id, data) data, label = "transform module", - datanames = "all" + datanames = character(0) ) } \arguments{ @@ -27,9 +27,8 @@ See \code{vignettes("data-transform-as-shiny-module")} for more information.} \item{label}{(\code{character(1)}) Label of the module.} \item{datanames}{(\code{character}) -Names of the datasets that are relevant for the module. The -filter panel will only display filters for specified \code{datanames}. The keyword \code{"all"} will show -filters of all datasets. \code{datanames} will be automatically appended to the \code{\link[=modules]{modules()}} \code{datanames}.} +Names of the datasets that are relevant for this module to evaluate. If set to \code{character(0)} +then module would receive \code{\link[=modules]{modules()}} \code{datanames}.} } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} diff --git a/tests/testthat/test-modules.R b/tests/testthat/test-modules.R index 594e801d45..9085f76462 100644 --- a/tests/testthat/test-modules.R +++ b/tests/testthat/test-modules.R @@ -534,26 +534,6 @@ testthat::test_that("module datanames is appended by its transformers datanames" testthat::expect_identical(out$datanames, c("c", "a", "b")) }) -testthat::test_that("module datanames is set to 'all' if any transformer $datanames is 'all'", { - transformer_w_datanames <- teal_transform_module( - ui = function(id) NULL, - server = function(id, data) { - moduleServer(id, function(input, output, session) { - reactive({ - new_data <- within(data(), { - new_dataset <- data.frame(a = 1:3, b = 4:6) - }) - new_data - }) - }) - }, - datanames = "all" - ) - - out <- module(datanames = "c", transformers = list(transformer_w_datanames)) - testthat::expect_identical(out$datanames, "all") -}) - testthat::test_that("module datanames stays 'all' regardless of transformers", { transformer_w_datanames <- teal_transform_module( ui = function(id) NULL, diff --git a/tests/testthat/test-teal_data_module.R b/tests/testthat/test-teal_data_module.R index d751cdc000..0361ea4ea4 100644 --- a/tests/testthat/test-teal_data_module.R +++ b/tests/testthat/test-teal_data_module.R @@ -22,3 +22,11 @@ testthat::test_that("teal_data_module throws when server has other formals than ".*formal arguments.*" ) }) + + +testthat::test_that("teal_transform_module doesn't accept datanames = 'all'", { + testthat::expect_error( + teal_transform_module(datanames = "all"), + "can't have datanames property equal to 'all'" + ) +}) diff --git a/vignettes/data-transform-as-shiny-module.Rmd b/vignettes/data-transform-as-shiny-module.Rmd index 854b507b66..94e3289a0b 100644 --- a/vignettes/data-transform-as-shiny-module.Rmd +++ b/vignettes/data-transform-as-shiny-module.Rmd @@ -164,76 +164,3 @@ if (interactive()) { shinyApp(app$ui, app$server) } ``` - -## Custom placement of the transform UI - -When a custom transformation is used, the UI for the transformation is placed below the filter panel. -However, there is a way to customize the placement of the UI inside the module content. - -In order to place the transformation UI inside the module there are few things one has to do: -1. Create a custom module wrapper function. -2. Call the desired module in the module wrapper function and store it in a variable so it's UI can be modified. -3. Modify the UI of the module with the transform UI at the desired location by calling the `ui_transform_data`. Note that in order for the transform to work you need to change the namespace of the `id` by passing `NS(gsub("-module$", "", id), "data_transform")`. -4. Set the `custom_ui` attribute of the `module$transformers` to `TRUE`. - -Now the custom module should embed the transformation UI inside the module content. - -Here is an example of a custom module wrapper function that modifies the `example_module` module. -```{r} -example_module_encoding <- function(label = "example module (on encoding)", datanames = "all", transformers = list()) { - mod <- example_module(label, datanames, transformers) - mod$ui <- function(id) { - ns <- NS(id) - teal.widgets::standard_layout( - output = verbatimTextOutput(ns("text")), - encoding = tags$div( - ui_transform_data(NS(gsub("-module$", "", id), "data_transform"), transformers), - selectInput(ns("dataname"), "Choose a dataset", choices = NULL), - teal.widgets::verbatim_popup_ui(ns("rcode"), "Show R code") - ) - ) - } - attr(mod$transformers, "custom_ui") <- TRUE - mod -} - -data <- within(teal_data(), { - iris <- iris - mtcars <- mtcars -}) -datanames(data) <- c("iris", "mtcars") - -my_transformers <- list( - teal_transform_module( - label = "Custom transform for iris", - ui = function(id) { - ns <- NS(id) - tags$div( - numericInput(ns("n_rows"), "Number of rows to subset", value = 6, min = 1, max = 150, step = 1) - ) - }, - server = function(id, data) { - moduleServer(id, function(input, output, session) { - reactive({ - within(data(), - { - iris <- head(iris, num_rows) - }, - num_rows = input$n_rows - ) - }) - }) - } - ) -) - -app <- init( - data = data, - modules = example_module_encoding(transformers = my_transformers) -) - -if (interactive()) { - shinyApp(app$ui, app$server) -} -``` - From 89e80289e420996e1ee6931f4de63130606eecf2 Mon Sep 17 00:00:00 2001 From: gogonzo Date: Thu, 17 Oct 2024 13:00:23 +0000 Subject: [PATCH 11/23] [skip actions] Bump version to 0.15.2.9072 --- DESCRIPTION | 4 ++-- NEWS.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 8df6334aac..e3a3f33d0b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Type: Package Package: teal Title: Exploratory Web Apps for Analyzing Clinical Trials Data -Version: 0.15.2.9071 -Date: 2024-10-14 +Version: 0.15.2.9072 +Date: 2024-10-17 Authors@R: c( person("Dawid", "Kaledkowski", , "dawid.kaledkowski@roche.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-9533-457X")), diff --git a/NEWS.md b/NEWS.md index 0e5649fa94..6d31436f80 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# teal 0.15.2.9071 +# teal 0.15.2.9072 ### New features From 4b79ef1cd5e1a711aa4ff9424c2bfc12c83a1ebf Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Tue, 22 Oct 2024 14:21:38 +0200 Subject: [PATCH 12/23] Use CRAN version of `renv` (#1392) Since `renv` was released on CRAN on Oct 12 https://cran.r-project.org/web/packages/renv/index.html with the newest fix for the bug that we reported in here https://github.com/rstudio/renv/issues/2007 we can just vbump `renv` and use CRAN version instead of GitHub version. Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- DESCRIPTION | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index e3a3f33d0b..c2500f5d69 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -59,7 +59,7 @@ Suggests: mirai (>= 1.1.1), MultiAssayExperiment, R6, - renv (>= 1.0.10.9000), + renv (>= 1.0.11), rmarkdown (>= 2.23), rvest (>= 1.0.0), shinytest2, @@ -71,8 +71,6 @@ VignetteBuilder: knitr RdMacros: lifecycle -Remotes: - rstudio/renv Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, insightsengineering/teal.slice, mllg/checkmate, jeroen/jsonlite, r-lib/lifecycle, daroczig/logger, shikokuchuo/mirai, From 2aba7241ff35ffd3c7aeb924ba88ffef4509f76b Mon Sep 17 00:00:00 2001 From: m7pr Date: Tue, 22 Oct 2024 12:22:46 +0000 Subject: [PATCH 13/23] [skip actions] Bump version to 0.15.2.9073 --- DESCRIPTION | 4 ++-- NEWS.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index c2500f5d69..c59b26a6ba 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Type: Package Package: teal Title: Exploratory Web Apps for Analyzing Clinical Trials Data -Version: 0.15.2.9072 -Date: 2024-10-17 +Version: 0.15.2.9073 +Date: 2024-10-22 Authors@R: c( person("Dawid", "Kaledkowski", , "dawid.kaledkowski@roche.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-9533-457X")), diff --git a/NEWS.md b/NEWS.md index 6d31436f80..744882b046 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# teal 0.15.2.9072 +# teal 0.15.2.9073 ### New features From 6895e1d7e0b4fd593b8703173176b9916b869882 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Wed, 23 Oct 2024 10:44:40 +0200 Subject: [PATCH 14/23] 210 substitute `teal.data::get_code` with `teal.code::get_code` (#1388) Partner to - https://github.com/insightsengineering/teal.data/pull/343 - https://github.com/insightsengineering/teal.code/pull/214 Replace `teal.data::get_code()` (that worked on `teal_data()`) with `teal.code::get_code()` (that works on `qenv`). --- DESCRIPTION | 4 ++-- R/module_filter_data.R | 2 +- R/teal_data_utils.R | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index c59b26a6ba..873192d20a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -37,7 +37,7 @@ BugReports: https://github.com/insightsengineering/teal/issues Depends: R (>= 4.0), shiny (>= 1.8.1), - teal.data (> 0.6.0.9007), + teal.data (>= 0.6.0.9014), teal.slice (>= 0.5.1.9009) Imports: checkmate (>= 2.1.0), @@ -48,7 +48,7 @@ Imports: rlang (>= 1.0.0), shinyjs, stats, - teal.code (>= 0.5.0), + teal.code (>= 0.5.0.9011), teal.logger (>= 0.2.0), teal.reporter (>= 0.3.1.9004), teal.widgets (>= 0.4.0), diff --git a/R/module_filter_data.R b/R/module_filter_data.R index 959fd867ee..cb60a08d5b 100644 --- a/R/module_filter_data.R +++ b/R/module_filter_data.R @@ -74,7 +74,7 @@ srv_filter_data <- function(id, datasets, active_datanames, data_rv, is_active) filter_changed <- reactive({ req(inherits(datasets(), "FilteredData")) new_signature <- c( - teal.data::get_code(data_rv()), + teal.code::get_code(data_rv()), .get_filter_expr(datasets = datasets(), datanames = active_datanames()) ) if (!identical(previous_signature(), new_signature)) { diff --git a/R/teal_data_utils.R b/R/teal_data_utils.R index caedf21e21..a92ee9cc05 100644 --- a/R/teal_data_utils.R +++ b/R/teal_data_utils.R @@ -53,7 +53,7 @@ NULL args = c( mget(x = datanames_corrected_with_raw, envir = teal.code::get_env(data)), list( - code = teal.data::get_code(data, datanames = datanames_corrected_with_raw), + code = teal.code::get_code(data, names = datanames_corrected_with_raw), join_keys = teal.data::join_keys(data)[datanames_corrected] ) ) From a1087d2d3ff0c62393c3d5277cd5f184d543e2d9 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 23 Oct 2024 08:45:33 +0000 Subject: [PATCH 15/23] [skip actions] Bump version to 0.15.2.9074 --- DESCRIPTION | 4 ++-- NEWS.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 873192d20a..54d716211f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Type: Package Package: teal Title: Exploratory Web Apps for Analyzing Clinical Trials Data -Version: 0.15.2.9073 -Date: 2024-10-22 +Version: 0.15.2.9074 +Date: 2024-10-23 Authors@R: c( person("Dawid", "Kaledkowski", , "dawid.kaledkowski@roche.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-9533-457X")), diff --git a/NEWS.md b/NEWS.md index 744882b046..247961954f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# teal 0.15.2.9073 +# teal 0.15.2.9074 ### New features From 147eef3d3235d8454ac20b31a571a61fba27a255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Fri, 25 Oct 2024 09:15:39 +0100 Subject: [PATCH 16/23] Allow non-standard datanames in teal .raw_data (#1382) wip # Pull Request Fixes #1366 ### Related: - https://github.com/insightsengineering/teal/pull/1382 - https://github.com/insightsengineering/teal.slice/pull/622 - https://github.com/insightsengineering/teal.data/pull/340 ### Changes description - `.raw_data` is created and supports non-standard R names, such as: - `%pipe%` - `assigns<-` - ... --- R/module_filter_data.R | 2 +- tests/testthat/test-module_teal.R | 105 ++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/R/module_filter_data.R b/R/module_filter_data.R index cb60a08d5b..efe4a53bd0 100644 --- a/R/module_filter_data.R +++ b/R/module_filter_data.R @@ -56,7 +56,7 @@ srv_filter_data <- function(id, datasets, active_datanames, data_rv, is_active) data, paste0( ".raw_data <- list2env(list(", - toString(sprintf("%1$s = %1$s", datanames)), + toString(sprintf("%1$s = %1$s", sapply(datanames, as.name))), "))\n", "lockEnvironment(.raw_data) #@linksto .raw_data" # this is environment and it is shared by qenvs. CAN'T MODIFY! ) diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index e18c975e13..b8878e5661 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -2216,3 +2216,108 @@ testthat::describe("srv_teal snapshot manager", { ) }) }) + +testthat::describe("Datanames with special symbols", { + testthat::it("are detected as datanames when defined as 'all'", { + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = teal.data::teal_data( + iris = iris, + `%a_pipe%` = function(lhs, rhs) paste(lhs, rhs) + ), + modules = modules(module("module_1", server = function(id, data) data)), + filter = teal_slices( + module_specific = TRUE + ) + ), + expr = { + session$setInputs("teal_modules-active_tab" = "module_1") + session$flushReact() + + testthat::expect_setequal( + ls( + teal.code::get_env(modules_output$module_1()()), + all.names = TRUE + ), + c(".raw_data", "iris", "%a_pipe%") + ) + } + ) + }) + + testthat::it("are present in datanames when used in pre-processing code", { + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = within( + teal.data::teal_data(), + { + iris <- iris + mtcars <- mtcars + `_a variable with spaces_` <- "new_column" # nolint: object_name. + iris <- cbind(iris, data.frame(`_a variable with spaces_`)) + } + ), + modules = modules( + module("module_1", server = function(id, data) data, datanames = c("iris", "_a variable with spaces_")) + ) + ), + expr = { + session$setInputs("teal_modules-active_tab" = "module_1") + session$flushReact() + testthat::expect_setequal( + ls( + teal.code::get_env(modules_output$module_1()()), + all.names = TRUE + ), + c(".raw_data", "iris", "_a variable with spaces_") + ) + } + ) + }) + + testthat::it("(when used as non-native pipe) are present in datanames in the pre-processing code", { + testthat::expect_warning( + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = within( + teal.data::teal_data(), + { + iris <- iris + mtcars <- mtcars + `%cbind%` <- function(lhs, rhs) cbind(lhs, rhs) + iris <- iris %cbind% data.frame("new_column") + } + ), + modules = modules( + module("module_1", server = function(id, data) data, , datanames = c("iris")) + ), + filter = teal_slices( + module_specific = TRUE + ) + ), + expr = { + session$setInputs("teal_modules-active_tab" = "module_1") + session$flushReact() + + testthat::expect_contains( + strsplit( + x = teal.code::get_code(modules_output$module_1()()), + split = "\n" + )[[1]], + c( + "`%cbind%` <- function(lhs, rhs) cbind(lhs, rhs)", + ".raw_data <- list2env(list(iris = iris))" + ) + ) + } + ), + "'package:teal' may not be available when loading" + ) + }) +}) From b3e607d7b223094d20c45ed4ff257fc2d5a49261 Mon Sep 17 00:00:00 2001 From: averissimo Date: Fri, 25 Oct 2024 08:16:36 +0000 Subject: [PATCH 17/23] [skip actions] Bump version to 0.15.2.9075 --- DESCRIPTION | 4 ++-- NEWS.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 54d716211f..cd6b3cd95a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Type: Package Package: teal Title: Exploratory Web Apps for Analyzing Clinical Trials Data -Version: 0.15.2.9074 -Date: 2024-10-23 +Version: 0.15.2.9075 +Date: 2024-10-25 Authors@R: c( person("Dawid", "Kaledkowski", , "dawid.kaledkowski@roche.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-9533-457X")), diff --git a/NEWS.md b/NEWS.md index 247961954f..47584a6fee 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# teal 0.15.2.9074 +# teal 0.15.2.9075 ### New features From e834d19a11a934b39cdbfe508257e0ed5a175a38 Mon Sep 17 00:00:00 2001 From: Pawel Rucki <12943682+pawelru@users.noreply.github.com> Date: Fri, 25 Oct 2024 12:57:15 +0200 Subject: [PATCH 18/23] add rmarkdown to VignetteBuilder (#1394) fix nosuggest ci --- DESCRIPTION | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index cd6b3cd95a..08fe67f252 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -68,7 +68,8 @@ Suggests: withr (>= 2.1.0), yaml (>= 1.1.0) VignetteBuilder: - knitr + knitr, + rmarkdown RdMacros: lifecycle Config/Needs/verdepcheck: rstudio/shiny, insightsengineering/teal.data, From 3f7409dfcc8b17d02029b039c1ca20ec735bf2ce Mon Sep 17 00:00:00 2001 From: pawelru Date: Fri, 25 Oct 2024 10:58:08 +0000 Subject: [PATCH 19/23] [skip actions] Bump version to 0.15.2.9076 --- DESCRIPTION | 2 +- NEWS.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 08fe67f252..9197703af6 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: teal Title: Exploratory Web Apps for Analyzing Clinical Trials Data -Version: 0.15.2.9075 +Version: 0.15.2.9076 Date: 2024-10-25 Authors@R: c( person("Dawid", "Kaledkowski", , "dawid.kaledkowski@roche.com", role = c("aut", "cre"), diff --git a/NEWS.md b/NEWS.md index 47584a6fee..4a7664c848 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# teal 0.15.2.9075 +# teal 0.15.2.9076 ### New features From b244759607509f60ca589d087ab8e854ba62b16f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Mon, 28 Oct 2024 11:43:04 +0000 Subject: [PATCH 20/23] Fixes show r code reproducibility and CI (#1399) # Pull Request Fixes #1398 #### Changes description - [x] Fixes problem with reproducibility of functions inside `teal_data` - Should fix CI error in https://github.com/r-universe/pharmaverse/actions/runs/11517110492/job/32061355783 - [x] Fixes `noSuggest` [failure in CI ](https://github.com/insightsengineering/teal/actions/runs/11423178886/job/31782067851) --- R/module_init_data.R | 13 ++- tests/testthat/test-module_teal.R | 155 +++++++++++++++++++++++------- tests/testthat/test-utils.R | 1 + 3 files changed, 132 insertions(+), 37 deletions(-) diff --git a/R/module_init_data.R b/R/module_init_data.R index 8c09936a75..f2a39ce6e0 100644 --- a/R/module_init_data.R +++ b/R/module_init_data.R @@ -126,11 +126,18 @@ srv_init_data <- function(id, data) { vapply( datanames, function(dataname, datasets) { - hash <- rlang::hash(data[[dataname]]) + x <- data[[dataname]] + + code <- if (is.function(x) && !is.primitive(x)) { + x <- deparse1(x) + bquote(rlang::hash(deparse1(.(as.name(dataname))))) + } else { + bquote(rlang::hash(.(as.name(dataname)))) + } sprintf( "stopifnot(%s == %s) # @linksto %s", - deparse1(bquote(rlang::hash(.(as.name(dataname))))), - deparse1(hash), + deparse1(code), + deparse1(rlang::hash(x)), dataname ) }, diff --git a/tests/testthat/test-module_teal.R b/tests/testthat/test-module_teal.R index b8878e5661..ed01caaef4 100644 --- a/tests/testthat/test-module_teal.R +++ b/tests/testthat/test-module_teal.R @@ -70,6 +70,8 @@ testthat::describe("srv_teal lockfile", { "creation process is invoked for teal.lockfile.mode = \"enabled\" ", "and snapshot is copied to teal_app.lock and removed after session ended" ), { + testthat::skip_if_not_installed("mirai") + testthat::skip_if_not_installed("renv") withr::with_options( list(teal.lockfile.mode = "enabled"), { @@ -95,6 +97,8 @@ testthat::describe("srv_teal lockfile", { ) }) testthat::it("creation process is not invoked for teal.lockfile.mode = \"disabled\"", { + testthat::skip_if_not_installed("mirai") + testthat::skip_if_not_installed("renv") withr::with_options( list(teal.lockfile.mode = "disabled"), { @@ -2280,44 +2284,127 @@ testthat::describe("Datanames with special symbols", { }) testthat::it("(when used as non-native pipe) are present in datanames in the pre-processing code", { - testthat::expect_warning( - shiny::testServer( - app = srv_teal, - args = list( - id = "test", - data = within( - teal.data::teal_data(), - { - iris <- iris - mtcars <- mtcars - `%cbind%` <- function(lhs, rhs) cbind(lhs, rhs) - iris <- iris %cbind% data.frame("new_column") - } - ), - modules = modules( - module("module_1", server = function(id, data) data, , datanames = c("iris")) - ), - filter = teal_slices( - module_specific = TRUE - ) + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = within( + teal.data::teal_data(), + { + iris <- iris + mtcars <- mtcars + `%cbind%` <- function(lhs, rhs) cbind(lhs, rhs) + iris <- iris %cbind% data.frame("new_column") + } ), - expr = { - session$setInputs("teal_modules-active_tab" = "module_1") - session$flushReact() + modules = modules( + module("module_1", server = function(id, data) data, , datanames = c("iris")) + ), + filter = teal_slices( + module_specific = TRUE + ) + ), + expr = { + session$setInputs("teal_modules-active_tab" = "module_1") + session$flushReact() - testthat::expect_contains( - strsplit( - x = teal.code::get_code(modules_output$module_1()()), - split = "\n" - )[[1]], - c( - "`%cbind%` <- function(lhs, rhs) cbind(lhs, rhs)", - ".raw_data <- list2env(list(iris = iris))" + testthat::expect_contains( + strsplit( + x = teal.code::get_code(modules_output$module_1()()), + split = "\n" + )[[1]], + c( + "`%cbind%` <- function(lhs, rhs) cbind(lhs, rhs)", + ".raw_data <- list2env(list(iris = iris))" + ) + ) + } + ) + }) +}) + +testthat::describe("teal.data code with a function defined", { + testthat::it("is fully reproducible", { + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = reactive(within(teal.data::teal_data(), { + fun <- function(x) { + y <- x + 1 + y + 3 + } + })), + modules = modules(module("module_1", server = function(id, data) data)) + ), , + expr = { + session$setInputs("teal_modules-active_tab" = "module_1") + session$flushReact() + + # Need to evaluate characters to preserve indentation + local_env <- new.env(parent = .GlobalEnv) + dat <- modules_output$module_1()() + + eval( + parse(text = teal.code::get_code(dat)), + envir = local_env + ) + + testthat::expect_identical(local_env$fun(1), 5) + testthat::expect_identical(local_env$fun(1), dat[["fun"]](1)) + } + ) + }) + + testthat::it("has the correct code (with hash)", { + shiny::testServer( + app = srv_teal, + args = list( + id = "test", + data = reactive(within(teal.data::teal_data(), { + fun <- function(x) { + y <- x + 1 + y + 3 + } + })), + modules = modules(module("module_1", server = function(id, data) data)) + ), , + expr = { + session$setInputs("teal_modules-active_tab" = "module_1") + session$flushReact() + + # Need to evaluate characters to preserve indentation + local_env <- new.env(parent = .GlobalEnv) + eval( + parse( + text = paste( + sep = "\n", + "fun <- function(x) {", + " y <- x + 1", + " y + 3", + "}" ) + ), + envir = local_env + ) + local(hash <- rlang::hash(deparse1(fun)), envir = local_env) + + testthat::expect_setequal( + trimws(strsplit( + x = teal.code::get_code(modules_output$module_1()()), + split = "\n" + )[[1]]), + c( + "fun <- function(x) {", + "y <- x + 1", + "y + 3", + "}", + sprintf("stopifnot(rlang::hash(deparse1(fun)) == \"%s\")", local_env$hash), + ".raw_data <- list2env(list(fun = fun))", + "lockEnvironment(.raw_data)" ) - } - ), - "'package:teal' may not be available when loading" + ) + } ) }) }) diff --git a/tests/testthat/test-utils.R b/tests/testthat/test-utils.R index 4904c46461..41c06581b6 100644 --- a/tests/testthat/test-utils.R +++ b/tests/testthat/test-utils.R @@ -1,4 +1,5 @@ testthat::test_that("get_teal_bs_theme", { + testthat::skip_if_not_installed("bslib") testthat::expect_true(is.null(get_teal_bs_theme())) withr::with_options(list("teal.bs_theme" = bslib::bs_theme(version = "5")), { testthat::expect_s3_class(get_teal_bs_theme(), "bs_theme") From da3be66a89a1b872b97d2732ff5712e62d0a1316 Mon Sep 17 00:00:00 2001 From: averissimo Date: Mon, 28 Oct 2024 11:44:00 +0000 Subject: [PATCH 21/23] [skip actions] Bump version to 0.15.2.9077 --- DESCRIPTION | 4 ++-- NEWS.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 9197703af6..3027bb1d20 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Type: Package Package: teal Title: Exploratory Web Apps for Analyzing Clinical Trials Data -Version: 0.15.2.9076 -Date: 2024-10-25 +Version: 0.15.2.9077 +Date: 2024-10-28 Authors@R: c( person("Dawid", "Kaledkowski", , "dawid.kaledkowski@roche.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-9533-457X")), diff --git a/NEWS.md b/NEWS.md index 4a7664c848..4a519f1772 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# teal 0.15.2.9076 +# teal 0.15.2.9077 ### New features From fb0141783c0f32bf5e0e80f572d3aaf04e107505 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Mon, 28 Oct 2024 13:04:55 +0100 Subject: [PATCH 22/23] Unify `#@linksto` usage (#1397) This does not change any behavior of anything. This is just consistency management with `#@linksto` tag that we have for `lockEnvironment(.raw_data)` https://github.com/search?q=repo%3Ainsightsengineering%2Fteal+%23+%40linksto&type=code image --------- Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- R/module_filter_data.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/module_filter_data.R b/R/module_filter_data.R index efe4a53bd0..14dc7b4ecf 100644 --- a/R/module_filter_data.R +++ b/R/module_filter_data.R @@ -58,7 +58,7 @@ srv_filter_data <- function(id, datasets, active_datanames, data_rv, is_active) ".raw_data <- list2env(list(", toString(sprintf("%1$s = %1$s", sapply(datanames, as.name))), "))\n", - "lockEnvironment(.raw_data) #@linksto .raw_data" # this is environment and it is shared by qenvs. CAN'T MODIFY! + "lockEnvironment(.raw_data) # @linksto .raw_data" # this is environment and it is shared by qenvs. CAN'T MODIFY! ) ) filtered_code <- .get_filter_expr(datasets = datasets, datanames = datanames) From 977ea9e266df05579db05b2cf9d996a4434c101e Mon Sep 17 00:00:00 2001 From: m7pr Date: Mon, 28 Oct 2024 12:05:50 +0000 Subject: [PATCH 23/23] [skip actions] Bump version to 0.15.2.9078 --- DESCRIPTION | 2 +- NEWS.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 3027bb1d20..ba9e3025f0 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: teal Title: Exploratory Web Apps for Analyzing Clinical Trials Data -Version: 0.15.2.9077 +Version: 0.15.2.9078 Date: 2024-10-28 Authors@R: c( person("Dawid", "Kaledkowski", , "dawid.kaledkowski@roche.com", role = c("aut", "cre"), diff --git a/NEWS.md b/NEWS.md index 4a519f1772..7e97dcc07a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# teal 0.15.2.9077 +# teal 0.15.2.9078 ### New features