From 843b36bb80ab5c886de95fb82491573c5e277104 Mon Sep 17 00:00:00 2001 From: osenan Date: Tue, 14 Jul 2026 07:32:31 +0200 Subject: [PATCH 01/16] fix: restore as it was in main to simplify implementation --- NAMESPACE | 2 - R/teal.osprey.R | 2 +- R/tm_g_events_term_id.R | 327 ++++++++++++--------------- R/tm_g_events_term_id_picks.R | 407 ---------------------------------- man/tm_g_events_term_id.Rd | 74 +++---- 5 files changed, 169 insertions(+), 643 deletions(-) delete mode 100644 R/tm_g_events_term_id_picks.R diff --git a/NAMESPACE b/NAMESPACE index 18f3d101..9d07d672 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -8,7 +8,6 @@ export(tm_g_ae_oview) export(tm_g_ae_sub) export(tm_g_butterfly) export(tm_g_events_term_id) -export(tm_g_events_term_id.picks) export(tm_g_heat_bygrade) export(tm_g_patient_profile) export(tm_g_spiderplot) @@ -20,5 +19,4 @@ import(formatters) import(osprey) import(shiny) import(teal) -import(teal.picks) import(teal.transform) diff --git a/R/teal.osprey.R b/R/teal.osprey.R index 13d3fb10..1831c221 100644 --- a/R/teal.osprey.R +++ b/R/teal.osprey.R @@ -4,6 +4,6 @@ #' the analysis functions from `osprey` package. See package [website](https://insightsengineering.github.io/osprey/). #' This enables `teal` app developers to easily create applications making use of the `osprey` analysis functions. #' -#' @import dplyr osprey shiny teal formatters teal.picks teal.transform +#' @import dplyr osprey shiny teal formatters teal.transform #' @keywords internal "_PACKAGE" diff --git a/R/tm_g_events_term_id.R b/R/tm_g_events_term_id.R index 6d4db895..5a9cea52 100644 --- a/R/tm_g_events_term_id.R +++ b/R/tm_g_events_term_id.R @@ -1,15 +1,14 @@ #' Events by Term Plot Teal Module #' -#' Display an events-by-term plot as a Shiny module using [teal.picks::picks()] encodings. +#' @description +#' +#' Display Events by Term plot as a shiny module #' #' @inheritParams teal.widgets::standard_layout #' @inheritParams teal::module #' @inheritParams argument_convention -#' @param term_var (`picks`)\cr -#' [teal.picks::picks()] object for the event term variable (single selection). -#' @param arm_var (`picks`)\cr -#' [teal.picks::picks()] object for the treatment arm variable (single selection). -#' The arm variable must be a factor in the analysis data. +#' @param term_var [teal.transform::choices_selected] object with all available choices +#' and pre-selected option names that can be used to specify the term for events #' #' @inherit argument_convention return #' @inheritSection teal::example_module Reporting @@ -33,19 +32,17 @@ #' modules = modules( #' tm_g_events_term_id( #' label = "Common AE", -#' term_var = teal.picks::picks( -#' teal.picks::datasets("ADAE"), -#' teal.picks::variables( -#' choices = teal.picks::is_categorical(min.len = 2), -#' selected = "AEDECOD" +#' dataname = "ADAE", +#' term_var = choices_selected( +#' selected = "AEDECOD", +#' choices = c( +#' "AEDECOD", "AETERM", +#' "AEHLT", "AELLT", "AEBODSYS" #' ) #' ), -#' arm_var = teal.picks::picks( -#' teal.picks::datasets("ADSL"), -#' teal.picks::variables( -#' choices = teal.picks::is_categorical(min.len = 2), -#' selected = "ACTARMCD" -#' ) +#' arm_var = choices_selected( +#' selected = "ACTARMCD", +#' choices = c("ACTARM", "ACTARMCD") #' ), #' plot_height = c(600, 200, 2000) #' ) @@ -55,38 +52,18 @@ #' shinyApp(app$ui, app$server) #' } #' -tm_g_events_term_id <- function(label = "Common AE", - term_var = teal.picks::picks( - teal.picks::datasets(), - teal.picks::variables( - choices = teal.picks::is_categorical(min.len = 2), - selected = 1L - ) - ), - arm_var = teal.picks::picks( - teal.picks::datasets(), - teal.picks::variables( - choices = teal.picks::is_categorical(min.len = 2), - selected = 1L - ) - ), +tm_g_events_term_id <- function(label, + dataname, + term_var, + arm_var, fontsize = c(5, 3, 7), plot_height = c(600L, 200L, 2000L), plot_width = NULL, transformators = list()) { + message("Initializing tm_g_events_term_id") checkmate::assert_string(label) - - checkmate::assert_class(term_var, "picks", .var.name = "term_var") - checkmate::assert_false( - teal.picks::is_pick_multiple(term_var$variables), - .var.name = "`term_var` must use variables(..., multiple = FALSE)" - ) - checkmate::assert_class(arm_var, "picks", .var.name = "arm_var") - checkmate::assert_false( - teal.picks::is_pick_multiple(arm_var$variables), - .var.name = "`arm_var` must use variables(..., multiple = FALSE)" - ) - + checkmate::assert_class(term_var, classes = "choices_selected") + checkmate::assert_class(arm_var, classes = "choices_selected") checkmate::assert( checkmate::check_number(fontsize, finite = TRUE), checkmate::assert( @@ -97,61 +74,60 @@ tm_g_events_term_id <- function(label = "Common AE", ) ) checkmate::assert_numeric(plot_height, len = 3, any.missing = FALSE, finite = TRUE) - checkmate::assert_numeric( - plot_height[1], - lower = plot_height[2], upper = plot_height[3], .var.name = "plot_height" - ) + checkmate::assert_numeric(plot_height[1], lower = plot_height[2], upper = plot_height[3], .var.name = "plot_height") checkmate::assert_numeric(plot_width, len = 3, any.missing = FALSE, null.ok = TRUE, finite = TRUE) checkmate::assert_numeric( plot_width[1], - lower = plot_width[2], upper = plot_width[3], null.ok = TRUE, .var.name = "plot_width" + lower = plot_width[2], + upper = plot_width[3], + null.ok = TRUE, + .var.name = "plot_width" ) - pick_slots <- list(term_var = term_var, arm_var = arm_var) - all_datanames <- .picks_all_datanames(pick_slots) - args <- as.list(environment()) module( label = label, - ui = ui_g_events_term_id, server = srv_g_events_term_id, - ui_args = args[names(args) %in% names(formals(ui_g_events_term_id))], - server_args = args[names(args) %in% names(formals(srv_g_events_term_id))], + server_args = list(label = label, dataname = dataname, plot_height = plot_height, plot_width = plot_width), + ui = ui_g_events_term_id, + ui_args = args, transformators = transformators, - datanames = all_datanames + datanames = c("ADSL", dataname) ) } -#' @keywords internal -ui_g_events_term_id <- function(id, - term_var, - arm_var, - fontsize) { +ui_g_events_term_id <- function(id, ...) { ns <- NS(id) + args <- list(...) teal.widgets::standard_layout( output = teal.widgets::white_small_well( plot_decorate_output(id = ns(NULL)) ), encoding = tags$div( - tags$label("Encodings", class = "text-primary"), tags$br(), - tags$div( - tags$label("Term variable"), - teal.picks::picks_ui(id = ns("term_var"), picks = term_var) + teal.widgets::optionalSelectInput( + ns("term"), + "Term Variable", + choices = get_choices(args$term_var$choices), + selected = args$term_var$selected ), - tags$div( - tags$label("Arm variable"), - teal.picks::picks_ui(id = ns("arm_var"), picks = arm_var) + teal.widgets::optionalSelectInput( + ns("arm_var"), + "Arm Variable", + choices = get_choices(args$arm_var$choices), + selected = args$arm_var$selected ), selectInput( ns("arm_ref"), "Control", - choices = NULL + choices = get_choices(args$arm_var$choices), + selected = args$arm_var$selected ), selectInput( ns("arm_trt"), "Treatment", - choices = NULL + choices = get_choices(args$arm_var$choices), + selected = args$arm_var$selected ), teal.widgets::optionalSelectInput( ns("sort"), @@ -203,15 +179,14 @@ ui_g_events_term_id <- function(id, value = c(-0.5, 0.5), step = 0.01 ), - checkboxInput( - ns("reverse"), + checkboxInput(ns("reverse"), "Reverse Order", value = FALSE ) ), ui_g_decorate( ns(NULL), - fontsize = fontsize, + fontsize = args$fontsize, titles = "Common AE Table", footnotes = "" ) @@ -219,58 +194,87 @@ ui_g_events_term_id <- function(id, ) } -#' @keywords internal srv_g_events_term_id <- function(id, data, - term_var, - arm_var, + dataname, + label, plot_height, plot_width) { checkmate::assert_class(data, "reactive") - checkmate::assert_class(isolate(data()), "teal_data") + checkmate::assert_class(shiny::isolate(data()), "teal_data") moduleServer(id, function(input, output, session) { teal.logger::log_shiny_input_changes(input, namespace = "teal.osprey") + iv <- reactive({ + iv <- shinyvalidate::InputValidator$new() + iv$add_rule("term", shinyvalidate::sv_required( + message = "Term Variable is required" + )) + iv$add_rule("arm_var", shinyvalidate::sv_required( + message = "Arm Variable is required" + )) + rule_diff <- function(value, other) { + if (isTRUE(value == other)) "Control and Treatment must be different" + } + iv$add_rule("arm_trt", rule_diff, other = input$arm_ref) + iv$add_rule("arm_ref", rule_diff, other = input$arm_trt) + iv$enable() + iv + }) - anl_selectors <- teal.picks::picks_srv( - id = "", - picks = list(term_var = term_var, arm_var = arm_var), - data = data + decorate_output <- srv_g_decorate( + id = NULL, plt = plot_r, plot_height = plot_height, plot_width = plot_width ) + font_size <- decorate_output$font_size + pws <- decorate_output$pws - data_with_card <- reactive({ - obj <- data() - teal.reporter::teal_card(obj) <- - c( - teal.reporter::teal_card(obj), - teal.reporter::teal_card("## Module's output(s)") + observeEvent(list(input$diff_ci_method, input$conf_level), { + req(!is.null(input$diff_ci_method) && !is.null(input$conf_level)) + diff_ci_method <- input$diff_ci_method + conf_level <- input$conf_level + updateTextAreaInput( + session, + "foot", + value = sprintf( + "Note: %d%% CI is calculated using %s", + round(conf_level * 100), + name_ci(diff_ci_method) ) - obj + ) }) - merged_anl <- teal.picks::merge_srv( - "merge_anl", - data = data_with_card, - selectors = anl_selectors, - output_name = "ANL", - join_fun = "dplyr::inner_join" + observeEvent(input$sort, + { + sort <- if (is.null(input$sort)) " " else input$sort + updateTextInput( + session, + "title", + value = sprintf( + "Common AE Table %s", + c( + "term" = "Sorted by Term", + "riskdiff" = "Sorted by Risk Difference", + "meanrisk" = "Sorted by Mean Risk", + " " = "" + )[sort] + ) + ) + }, + ignoreNULL = FALSE ) - anl_q <- merged_anl$data - merge_vars <- merged_anl$variables - - observeEvent(anl_selectors$arm_var(), + observeEvent(input$arm_var, { - arm_selector <- anl_selectors$arm_var() - req(arm_selector) - arm_var_name <- arm_selector$variables$selected - arm_dataset <- arm_selector$datasets$selected - req(arm_var_name, arm_dataset) + arm_var <- input$arm_var + ANL <- data()[[dataname]] - arm_data <- data()[[arm_dataset]] - choices <- levels(arm_data[[arm_var_name]]) + choices <- levels(ANL[[arm_var]]) - trt_index <- if (length(choices) == 1L) 1L else 2L + if (length(choices) == 1) { + trt_index <- 1 + } else { + trt_index <- 2 + } updateSelectInput( session, @@ -288,104 +292,57 @@ srv_g_events_term_id <- function(id, ignoreNULL = TRUE ) - observeEvent(input$sort, - { - sort <- if (is.null(input$sort)) " " else input$sort - updateTextInput( - session, - "title", - value = sprintf( - "Common AE Table %s", - c( - "term" = "Sorted by Term", - "riskdiff" = "Sorted by Risk Difference", - "meanrisk" = "Sorted by Mean Risk", - " " = "" - )[sort] - ) + output_q <- reactive({ + obj <- data() + teal.reporter::teal_card(obj) <- + c( + teal.reporter::teal_card(obj), + teal.reporter::teal_card("## Module's output(s)") ) - }, - ignoreNULL = FALSE - ) - observeEvent(list(input$diff_ci_method, input$conf_level), { - req(!is.null(input$diff_ci_method) && !is.null(input$conf_level)) - diff_ci_method <- input$diff_ci_method - conf_level <- input$conf_level - updateTextAreaInput( - session, - "foot", - value = sprintf( - "Note: %d%% CI is calculated using %s", - round(conf_level * 100), - name_ci(diff_ci_method) - ) - ) - }) + ANL <- obj[[dataname]] - decorate_output <- srv_g_decorate( - id = NULL, - plt = plot_r, - plot_height = plot_height, - plot_width = plot_width - ) - font_size <- decorate_output$font_size - pws <- decorate_output$pws + teal::validate_inputs(iv()) - output_q <- reactive({ - merged_vars <- merge_vars() - validate( - need( - length(merged_vars[["term_var"]]) > 0L, - "Please select a term variable" - ), - need( - length(merged_vars[["arm_var"]]) > 0L, - "Please select an arm variable" + shiny::validate( + shiny::need(is.factor(ANL[[input$arm_var]]), "Arm Var must be a factor variable. Contact developer."), + shiny::need( + input$arm_trt %in% ANL[[req(input$arm_var)]] && input$arm_ref %in% ANL[[req(input$arm_var)]], + "Cannot generate plot. The dataset does not contain subjects from both the control and treatment arms." ) ) - term_var_name <- merged_vars[["term_var"]][[1L]] - arm_var_name <- merged_vars[["arm_var"]][[1L]] - - arm_selector <- anl_selectors$arm_var() - arm_var_orig <- arm_selector$variables$selected - arm_dataset <- arm_selector$datasets$selected + adsl_vars <- unique(c("USUBJID", "STUDYID", input$arm_var)) + anl_vars <- c("USUBJID", "STUDYID", input$term) - qenv <- anl_q() - ANL <- qenv[["ANL"]] - - validate( - need( - is.factor(ANL[[arm_var_name]]), - "Arm Variable must be a factor variable." - ), - need( - input$arm_trt %in% ANL[[arm_var_name]] && input$arm_ref %in% ANL[[arm_var_name]], - "Cannot generate plot. The dataset does not contain subjects from both the control and treatment arms." - ), - need( - !isTRUE(input$arm_trt == input$arm_ref), - "Control and Treatment must be different." + q1 <- teal.code::eval_code( + obj, + code = bquote( + ANL <- merge( + x = ADSL[, .(adsl_vars), drop = FALSE], + y = .(as.name(dataname))[, .(anl_vars), drop = FALSE], + all.x = FALSE, + all.y = FALSE, + by = c("USUBJID", "STUDYID") + ) ) ) - teal::validate_has_data( - ANL, + teal::validate_has_data(q1[["ANL"]], min_nrow = 10, msg = "Analysis data set must have at least 10 data points" ) - teal.reporter::teal_card(qenv) <- c(teal.reporter::teal_card(qenv), "### Plot") + teal.reporter::teal_card(q1) <- c(teal.reporter::teal_card(q1), "### Plot") - teal.code::eval_code( - qenv, + q2 <- teal.code::eval_code( + q1, code = bquote( plot <- osprey::g_events_term_id( - term = ANL[[.(term_var_name)]], + term = ANL[[.(input$term)]], id = ANL$USUBJID, - arm = ANL[[.(arm_var_name)]], - arm_N = table(.(as.name(arm_dataset))[[.(arm_var_orig)]]), + arm = ANL[[.(input$arm_var)]], + arm_N = table(ADSL[[.(input$arm_var)]]), ref = .(input$arm_ref), trt = .(input$arm_trt), sort_by = .(input$sort), diff --git a/R/tm_g_events_term_id_picks.R b/R/tm_g_events_term_id_picks.R deleted file mode 100644 index b7690129..00000000 --- a/R/tm_g_events_term_id_picks.R +++ /dev/null @@ -1,407 +0,0 @@ -#' @describeIn tm_g_events_term_id [teal.picks::picks()]-based encodings (`picks`). -#' @export -tm_g_events_term_id.picks <- function(label = "Common AE", # nolint: object_name_linter. - dataname = NULL, - term_var = teal.picks::picks( - teal.picks::datasets(), - teal.picks::variables( - choices = teal.picks::is_categorical(min.len = 2), - selected = 1L - ) - ), - arm_var = teal.picks::picks( - teal.picks::datasets(), - teal.picks::variables( - choices = teal.picks::is_categorical(min.len = 2), - selected = 1L - ) - ), - fontsize = c(5, 3, 7), - plot_height = c(600L, 200L, 2000L), - plot_width = NULL, - transformators = list()) { - checkmate::assert_string(label) - - checkmate::assert_class(term_var, "picks", .var.name = "term_var") - checkmate::assert_false( - teal.picks::is_pick_multiple(term_var$variables), - .var.name = "`term_var` must use variables(..., multiple = FALSE)" - ) - checkmate::assert_class(arm_var, "picks", .var.name = "arm_var") - checkmate::assert_false( - teal.picks::is_pick_multiple(arm_var$variables), - .var.name = "`arm_var` must use variables(..., multiple = FALSE)" - ) - - checkmate::assert( - checkmate::check_number(fontsize, finite = TRUE), - checkmate::assert( - combine = "and", - .var.name = "fontsize", - checkmate::check_numeric(fontsize, len = 3, any.missing = FALSE, finite = TRUE), - checkmate::check_numeric(fontsize[1], lower = fontsize[2], upper = fontsize[3]) - ) - ) - checkmate::assert_numeric(plot_height, len = 3, any.missing = FALSE, finite = TRUE) - checkmate::assert_numeric( - plot_height[1], - lower = plot_height[2], upper = plot_height[3], .var.name = "plot_height" - ) - checkmate::assert_numeric(plot_width, len = 3, any.missing = FALSE, null.ok = TRUE, finite = TRUE) - checkmate::assert_numeric( - plot_width[1], - lower = plot_width[2], upper = plot_width[3], null.ok = TRUE, .var.name = "plot_width" - ) - - pick_slots <- list(term_var = term_var, arm_var = arm_var) - all_datanames <- unique( - unlist( - lapply( - pick_slots, - function(p) { - ch <- p$datasets$choices - if (checkmate::test_character(ch, min.len = 1L)) { - return(unique(as.character(ch))) - } - sel <- p$datasets$selected - unique(as.character(unlist(sel, recursive = FALSE, use.names = FALSE))) - } - ), - use.names = FALSE - ) - ) - all_datanames <- all_datanames[nzchar(all_datanames) & !is.na(all_datanames)] - - args <- as.list(environment()) - - module( - label = label, - ui = ui_g_events_term_id_picks, - server = srv_g_events_term_id_picks, - ui_args = args[names(args) %in% names(formals(ui_g_events_term_id_picks))], - server_args = args[names(args) %in% names(formals(srv_g_events_term_id_picks))], - transformators = transformators, - datanames = all_datanames - ) -} - -#' @examples -#' # Using the picks method -#' data <- teal_data() %>% -#' within({ -#' ADSL <- rADSL -#' ADAE <- rADAE -#' }) -#' -#' join_keys(data) <- default_cdisc_join_keys[names(data)] -#' -#' app <- init( -#' data = data, -#' modules = modules( -#' tm_g_events_term_id( -#' label = "Common AE", -#' term_var = teal.picks::picks( -#' teal.picks::datasets("ADAE"), -#' teal.picks::variables( -#' choices = teal.picks::is_categorical(min.len = 2), -#' selected = "AEDECOD" -#' ) -#' ), -#' arm_var = teal.picks::picks( -#' teal.picks::datasets("ADSL"), -#' teal.picks::variables( -#' choices = teal.picks::is_categorical(min.len = 2), -#' selected = "ACTARMCD" -#' ) -#' ), -#' plot_height = c(600, 200, 2000) -#' ) -#' ) -#' ) -#' if (interactive()) { -#' shinyApp(app$ui, app$server) -#' } - -#' @keywords internal -ui_g_events_term_id_picks <- function(id, - term_var, - arm_var, - fontsize) { - ns <- NS(id) - teal.widgets::standard_layout( - output = teal.widgets::white_small_well( - plot_decorate_output(id = ns(NULL)) - ), - encoding = tags$div( - tags$label("Encodings", class = "text-primary"), tags$br(), - tags$div( - tags$label("Term variable"), - teal.picks::picks_ui(id = ns("term_var"), picks = term_var) - ), - tags$div( - tags$label("Arm variable"), - teal.picks::picks_ui(id = ns("arm_var"), picks = arm_var) - ), - selectInput( - ns("arm_ref"), - "Control", - choices = NULL - ), - selectInput( - ns("arm_trt"), - "Treatment", - choices = NULL - ), - teal.widgets::optionalSelectInput( - ns("sort"), - "Sort By", - choices = c( - "Term" = "term", - "Risk Difference" = "riskdiff", - "Mean Risk" = "meanrisk" - ), - selected = NULL - ), - teal.widgets::panel_item( - "Confidence interval settings", - teal.widgets::optionalSelectInput( - ns("diff_ci_method"), - "Method for Difference of Proportions CI", - choices = ci_choices, - selected = ci_choices[1] - ), - teal.widgets::optionalSliderInput( - ns("conf_level"), - "Confidence Level", - min = 0.5, - max = 1, - value = 0.95 - ) - ), - teal.widgets::panel_item( - "Additional plot settings", - teal.widgets::optionalSelectInput( - ns("axis"), - "Axis Side", - choices = c("Left" = "left", "Right" = "right"), - selected = "left" - ), - sliderInput( - ns("raterange"), - "Overall Rate Range", - min = 0, - max = 1, - value = c(0.1, 1), - step = 0.01 - ), - sliderInput( - ns("diffrange"), - "Rate Difference Range", - min = -1, - max = 1, - value = c(-0.5, 0.5), - step = 0.01 - ), - checkboxInput( - ns("reverse"), - "Reverse Order", - value = FALSE - ) - ), - ui_g_decorate( - ns(NULL), - fontsize = fontsize, - titles = "Common AE Table", - footnotes = "" - ) - ) - ) -} - -#' @keywords internal -srv_g_events_term_id_picks <- function(id, - data, - term_var, - arm_var, - plot_height, - plot_width) { - checkmate::assert_class(data, "reactive") - checkmate::assert_class(isolate(data()), "teal_data") - - moduleServer(id, function(input, output, session) { - teal.logger::log_shiny_input_changes(input, namespace = "teal.osprey") - - anl_selectors <- teal.picks::picks_srv( - id = "", - picks = list(term_var = term_var, arm_var = arm_var), - data = data - ) - - data_with_card <- reactive({ - obj <- data() - teal.reporter::teal_card(obj) <- - c( - teal.reporter::teal_card(obj), - teal.reporter::teal_card("## Module's output(s)") - ) - obj - }) - - merged_anl <- teal.picks::merge_srv( - "merge_anl", - data = data_with_card, - selectors = anl_selectors, - output_name = "ANL", - join_fun = "dplyr::inner_join" - ) - - anl_q <- merged_anl$data - merge_vars <- merged_anl$variables - - observeEvent(anl_selectors$arm_var(), - { - arm_selector <- anl_selectors$arm_var() - req(arm_selector) - arm_var_name <- arm_selector$variables$selected - arm_dataset <- arm_selector$datasets$selected - req(arm_var_name, arm_dataset) - - arm_data <- data()[[arm_dataset]] - choices <- levels(arm_data[[arm_var_name]]) - - trt_index <- if (length(choices) == 1L) 1L else 2L - - updateSelectInput( - session, - "arm_ref", - selected = choices[1], - choices = choices - ) - updateSelectInput( - session, - "arm_trt", - selected = choices[trt_index], - choices = choices - ) - }, - ignoreNULL = TRUE - ) - - observeEvent(input$sort, - { - sort <- if (is.null(input$sort)) " " else input$sort - updateTextInput( - session, - "title", - value = sprintf( - "Common AE Table %s", - c( - "term" = "Sorted by Term", - "riskdiff" = "Sorted by Risk Difference", - "meanrisk" = "Sorted by Mean Risk", - " " = "" - )[sort] - ) - ) - }, - ignoreNULL = FALSE - ) - - observeEvent(list(input$diff_ci_method, input$conf_level), { - req(!is.null(input$diff_ci_method) && !is.null(input$conf_level)) - diff_ci_method <- input$diff_ci_method - conf_level <- input$conf_level - updateTextAreaInput( - session, - "foot", - value = sprintf( - "Note: %d%% CI is calculated using %s", - round(conf_level * 100), - name_ci(diff_ci_method) - ) - ) - }) - - decorate_output <- srv_g_decorate( - id = NULL, - plt = plot_r, - plot_height = plot_height, - plot_width = plot_width - ) - font_size <- decorate_output$font_size - pws <- decorate_output$pws - - output_q <- reactive({ - merged_vars <- merge_vars() - validate( - need( - length(merged_vars[["term_var"]]) > 0L, - "Please select a term variable" - ), - need( - length(merged_vars[["arm_var"]]) > 0L, - "Please select an arm variable" - ) - ) - - term_var_name <- merged_vars[["term_var"]][[1L]] - arm_var_name <- merged_vars[["arm_var"]][[1L]] - - arm_selector <- anl_selectors$arm_var() - arm_var_orig <- arm_selector$variables$selected - arm_dataset <- arm_selector$datasets$selected - - qenv <- anl_q() - ANL <- qenv[["ANL"]] - - validate( - need( - is.factor(ANL[[arm_var_name]]), - "Arm Variable must be a factor variable." - ), - need( - input$arm_trt %in% ANL[[arm_var_name]] && input$arm_ref %in% ANL[[arm_var_name]], - "Cannot generate plot. The dataset does not contain subjects from both the control and treatment arms." - ), - need( - !isTRUE(input$arm_trt == input$arm_ref), - "Control and Treatment must be different." - ) - ) - - teal::validate_has_data( - ANL, - min_nrow = 10, - msg = "Analysis data set must have at least 10 data points" - ) - - teal.reporter::teal_card(qenv) <- c(teal.reporter::teal_card(qenv), "### Plot") - - teal.code::eval_code( - qenv, - code = bquote( - plot <- osprey::g_events_term_id( - term = ANL[[.(term_var_name)]], - id = ANL$USUBJID, - arm = ANL[[.(arm_var_name)]], - arm_N = table(.(as.name(arm_dataset))[[.(arm_var_orig)]]), - ref = .(input$arm_ref), - trt = .(input$arm_trt), - sort_by = .(input$sort), - rate_range = .(input$raterange), - diff_range = .(input$diffrange), - reversed = .(input$reverse), - conf_level = .(input$conf_level), - diff_ci_method = .(input$diff_ci_method), - axis_side = .(input$axis), - fontsize = .(font_size()), - draw = TRUE - ) - ) - ) - }) - - plot_r <- reactive(output_q()[["plot"]]) - set_chunk_dims(pws, output_q) - }) -} diff --git a/man/tm_g_events_term_id.Rd b/man/tm_g_events_term_id.Rd index e3af6ce8..f7745d13 100644 --- a/man/tm_g_events_term_id.Rd +++ b/man/tm_g_events_term_id.Rd @@ -1,30 +1,14 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/tm_g_events_term_id.R, -% R/tm_g_events_term_id_picks.R +% Please edit documentation in R/tm_g_events_term_id.R \name{tm_g_events_term_id} \alias{tm_g_events_term_id} -\alias{tm_g_events_term_id.picks} \title{Events by Term Plot Teal Module} \usage{ tm_g_events_term_id( - label = "Common AE", - term_var = teal.picks::picks(teal.picks::datasets(), teal.picks::variables(choices = - teal.picks::is_categorical(min.len = 2), selected = 1L)), - arm_var = teal.picks::picks(teal.picks::datasets(), teal.picks::variables(choices = - teal.picks::is_categorical(min.len = 2), selected = 1L)), - fontsize = c(5, 3, 7), - plot_height = c(600L, 200L, 2000L), - plot_width = NULL, - transformators = list() -) - -tm_g_events_term_id.picks( - label = "Common AE", - dataname = NULL, - term_var = teal.picks::picks(teal.picks::datasets(), teal.picks::variables(choices = - teal.picks::is_categorical(min.len = 2), selected = 1L)), - arm_var = teal.picks::picks(teal.picks::datasets(), teal.picks::variables(choices = - teal.picks::is_categorical(min.len = 2), selected = 1L)), + label, + dataname, + term_var, + arm_var, fontsize = c(5, 3, 7), plot_height = c(600L, 200L, 2000L), plot_width = NULL, @@ -35,12 +19,17 @@ tm_g_events_term_id.picks( \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{term_var}{(\code{picks})\cr -\code{\link[teal.picks:picks]{teal.picks::picks()}} object for the event term variable (single selection).} +\item{dataname}{(\code{character(1)})\cr +analysis data used in the teal module, needs to be +available in the list passed to the \code{data} argument of \code{\link[teal:init]{teal::init()}}.} -\item{arm_var}{(\code{picks})\cr -\code{\link[teal.picks:picks]{teal.picks::picks()}} object for the treatment arm variable (single selection). -The arm variable must be a factor in the analysis data.} +\item{term_var}{\link[teal.transform:choices_selected]{teal.transform::choices_selected} object with all available choices +and pre-selected option names that can be used to specify the term for events} + +\item{arm_var}{(\code{choices_selected})\cr +object with all available choices and the pre-selected option for variable +names that can be used as \code{arm_var}. See \code{\link[teal.transform:choices_selected]{teal.transform::choices_selected()}} for +details. Column \code{arm_var} in the \code{dataname} has to be a factor.} \item{fontsize}{(\code{numeric(1)} or \code{numeric(3)})\cr Defines initial possible range of font-size. \code{fontsize} is set for @@ -55,22 +44,13 @@ vector to indicate default value, minimum and maximum values.} \item{transformators}{(\code{list} of \code{teal_transform_module}) that will be applied to transform module's data input. To learn more check \code{vignette("transform-input-data", package = "teal")}.} - -\item{dataname}{(\code{character(1)})\cr -analysis data used in the teal module, needs to be -available in the list passed to the \code{data} argument of \code{\link[teal:init]{teal::init()}}.} } \value{ -the \code{\link[teal:module]{teal::module()}} object. +the \code{\link[teal:teal_modules]{teal::module()}} object. } \description{ -Display an events-by-term plot as a Shiny module using \code{\link[teal.picks:picks]{teal.picks::picks()}} encodings. +Display Events by Term plot as a shiny module } -\section{Functions}{ -\itemize{ -\item \code{tm_g_events_term_id.picks()}: \code{\link[teal.picks:picks]{teal.picks::picks()}}-based encodings (\code{picks}). - -}} \section{Reporting}{ @@ -101,19 +81,17 @@ app <- init( modules = modules( tm_g_events_term_id( label = "Common AE", - term_var = teal.picks::picks( - teal.picks::datasets("ADAE"), - teal.picks::variables( - choices = teal.picks::is_categorical(min.len = 2), - selected = "AEDECOD" + dataname = "ADAE", + term_var = choices_selected( + selected = "AEDECOD", + choices = c( + "AEDECOD", "AETERM", + "AEHLT", "AELLT", "AEBODSYS" ) ), - arm_var = teal.picks::picks( - teal.picks::datasets("ADSL"), - teal.picks::variables( - choices = teal.picks::is_categorical(min.len = 2), - selected = "ACTARMCD" - ) + arm_var = choices_selected( + selected = "ACTARMCD", + choices = c("ACTARM", "ACTARMCD") ), plot_height = c(600, 200, 2000) ) From 165e8f747328be5fc57237d06f5e4e6d5f27da95 Mon Sep 17 00:00:00 2001 From: osenan Date: Tue, 14 Jul 2026 11:46:42 +0200 Subject: [PATCH 02/16] refactor: allow plot to be generated --- R/tm_g_events_term_id.R | 218 +++++++++++++++++++++++----------------- 1 file changed, 128 insertions(+), 90 deletions(-) diff --git a/R/tm_g_events_term_id.R b/R/tm_g_events_term_id.R index 5a9cea52..d5869d08 100644 --- a/R/tm_g_events_term_id.R +++ b/R/tm_g_events_term_id.R @@ -62,8 +62,17 @@ tm_g_events_term_id <- function(label, transformators = list()) { message("Initializing tm_g_events_term_id") checkmate::assert_string(label) - checkmate::assert_class(term_var, classes = "choices_selected") - checkmate::assert_class(arm_var, classes = "choices_selected") + checkmate::assert_string(dataname) + + term_var <- migrate_choices_selected_to_variables(term_var) + arm_var <- migrate_choices_selected_to_variables(arm_var) + + term_var <- create_picks_helper(teal.picks::datasets(dataname, dataname), term_var) + arm_var <- create_picks_helper(teal.picks::datasets("ADSL", "ADSL"), arm_var) + + term_var <- force_pick_to_single(term_var, "term_var") + arm_var <- force_pick_to_single(arm_var, "arm_var") + checkmate::assert( checkmate::check_number(fontsize, finite = TRUE), checkmate::assert( @@ -87,47 +96,43 @@ tm_g_events_term_id <- function(label, args <- as.list(environment()) module( + datanames = c("ADSL", dataname), label = label, server = srv_g_events_term_id, - server_args = list(label = label, dataname = dataname, plot_height = plot_height, plot_width = plot_width), + server_args = args[names(args) %in% names(formals(srv_g_events_term_id))], ui = ui_g_events_term_id, - ui_args = args, - transformators = transformators, - datanames = c("ADSL", dataname) + ui_args = args[names(args) %in% names(formals(ui_g_events_term_id))], + transformators = transformators ) } -ui_g_events_term_id <- function(id, ...) { +ui_g_events_term_id <- function(id, + term_var, + arm_var, + fontsize) { ns <- NS(id) - args <- list(...) teal.widgets::standard_layout( output = teal.widgets::white_small_well( plot_decorate_output(id = ns(NULL)) ), encoding = tags$div( - teal.widgets::optionalSelectInput( - ns("term"), - "Term Variable", - choices = get_choices(args$term_var$choices), - selected = args$term_var$selected - ), - teal.widgets::optionalSelectInput( - ns("arm_var"), - "Arm Variable", - choices = get_choices(args$arm_var$choices), - selected = args$arm_var$selected - ), + tags$div( + tags$strong("Term Variable"), + teal.picks::picks_ui(id = ns("term_var"), picks = term_var) + ), + tags$div( + tags$strong("Arm Variable"), + teal.picks::picks_ui(id = ns("arm_var"), picks = arm_var) + ), selectInput( ns("arm_ref"), "Control", - choices = get_choices(args$arm_var$choices), - selected = args$arm_var$selected + choices = "" ), selectInput( ns("arm_trt"), "Treatment", - choices = get_choices(args$arm_var$choices), - selected = args$arm_var$selected + choices = "" ), teal.widgets::optionalSelectInput( ns("sort"), @@ -186,7 +191,7 @@ ui_g_events_term_id <- function(id, ...) { ), ui_g_decorate( ns(NULL), - fontsize = args$fontsize, + fontsize = fontsize, titles = "Common AE Table", footnotes = "" ) @@ -198,6 +203,8 @@ srv_g_events_term_id <- function(id, data, dataname, label, + term_var, + arm_var, plot_height, plot_width) { checkmate::assert_class(data, "reactive") @@ -205,23 +212,33 @@ srv_g_events_term_id <- function(id, moduleServer(id, function(input, output, session) { teal.logger::log_shiny_input_changes(input, namespace = "teal.osprey") - iv <- reactive({ - iv <- shinyvalidate::InputValidator$new() - iv$add_rule("term", shinyvalidate::sv_required( - message = "Term Variable is required" - )) - iv$add_rule("arm_var", shinyvalidate::sv_required( - message = "Arm Variable is required" - )) - rule_diff <- function(value, other) { - if (isTRUE(value == other)) "Control and Treatment must be different" - } - iv$add_rule("arm_trt", rule_diff, other = input$arm_ref) - iv$add_rule("arm_ref", rule_diff, other = input$arm_trt) - iv$enable() - iv + + anl_selectors <- teal.picks::picks_srv( + picks = list(term_var = term_var, arm_var = arm_var), + data = data + ) + + data_with_card <- reactive({ + obj <- data() + teal.reporter::teal_card(obj) <- + c( + teal.reporter::teal_card(obj), + teal.reporter::teal_card("## Module's output(s)") + ) + obj }) + merged_anl <- teal.picks::merge_srv( + "merge_anl", + data = data_with_card, + selectors = anl_selectors, + output_name = "ANL", + join_fun = "dplyr::inner_join" + ) + + anl_q <- merged_anl$data + merge_vars <- merged_anl$variables + decorate_output <- srv_g_decorate( id = NULL, plt = plot_r, plot_height = plot_height, plot_width = plot_width ) @@ -263,12 +280,16 @@ srv_g_events_term_id <- function(id, ignoreNULL = FALSE ) - observeEvent(input$arm_var, + observeEvent(anl_selectors$arm_var(), { - arm_var <- input$arm_var - ANL <- data()[[dataname]] + arm_selector <- anl_selectors$arm_var() + req(arm_selector) + arm_var_name <- arm_selector$variables$selected + arm_dataset <- arm_selector$datasets$selected + req(arm_var_name, arm_dataset) - choices <- levels(ANL[[arm_var]]) + arm_data <- data()[[arm_dataset]] + choices <- levels(arm_data[[arm_var_name]]) if (length(choices) == 1) { trt_index <- 1 @@ -293,70 +314,87 @@ srv_g_events_term_id <- function(id, ) output_q <- reactive({ - obj <- data() - teal.reporter::teal_card(obj) <- - c( - teal.reporter::teal_card(obj), - teal.reporter::teal_card("## Module's output(s)") + merged_vars <- merge_vars() + validate( + need( + length(merged_vars[["term_var"]]) > 0L, + "Please select a term variable" + ), + need( + length(merged_vars[["arm_var"]]) > 0L, + "Please select an arm variable" ) + ) - ANL <- obj[[dataname]] - - teal::validate_inputs(iv()) + term_var_name <- merged_vars[["term_var"]][[1L]] + arm_var_name <- merged_vars[["arm_var"]][[1L]] - shiny::validate( - shiny::need(is.factor(ANL[[input$arm_var]]), "Arm Var must be a factor variable. Contact developer."), - shiny::need( - input$arm_trt %in% ANL[[req(input$arm_var)]] && input$arm_ref %in% ANL[[req(input$arm_var)]], - "Cannot generate plot. The dataset does not contain subjects from both the control and treatment arms." - ) - ) + arm_selector <- anl_selectors$arm_var() + arm_var_orig <- arm_selector$variables$selected + arm_dataset <- arm_selector$datasets$selected - adsl_vars <- unique(c("USUBJID", "STUDYID", input$arm_var)) - anl_vars <- c("USUBJID", "STUDYID", input$term) + qenv <- anl_q() + ANL <- qenv[["ANL"]] - q1 <- teal.code::eval_code( - obj, - code = bquote( - ANL <- merge( - x = ADSL[, .(adsl_vars), drop = FALSE], - y = .(as.name(dataname))[, .(anl_vars), drop = FALSE], - all.x = FALSE, - all.y = FALSE, - by = c("USUBJID", "STUDYID") - ) + validate( + need( + is.factor(ANL[[arm_var_name]]), + "Arm Var must be a factor variable. Contact developer." + ), + need( + input$arm_trt %in% ANL[[arm_var_name]] && input$arm_ref %in% ANL[[arm_var_name]], + "Cannot generate plot. The dataset does not contain subjects from both the control and treatment arms." + ), + need( + !isTRUE(input$arm_trt == input$arm_ref), + "Control and Treatment must be different." ) ) - teal::validate_has_data(q1[["ANL"]], + teal::validate_has_data(ANL, min_nrow = 10, msg = "Analysis data set must have at least 10 data points" ) - teal.reporter::teal_card(q1) <- c(teal.reporter::teal_card(q1), "### Plot") + teal.reporter::teal_card(qenv) <- c(teal.reporter::teal_card(qenv), "### Plot") - q2 <- teal.code::eval_code( - q1, - code = bquote( + q2 <- within( + qenv, { plot <- osprey::g_events_term_id( - term = ANL[[.(input$term)]], + term = ANL[[term_var_name]], id = ANL$USUBJID, - arm = ANL[[.(input$arm_var)]], - arm_N = table(ADSL[[.(input$arm_var)]]), - ref = .(input$arm_ref), - trt = .(input$arm_trt), - sort_by = .(input$sort), - rate_range = .(input$raterange), - diff_range = .(input$diffrange), - reversed = .(input$reverse), - conf_level = .(input$conf_level), - diff_ci_method = .(input$diff_ci_method), - axis_side = .(input$axis), - fontsize = .(font_size()), + arm = ANL[[arm_var_name]], + arm_N = table(get(arm_dataset)[[arm_var_orig]]), + ref = arm_ref, + trt = arm_trt, + sort_by = sort_by, + rate_range = rate_range, + diff_range = diff_range, + reversed = reversed, + conf_level = conf_level, + diff_ci_method = diff_ci_method, + axis_side = axis_side, + fontsize = fontsize, draw = TRUE ) - ) + }, + term_var_name = term_var_name, + arm_var_name = arm_var_name, + arm_dataset = arm_dataset, + arm_var_orig = arm_var_orig, + arm_ref = input$arm_ref, + arm_trt = input$arm_trt, + sort_by = input$sort, + rate_range = input$raterange, + diff_range = input$diffrange, + reversed = input$reverse, + conf_level = input$conf_level, + diff_ci_method = input$diff_ci_method, + axis_side = input$axis, + fontsize = font_size() ) + + q2 }) plot_r <- reactive(output_q()[["plot"]]) From 409dd8eaa6e2b6422b4a755c15aaad3bec83f609 Mon Sep 17 00:00:00 2001 From: osenan Date: Tue, 14 Jul 2026 12:56:53 +0200 Subject: [PATCH 03/16] refactor: fix tests and use teal function for input validation --- R/tm_g_events_term_id.R | 83 ++++++++++--------- man/tm_g_events_term_id.Rd | 17 ++-- .../test-shinytest2-tm_g_events_term_id.R | 23 ++--- tests/testthat/test-tm_g_events_term_id.R | 11 ++- 4 files changed, 69 insertions(+), 65 deletions(-) diff --git a/R/tm_g_events_term_id.R b/R/tm_g_events_term_id.R index d5869d08..e8353ca5 100644 --- a/R/tm_g_events_term_id.R +++ b/R/tm_g_events_term_id.R @@ -7,7 +7,8 @@ #' @inheritParams teal.widgets::standard_layout #' @inheritParams teal::module #' @inheritParams argument_convention -#' @param term_var [teal.transform::choices_selected] object with all available choices +#' @param term_var Either a ([`teal.transform::choices_selected`]) +#' `choices_selected` object or a (`[teal.picks::variables()]`) object with all available choices #' and pre-selected option names that can be used to specify the term for events #' #' @inherit argument_convention return @@ -33,16 +34,16 @@ #' tm_g_events_term_id( #' label = "Common AE", #' dataname = "ADAE", -#' term_var = choices_selected( -#' selected = "AEDECOD", +#' term_var = variables( #' choices = c( #' "AEDECOD", "AETERM", #' "AEHLT", "AELLT", "AEBODSYS" -#' ) +#' ), +#' selected = "AEDECOD" #' ), -#' arm_var = choices_selected( -#' selected = "ACTARMCD", -#' choices = c("ACTARM", "ACTARMCD") +#' arm_var = variables( +#' choices = c("ACTARM", "ACTARMCD"), +#' selected = "ACTARMCD" #' ), #' plot_height = c(600, 200, 2000) #' ) @@ -116,14 +117,14 @@ ui_g_events_term_id <- function(id, plot_decorate_output(id = ns(NULL)) ), encoding = tags$div( - tags$div( - tags$strong("Term Variable"), - teal.picks::picks_ui(id = ns("term_var"), picks = term_var) - ), - tags$div( - tags$strong("Arm Variable"), - teal.picks::picks_ui(id = ns("arm_var"), picks = arm_var) - ), + tags$div( + tags$strong("Term Variable"), + teal.picks::picks_ui(id = ns("term_var"), picks = term_var) + ), + tags$div( + tags$strong("Arm Variable"), + teal.picks::picks_ui(id = ns("arm_var"), picks = arm_var) + ), selectInput( ns("arm_ref"), "Control", @@ -315,15 +316,15 @@ srv_g_events_term_id <- function(id, output_q <- reactive({ merged_vars <- merge_vars() - validate( - need( - length(merged_vars[["term_var"]]) > 0L, - "Please select a term variable" - ), - need( - length(merged_vars[["arm_var"]]) > 0L, - "Please select an arm variable" - ) + teal::validate_input( + "term_var-variables-selected", + condition = function(x) length(x) > 0L, + message = "Please select a term variable" + ) + teal::validate_input( + "arm_var-variables-selected", + condition = function(x) length(x) > 0L, + message = "Please select an arm variable" ) term_var_name <- merged_vars[["term_var"]][[1L]] @@ -336,19 +337,24 @@ srv_g_events_term_id <- function(id, qenv <- anl_q() ANL <- qenv[["ANL"]] - validate( - need( - is.factor(ANL[[arm_var_name]]), - "Arm Var must be a factor variable. Contact developer." - ), - need( - input$arm_trt %in% ANL[[arm_var_name]] && input$arm_ref %in% ANL[[arm_var_name]], - "Cannot generate plot. The dataset does not contain subjects from both the control and treatment arms." - ), - need( - !isTRUE(input$arm_trt == input$arm_ref), - "Control and Treatment must be different." - ) + teal::validate_input( + "arm_var-variables-selected", + condition = function(x) length(x) > 0L && is.factor(ANL[[x]]), + message = "Arm Var must be a factor variable. Contact developer." + ) + teal::validate_input( + c("arm_trt", "arm_ref", "arm_var-variables-selected"), + condition = function(arm_trt, arm_ref, arm_var_selected) { + arm_trt %in% ANL[[arm_var_selected]] && arm_ref %in% ANL[[arm_var_selected]] + }, + message = "Cannot generate plot. The dataset does not contain subjects from both the control and treatment arms." + ) + teal::validate_input( + c("arm_trt", "arm_ref"), + condition = function(arm_trt, arm_ref) { + !isTRUE(arm_trt == arm_ref) + }, + message = "Control and Treatment must be different." ) teal::validate_has_data(ANL, @@ -359,7 +365,8 @@ srv_g_events_term_id <- function(id, teal.reporter::teal_card(qenv) <- c(teal.reporter::teal_card(qenv), "### Plot") q2 <- within( - qenv, { + qenv, + { plot <- osprey::g_events_term_id( term = ANL[[term_var_name]], id = ANL$USUBJID, diff --git a/man/tm_g_events_term_id.Rd b/man/tm_g_events_term_id.Rd index f7745d13..a09253e5 100644 --- a/man/tm_g_events_term_id.Rd +++ b/man/tm_g_events_term_id.Rd @@ -23,7 +23,8 @@ For \code{modules()} defaults to \code{"root"}. See \code{Details}.} analysis data used in the teal module, needs to be available in the list passed to the \code{data} argument of \code{\link[teal:init]{teal::init()}}.} -\item{term_var}{\link[teal.transform:choices_selected]{teal.transform::choices_selected} object with all available choices +\item{term_var}{Either a (\code{\link[teal.transform:choices_selected]{teal.transform::choices_selected}}) +\code{choices_selected} object or a (\verb{[teal.picks::variables()]}) object with all available choices and pre-selected option names that can be used to specify the term for events} \item{arm_var}{(\code{choices_selected})\cr @@ -46,7 +47,7 @@ vector to indicate default value, minimum and maximum values.} To learn more check \code{vignette("transform-input-data", package = "teal")}.} } \value{ -the \code{\link[teal:teal_modules]{teal::module()}} object. +the \code{\link[teal:module]{teal::module()}} object. } \description{ Display Events by Term plot as a shiny module @@ -82,16 +83,16 @@ app <- init( tm_g_events_term_id( label = "Common AE", dataname = "ADAE", - term_var = choices_selected( - selected = "AEDECOD", + term_var = variables( choices = c( "AEDECOD", "AETERM", "AEHLT", "AELLT", "AEBODSYS" - ) + ), + selected = "AEDECOD" ), - arm_var = choices_selected( - selected = "ACTARMCD", - choices = c("ACTARM", "ACTARMCD") + arm_var = variables( + choices = c("ACTARM", "ACTARMCD"), + selected = "ACTARMCD" ), plot_height = c(600, 200, 2000) ) diff --git a/tests/testthat/test-shinytest2-tm_g_events_term_id.R b/tests/testthat/test-shinytest2-tm_g_events_term_id.R index a9fdb1e3..8bbaee3a 100644 --- a/tests/testthat/test-shinytest2-tm_g_events_term_id.R +++ b/tests/testthat/test-shinytest2-tm_g_events_term_id.R @@ -14,22 +14,15 @@ app_driver_tm_g_events_term_id <- function() { data = data, modules = tm_g_events_term_id( label = "Common AE", - term_var = teal.picks::picks( - teal.picks::datasets("ADAE"), - teal.picks::variables( - choices = c("AEDECOD", "AETERM", "AEHLT"), - selected = "AEDECOD", - multiple = FALSE - ) + term_var = teal.picks::variables( + choices = c("AEDECOD", "AETERM", "AEHLT"), + selected = "AEDECOD" ), - arm_var = teal.picks::picks( - teal.picks::datasets("ADSL"), - teal.picks::variables( - choices = c("ACTARMCD", "ACTARM"), - selected = "ACTARMCD", - multiple = FALSE - ) - ) + arm_var = teal.picks::variables( + choices = c("ACTARMCD", "ACTARM"), + selected = "ACTARMCD" + ), + dataname = "ADAE" ) ) ) diff --git a/tests/testthat/test-tm_g_events_term_id.R b/tests/testthat/test-tm_g_events_term_id.R index 0d4376e8..4bb4a02f 100644 --- a/tests/testthat/test-tm_g_events_term_id.R +++ b/tests/testthat/test-tm_g_events_term_id.R @@ -3,6 +3,7 @@ testthat::test_that("tm_g_events_term_id builds a teal module with picks encodin mod <- tm_g_events_term_id( label = "Common AE", + dataname = "ADAE", term_var = teal.picks::picks( teal.picks::datasets("ADAE"), teal.picks::variables( @@ -20,10 +21,10 @@ testthat::test_that("tm_g_events_term_id builds a teal module with picks encodin ) testthat::expect_s3_class(mod, "teal_module") testthat::expect_identical(mod$server, srv_g_events_term_id) - testthat::expect_equal(mod$datanames, c("ADAE", "ADSL")) + testthat::expect_equal(mod$datanames, c("ADSL", "ADAE")) }) -testthat::test_that("tm_g_events_term_id rejects multiple variable selection", { +testthat::test_that("tm_g_events_term_id coerces multiple variable selection", { testthat::skip_if_not_installed("teal.picks") term_var <- teal.picks::picks( @@ -42,12 +43,14 @@ testthat::test_that("tm_g_events_term_id rejects multiple variable selection", { ) ) - testthat::expect_error( + mod <- testthat::expect_warning( tm_g_events_term_id( label = "Common AE", + dataname = "ADAE", term_var = term_var, arm_var = arm_var ), - "`term_var` must use variables\\(\\.\\.\\., multiple = FALSE\\)" + "accepts only a single variable selection" ) + testthat::expect_false(teal.picks::is_pick_multiple(mod$ui_args$term_var$variables)) }) From 4f37d5ae48433ac2568c026652a243dba840a99a Mon Sep 17 00:00:00 2001 From: osenan Date: Tue, 14 Jul 2026 12:59:31 +0200 Subject: [PATCH 04/16] chore: fix lintrs --- R/tm_g_events_term_id.R | 3 ++- tests/testthat/test-shinytest2-tm_g_events_term_id.R | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/R/tm_g_events_term_id.R b/R/tm_g_events_term_id.R index e8353ca5..5bc6c60d 100644 --- a/R/tm_g_events_term_id.R +++ b/R/tm_g_events_term_id.R @@ -347,7 +347,8 @@ srv_g_events_term_id <- function(id, condition = function(arm_trt, arm_ref, arm_var_selected) { arm_trt %in% ANL[[arm_var_selected]] && arm_ref %in% ANL[[arm_var_selected]] }, - message = "Cannot generate plot. The dataset does not contain subjects from both the control and treatment arms." + message = + "Cannot generate plot. The dataset does not contain subjects from both the control and treatment arms." ) teal::validate_input( c("arm_trt", "arm_ref"), diff --git a/tests/testthat/test-shinytest2-tm_g_events_term_id.R b/tests/testthat/test-shinytest2-tm_g_events_term_id.R index 8bbaee3a..1db27c6d 100644 --- a/tests/testthat/test-shinytest2-tm_g_events_term_id.R +++ b/tests/testthat/test-shinytest2-tm_g_events_term_id.R @@ -1,4 +1,4 @@ -create_tm_g_events_term_id_data <- function() { +create_tm_g_events_term_id_data <- function() { # nolint: object_length_linter. data <- within(teal.data::teal_data(), { ADSL <- teal.data::rADSL ADAE <- teal.data::rADAE From 87948a79658a6b8466b784db2635d97f402d270f Mon Sep 17 00:00:00 2001 From: osenan Date: Tue, 14 Jul 2026 13:15:24 +0200 Subject: [PATCH 05/16] chore: fix R CMD checks --- R/tm_g_events_term_id.R | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/R/tm_g_events_term_id.R b/R/tm_g_events_term_id.R index 5bc6c60d..a54f9363 100644 --- a/R/tm_g_events_term_id.R +++ b/R/tm_g_events_term_id.R @@ -365,6 +365,18 @@ srv_g_events_term_id <- function(id, teal.reporter::teal_card(qenv) <- c(teal.reporter::teal_card(qenv), "### Plot") + # This is redundant only added to avoid R CMD NOTES + arm_ref <- input$arm_ref + arm_trt <- input$arm_trt + sort_by <- input$sort + rate_range <- input$raterange + diff_range <- input$diffrange + reversed <- input$reverse + conf_level <- input$conf_level + diff_ci_method <- input$diff_ci_method + axis_side <- input$axis + fontsize <- font_size() + q2 <- within( qenv, { From e3ed6c0058eacd025db19db95c5e3f24417fa87d Mon Sep 17 00:00:00 2001 From: osenan Date: Tue, 14 Jul 2026 13:33:16 +0200 Subject: [PATCH 06/16] chore: remove R CMD checks --- NAMESPACE | 1 + R/teal.osprey.R | 2 +- R/tm_g_butterfly.R | 8 +++++++- R/tm_g_swimlane.R | 1 + R/tm_g_waterfall.R | 1 + man/tm_g_butterfly.Rd | 3 ++- 6 files changed, 13 insertions(+), 3 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 9d07d672..37d44b41 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -19,4 +19,5 @@ import(formatters) import(osprey) import(shiny) import(teal) +import(teal.picks) import(teal.transform) diff --git a/R/teal.osprey.R b/R/teal.osprey.R index 1831c221..21269051 100644 --- a/R/teal.osprey.R +++ b/R/teal.osprey.R @@ -4,6 +4,6 @@ #' the analysis functions from `osprey` package. See package [website](https://insightsengineering.github.io/osprey/). #' This enables `teal` app developers to easily create applications making use of the `osprey` analysis functions. #' -#' @import dplyr osprey shiny teal formatters teal.transform +#' @import dplyr osprey shiny teal formatters teal.transform teal.picks #' @keywords internal "_PACKAGE" diff --git a/R/tm_g_butterfly.R b/R/tm_g_butterfly.R index d83c2445..32e50f30 100644 --- a/R/tm_g_butterfly.R +++ b/R/tm_g_butterfly.R @@ -116,7 +116,7 @@ tm_g_butterfly <- function(label, color_by_var, count_by_var, facet_var = NULL, - sort_by_var = values( + sort_by_var = teal.picks::values( choices = c("count", "alphabetical"), selected = "count" ), @@ -478,6 +478,12 @@ srv_g_butterfly <- function( }) ) + # This is redundant, only added to avoid NOTE in R CMD check + right <- q1[["right"]] + right_name <- q1[["right_name"]] + left <- q1[["left"]] + left_name <- q1[["left_name"]] + teal.reporter::teal_card(q1) <- c(teal.reporter::teal_card(q1), "### Plot") if (!is.null(filter_var_name) || !is.null(facet_var_name) || !is.null(sort_by_var_name)) { diff --git a/R/tm_g_swimlane.R b/R/tm_g_swimlane.R index ee405ecf..b60163db 100644 --- a/R/tm_g_swimlane.R +++ b/R/tm_g_swimlane.R @@ -251,6 +251,7 @@ srv_g_swimlane <- function(id, output_q <- reactive({ obj <- data() + ANL <- NULL # to avoid triggering NOTE on R CMD check teal.reporter::teal_card(obj) <- c( teal.reporter::teal_card(obj), diff --git a/R/tm_g_waterfall.R b/R/tm_g_waterfall.R index cd07fcdd..09a59f39 100644 --- a/R/tm_g_waterfall.R +++ b/R/tm_g_waterfall.R @@ -296,6 +296,7 @@ srv_g_waterfall <- function(id, output_q <- reactive({ obj <- data() + anl <- NULL # to avoid triggering NOTE on R CMD check teal.reporter::teal_card(obj) <- c( teal.reporter::teal_card(obj), diff --git a/man/tm_g_butterfly.Rd b/man/tm_g_butterfly.Rd index 57283260..247966a2 100644 --- a/man/tm_g_butterfly.Rd +++ b/man/tm_g_butterfly.Rd @@ -14,7 +14,8 @@ tm_g_butterfly( color_by_var, count_by_var, facet_var = NULL, - sort_by_var = values(choices = c("count", "alphabetical"), selected = "count"), + sort_by_var = teal.picks::values(choices = c("count", "alphabetical"), selected = + "count"), legend_on = TRUE, plot_height = c(600L, 200L, 2000L), plot_width = NULL, From 7f22cedb4c7c83dccc3b2fdafecb6ae093164747 Mon Sep 17 00:00:00 2001 From: osenan Date: Tue, 14 Jul 2026 13:56:21 +0200 Subject: [PATCH 07/16] chore: fix lintrs --- R/tm_g_waterfall.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/tm_g_waterfall.R b/R/tm_g_waterfall.R index 09a59f39..430bbec7 100644 --- a/R/tm_g_waterfall.R +++ b/R/tm_g_waterfall.R @@ -243,7 +243,7 @@ ui_g_waterfall <- function(id, } #' @keywords internal -.waterfall_picks_selected_values <- function(selector_state) { +.waterfall_picks_selected_values <- function(selector_state) { # nolint: object_length_linter. if (is.null(selector_state) || is.null(selector_state$values)) { return(character()) } From da7cc2503d624ff881116ac92975a018b8f477c8 Mon Sep 17 00:00:00 2001 From: osenan Date: Tue, 14 Jul 2026 13:57:09 +0200 Subject: [PATCH 08/16] chore: remove failing URL from README --- README.md | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/README.md b/README.md index 130b8bc0..d3d3042a 100644 --- a/README.md +++ b/README.md @@ -35,17 +35,3 @@ pak::pkg_install("insightsengineering/teal.osprey@*release") # install.packages("pak") pak::pkg_install("insightsengineering/teal.osprey") ``` - -## Stargazers and Forkers - -### Stargazers over time - -[![Stargazers over time](https://starchart.cc/insightsengineering/teal.osprey.svg)](https://starchart.cc/insightsengineering/teal.osprey) - -### Stargazers - -[![Stargazers repo roster for @insightsengineering/teal.osprey](https://reporoster.com/stars/insightsengineering/teal.osprey)](https://github.com/insightsengineering/teal.osprey/stargazers) - -### Forkers - -[![Forkers repo roster for @insightsengineering/teal.osprey](https://reporoster.com/forks/insightsengineering/teal.osprey)](https://github.com/insightsengineering/teal.osprey/network/members) From 2bb35c8ad27b0a7ebbab2061f073ef57d4cc4ab6 Mon Sep 17 00:00:00 2001 From: Oriol Senan <35930244+osenan@users.noreply.github.com> Date: Tue, 14 Jul 2026 15:42:15 +0200 Subject: [PATCH 09/16] Update R/tm_g_events_term_id.R MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Lluís Revilla <185338939+llrs-roche@users.noreply.github.com> Signed-off-by: Oriol Senan <35930244+osenan@users.noreply.github.com> --- R/tm_g_events_term_id.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/tm_g_events_term_id.R b/R/tm_g_events_term_id.R index a54f9363..904b7de0 100644 --- a/R/tm_g_events_term_id.R +++ b/R/tm_g_events_term_id.R @@ -97,7 +97,7 @@ tm_g_events_term_id <- function(label, args <- as.list(environment()) module( - datanames = c("ADSL", dataname), + datanames = unique(c("ADSL", dataname)), label = label, server = srv_g_events_term_id, server_args = args[names(args) %in% names(formals(srv_g_events_term_id))], From 04f6e470fae36757673dc1960020b5726c7fbe1a Mon Sep 17 00:00:00 2001 From: Oriol Senan <35930244+osenan@users.noreply.github.com> Date: Tue, 14 Jul 2026 15:46:44 +0200 Subject: [PATCH 10/16] Update R/tm_g_events_term_id.R MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Lluís Revilla <185338939+llrs-roche@users.noreply.github.com> Signed-off-by: Oriol Senan <35930244+osenan@users.noreply.github.com> --- R/tm_g_events_term_id.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R/tm_g_events_term_id.R b/R/tm_g_events_term_id.R index 904b7de0..b2b18ff6 100644 --- a/R/tm_g_events_term_id.R +++ b/R/tm_g_events_term_id.R @@ -365,7 +365,8 @@ srv_g_events_term_id <- function(id, teal.reporter::teal_card(qenv) <- c(teal.reporter::teal_card(qenv), "### Plot") - # This is redundant only added to avoid R CMD NOTES + # This is redundant only added to avoid R CMD NOTE on no visible binding: " + arm_ref <- arm_trt <- sort_by <- rate_range <- diff_range <- reversed <- conf_level <- diff_ci_method <- axis_side <- fontsize <- NULL arm_ref <- input$arm_ref arm_trt <- input$arm_trt sort_by <- input$sort From a49584fe52bb427bafd182c0ebe6d587c0cb859b Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 13:49:18 +0000 Subject: [PATCH 11/16] [skip style] [skip vbump] Restyle files --- R/tm_g_events_term_id.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/tm_g_events_term_id.R b/R/tm_g_events_term_id.R index b2b18ff6..1a050c22 100644 --- a/R/tm_g_events_term_id.R +++ b/R/tm_g_events_term_id.R @@ -366,7 +366,7 @@ srv_g_events_term_id <- function(id, teal.reporter::teal_card(qenv) <- c(teal.reporter::teal_card(qenv), "### Plot") # This is redundant only added to avoid R CMD NOTE on no visible binding: " - arm_ref <- arm_trt <- sort_by <- rate_range <- diff_range <- reversed <- conf_level <- diff_ci_method <- axis_side <- fontsize <- NULL + arm_ref <- arm_trt <- sort_by <- rate_range <- diff_range <- reversed <- conf_level <- diff_ci_method <- axis_side <- fontsize <- NULL arm_ref <- input$arm_ref arm_trt <- input$arm_trt sort_by <- input$sort From 0f1ddde853aed413f02f468467fdfea7cf94ae44 Mon Sep 17 00:00:00 2001 From: osenan Date: Tue, 14 Jul 2026 15:59:47 +0200 Subject: [PATCH 12/16] chore: remove NOTEs in a better way --- R/tm_g_events_term_id.R | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/R/tm_g_events_term_id.R b/R/tm_g_events_term_id.R index 1a050c22..f51193ae 100644 --- a/R/tm_g_events_term_id.R +++ b/R/tm_g_events_term_id.R @@ -366,17 +366,7 @@ srv_g_events_term_id <- function(id, teal.reporter::teal_card(qenv) <- c(teal.reporter::teal_card(qenv), "### Plot") # This is redundant only added to avoid R CMD NOTE on no visible binding: " - arm_ref <- arm_trt <- sort_by <- rate_range <- diff_range <- reversed <- conf_level <- diff_ci_method <- axis_side <- fontsize <- NULL - arm_ref <- input$arm_ref - arm_trt <- input$arm_trt - sort_by <- input$sort - rate_range <- input$raterange - diff_range <- input$diffrange - reversed <- input$reverse - conf_level <- input$conf_level - diff_ci_method <- input$diff_ci_method - axis_side <- input$axis - fontsize <- font_size() + arm_ref <- arm_trt <- sort_by <- rate_range <- diff_range <- reversed <- conf_level <- diff_ci_method <- axis_side <- fontsize <- NULL # nolint: line_length. q2 <- within( qenv, From 73b94d22ebdf8ae95f59ccf1effbdd8d63f73152 Mon Sep 17 00:00:00 2001 From: osenan Date: Tue, 14 Jul 2026 15:57:29 +0200 Subject: [PATCH 13/16] chore: fix CI lintr issues --- R/tm_g_events_term_id.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/tm_g_events_term_id.R b/R/tm_g_events_term_id.R index f51193ae..db0db545 100644 --- a/R/tm_g_events_term_id.R +++ b/R/tm_g_events_term_id.R @@ -366,7 +366,7 @@ srv_g_events_term_id <- function(id, teal.reporter::teal_card(qenv) <- c(teal.reporter::teal_card(qenv), "### Plot") # This is redundant only added to avoid R CMD NOTE on no visible binding: " - arm_ref <- arm_trt <- sort_by <- rate_range <- diff_range <- reversed <- conf_level <- diff_ci_method <- axis_side <- fontsize <- NULL # nolint: line_length. + arm_ref <- arm_trt <- sort_by <- rate_range <- diff_range <- reversed <- conf_level <- diff_ci_method <- axis_side <- fontsize <- NULL # nolint: line_length_linter. q2 <- within( qenv, From 7758d04eacf0f74610821aed7c2b28e583da60b2 Mon Sep 17 00:00:00 2001 From: osenan Date: Thu, 16 Jul 2026 10:43:47 +0200 Subject: [PATCH 14/16] refactor: add parent_dataname argument and remove usage of --- R/tm_g_events_term_id.R | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/R/tm_g_events_term_id.R b/R/tm_g_events_term_id.R index db0db545..8ca9c2e5 100644 --- a/R/tm_g_events_term_id.R +++ b/R/tm_g_events_term_id.R @@ -34,6 +34,7 @@ #' tm_g_events_term_id( #' label = "Common AE", #' dataname = "ADAE", +#' parent_dataname = "ADSL", #' term_var = variables( #' choices = c( #' "AEDECOD", "AETERM", @@ -55,6 +56,7 @@ #' tm_g_events_term_id <- function(label, dataname, + parent_dataname, term_var, arm_var, fontsize = c(5, 3, 7), @@ -64,12 +66,13 @@ tm_g_events_term_id <- function(label, message("Initializing tm_g_events_term_id") checkmate::assert_string(label) checkmate::assert_string(dataname) + checkmate::assert_string(parent_dataname) term_var <- migrate_choices_selected_to_variables(term_var) arm_var <- migrate_choices_selected_to_variables(arm_var) term_var <- create_picks_helper(teal.picks::datasets(dataname, dataname), term_var) - arm_var <- create_picks_helper(teal.picks::datasets("ADSL", "ADSL"), arm_var) + arm_var <- create_picks_helper(teal.picks::datasets(parent_dataname, parent_dataname), arm_var) term_var <- force_pick_to_single(term_var, "term_var") arm_var <- force_pick_to_single(arm_var, "arm_var") @@ -203,13 +206,14 @@ ui_g_events_term_id <- function(id, srv_g_events_term_id <- function(id, data, dataname, + parent_dataname, label, term_var, arm_var, plot_height, plot_width) { checkmate::assert_class(data, "reactive") - checkmate::assert_class(shiny::isolate(data()), "teal_data") + checkmate::assert_class(isolate(data()), "teal_data") moduleServer(id, function(input, output, session) { teal.logger::log_shiny_input_changes(input, namespace = "teal.osprey") @@ -332,7 +336,6 @@ srv_g_events_term_id <- function(id, arm_selector <- anl_selectors$arm_var() arm_var_orig <- arm_selector$variables$selected - arm_dataset <- arm_selector$datasets$selected qenv <- anl_q() ANL <- qenv[["ANL"]] @@ -375,7 +378,7 @@ srv_g_events_term_id <- function(id, term = ANL[[term_var_name]], id = ANL$USUBJID, arm = ANL[[arm_var_name]], - arm_N = table(get(arm_dataset)[[arm_var_orig]]), + arm_N = table(parent_dataname[[arm_var_orig]]), ref = arm_ref, trt = arm_trt, sort_by = sort_by, @@ -391,7 +394,7 @@ srv_g_events_term_id <- function(id, }, term_var_name = term_var_name, arm_var_name = arm_var_name, - arm_dataset = arm_dataset, + parent_dataname = as.name(parent_dataname), arm_var_orig = arm_var_orig, arm_ref = input$arm_ref, arm_trt = input$arm_trt, From 052e540963002a6d08e599dc76a2310ee40a0c11 Mon Sep 17 00:00:00 2001 From: osenan Date: Thu, 16 Jul 2026 11:32:04 +0200 Subject: [PATCH 15/16] refactor: use utils function to pass datanames to module --- R/tm_g_events_term_id.R | 6 +++--- R/utils_picks.R | 21 +++++++++++++++++++++ man/tm_g_events_term_id.Rd | 6 ++++-- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/R/tm_g_events_term_id.R b/R/tm_g_events_term_id.R index 8ca9c2e5..76281b3e 100644 --- a/R/tm_g_events_term_id.R +++ b/R/tm_g_events_term_id.R @@ -7,8 +7,8 @@ #' @inheritParams teal.widgets::standard_layout #' @inheritParams teal::module #' @inheritParams argument_convention -#' @param term_var Either a ([`teal.transform::choices_selected`]) -#' `choices_selected` object or a (`[teal.picks::variables()]`) object with all available choices +#' @param term_var Either a (`[teal.picks::variables()]`) object or +#' a ([`teal.transform::choices_selected`]) `choices_selected` object with all available choices #' and pre-selected option names that can be used to specify the term for events #' #' @inherit argument_convention return @@ -100,7 +100,7 @@ tm_g_events_term_id <- function(label, args <- as.list(environment()) module( - datanames = unique(c("ADSL", dataname)), + datanames = .picks_datanames(list(term_var, arm_var)), label = label, server = srv_g_events_term_id, server_args = args[names(args) %in% names(formals(srv_g_events_term_id))], diff --git a/R/utils_picks.R b/R/utils_picks.R index ea059288..d00b68e8 100644 --- a/R/utils_picks.R +++ b/R/utils_picks.R @@ -1,3 +1,24 @@ +#' Extract datanames from list of picks +#' @keywords internal +#' @noRd +.picks_datanames <- function(x) { + checkmate::assert_list(x, c("picks", "NULL")) + datanames_list <- lapply(x, function(x) { + if (is.character(x$datasets$choices)) { + x$datasets$choices + } else { + NULL + } + }) + + if (any(vapply(datanames_list, is.null, logical(1)))) { + "all" + } else { + unique(unlist(datanames_list)) + } +} + + #' Create a reactive that sets plot dimensions on a `teal_card` #' #' This is a convenience function that creates a reactive expression that diff --git a/man/tm_g_events_term_id.Rd b/man/tm_g_events_term_id.Rd index a09253e5..5aeb6664 100644 --- a/man/tm_g_events_term_id.Rd +++ b/man/tm_g_events_term_id.Rd @@ -7,6 +7,7 @@ tm_g_events_term_id( label, dataname, + parent_dataname, term_var, arm_var, fontsize = c(5, 3, 7), @@ -23,8 +24,8 @@ For \code{modules()} defaults to \code{"root"}. See \code{Details}.} analysis data used in the teal module, needs to be available in the list passed to the \code{data} argument of \code{\link[teal:init]{teal::init()}}.} -\item{term_var}{Either a (\code{\link[teal.transform:choices_selected]{teal.transform::choices_selected}}) -\code{choices_selected} object or a (\verb{[teal.picks::variables()]}) object with all available choices +\item{term_var}{Either a (\verb{[teal.picks::variables()]}) object or +a (\code{\link[teal.transform:choices_selected]{teal.transform::choices_selected}}) \code{choices_selected} object with all available choices and pre-selected option names that can be used to specify the term for events} \item{arm_var}{(\code{choices_selected})\cr @@ -83,6 +84,7 @@ app <- init( tm_g_events_term_id( label = "Common AE", dataname = "ADAE", + parent_dataname = "ADSL", term_var = variables( choices = c( "AEDECOD", "AETERM", From 512b2cf1c7bb2e9d77f3439eeaf6e92cfa8f8154 Mon Sep 17 00:00:00 2001 From: osenan Date: Thu, 16 Jul 2026 11:41:05 +0200 Subject: [PATCH 16/16] chore: fix R CMD notes and errors --- R/tm_g_ae_sub.R | 2 ++ R/tm_g_events_term_id.R | 4 +++- man/tm_g_events_term_id.Rd | 4 ++++ tests/testthat/test-shinytest2-tm_g_events_term_id.R | 3 ++- tests/testthat/test-tm_g_events_term_id.R | 4 +++- 5 files changed, 14 insertions(+), 3 deletions(-) diff --git a/R/tm_g_ae_sub.R b/R/tm_g_ae_sub.R index 223ed8a9..d1595ebc 100644 --- a/R/tm_g_ae_sub.R +++ b/R/tm_g_ae_sub.R @@ -341,6 +341,8 @@ srv_g_ae_sub <- function(id, teal.reporter::teal_card(q1) <- c(teal.reporter::teal_card(q1), "### Plot") + arm_n <- NULL # to avoid R CMD NOTE on no visible binding + q2 <- within( q1, { diff --git a/R/tm_g_events_term_id.R b/R/tm_g_events_term_id.R index 76281b3e..afe8231f 100644 --- a/R/tm_g_events_term_id.R +++ b/R/tm_g_events_term_id.R @@ -10,7 +10,9 @@ #' @param term_var Either a (`[teal.picks::variables()]`) object or #' a ([`teal.transform::choices_selected`]) `choices_selected` object with all available choices #' and pre-selected option names that can be used to specify the term for events -#' +#' @param parent_dataname (`character(1)`)\cr +#' analysis data used form arm_var in the teal module, needs to be +#' available in the list passed to the `data` argument of [teal::init()]. #' @inherit argument_convention return #' @inheritSection teal::example_module Reporting #' diff --git a/man/tm_g_events_term_id.Rd b/man/tm_g_events_term_id.Rd index 5aeb6664..e7c0428a 100644 --- a/man/tm_g_events_term_id.Rd +++ b/man/tm_g_events_term_id.Rd @@ -24,6 +24,10 @@ For \code{modules()} defaults to \code{"root"}. See \code{Details}.} analysis data used in the teal module, needs to be available in the list passed to the \code{data} argument of \code{\link[teal:init]{teal::init()}}.} +\item{parent_dataname}{(\code{character(1)})\cr +analysis data used form arm_var in the teal module, needs to be +available in the list passed to the \code{data} argument of \code{\link[teal:init]{teal::init()}}.} + \item{term_var}{Either a (\verb{[teal.picks::variables()]}) object or a (\code{\link[teal.transform:choices_selected]{teal.transform::choices_selected}}) \code{choices_selected} object with all available choices and pre-selected option names that can be used to specify the term for events} diff --git a/tests/testthat/test-shinytest2-tm_g_events_term_id.R b/tests/testthat/test-shinytest2-tm_g_events_term_id.R index 1db27c6d..399db05b 100644 --- a/tests/testthat/test-shinytest2-tm_g_events_term_id.R +++ b/tests/testthat/test-shinytest2-tm_g_events_term_id.R @@ -22,7 +22,8 @@ app_driver_tm_g_events_term_id <- function() { choices = c("ACTARMCD", "ACTARM"), selected = "ACTARMCD" ), - dataname = "ADAE" + dataname = "ADAE", + parent_dataname = "ADSL" ) ) ) diff --git a/tests/testthat/test-tm_g_events_term_id.R b/tests/testthat/test-tm_g_events_term_id.R index 4bb4a02f..1b57385a 100644 --- a/tests/testthat/test-tm_g_events_term_id.R +++ b/tests/testthat/test-tm_g_events_term_id.R @@ -4,6 +4,7 @@ testthat::test_that("tm_g_events_term_id builds a teal module with picks encodin mod <- tm_g_events_term_id( label = "Common AE", dataname = "ADAE", + parent_dataname = "ADSL", term_var = teal.picks::picks( teal.picks::datasets("ADAE"), teal.picks::variables( @@ -21,7 +22,7 @@ testthat::test_that("tm_g_events_term_id builds a teal module with picks encodin ) testthat::expect_s3_class(mod, "teal_module") testthat::expect_identical(mod$server, srv_g_events_term_id) - testthat::expect_equal(mod$datanames, c("ADSL", "ADAE")) + testthat::expect_equal(mod$datanames, c("ADAE", "ADSL")) }) testthat::test_that("tm_g_events_term_id coerces multiple variable selection", { @@ -47,6 +48,7 @@ testthat::test_that("tm_g_events_term_id coerces multiple variable selection", { tm_g_events_term_id( label = "Common AE", dataname = "ADAE", + parent_dataname = "ADSL", term_var = term_var, arm_var = arm_var ),