diff --git a/NAMESPACE b/NAMESPACE index 18f3d101..37d44b41 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) diff --git a/R/teal.osprey.R b/R/teal.osprey.R index 13d3fb10..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.picks teal.transform +#' @import dplyr osprey shiny teal formatters teal.transform teal.picks #' @keywords internal "_PACKAGE" 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_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_events_term_id.R b/R/tm_g_events_term_id.R index 6d4db895..afe8231f 100644 --- a/R/tm_g_events_term_id.R +++ b/R/tm_g_events_term_id.R @@ -1,16 +1,18 @@ #' 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 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 #' @@ -33,19 +35,18 @@ #' 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", +#' parent_dataname = "ADSL", +#' term_var = variables( +#' choices = c( +#' "AEDECOD", "AETERM", +#' "AEHLT", "AELLT", "AEBODSYS" +#' ), +#' selected = "AEDECOD" #' ), -#' arm_var = teal.picks::picks( -#' teal.picks::datasets("ADSL"), -#' teal.picks::variables( -#' choices = teal.picks::is_categorical(min.len = 2), -#' selected = "ACTARMCD" -#' ) +#' arm_var = variables( +#' choices = c("ACTARM", "ACTARMCD"), +#' selected = "ACTARMCD" #' ), #' plot_height = c(600, 200, 2000) #' ) @@ -55,37 +56,28 @@ #' 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, + parent_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_string(dataname) + checkmate::assert_string(parent_dataname) - 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)" - ) + 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(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") checkmate::assert( checkmate::check_number(fontsize, finite = TRUE), @@ -97,33 +89,29 @@ 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( + datanames = .picks_datanames(list(term_var, arm_var)), 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))], - transformators = transformators, - datanames = all_datanames + ui = ui_g_events_term_id, + ui_args = args[names(args) %in% names(formals(ui_g_events_term_id))], + transformators = transformators ) } -#' @keywords internal ui_g_events_term_id <- function(id, term_var, arm_var, @@ -134,24 +122,23 @@ ui_g_events_term_id <- function(id, plot_decorate_output(id = ns(NULL)) ), encoding = tags$div( - tags$label("Encodings", class = "text-primary"), tags$br(), tags$div( - tags$label("Term variable"), + tags$strong("Term Variable"), teal.picks::picks_ui(id = ns("term_var"), picks = term_var) ), tags$div( - tags$label("Arm variable"), + tags$strong("Arm Variable"), teal.picks::picks_ui(id = ns("arm_var"), picks = arm_var) ), selectInput( ns("arm_ref"), "Control", - choices = NULL + choices = "" ), selectInput( ns("arm_trt"), "Treatment", - choices = NULL + choices = "" ), teal.widgets::optionalSelectInput( ns("sort"), @@ -203,8 +190,7 @@ 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 ) @@ -219,9 +205,11 @@ ui_g_events_term_id <- function(id, ) } -#' @keywords internal srv_g_events_term_id <- function(id, data, + dataname, + parent_dataname, + label, term_var, arm_var, plot_height, @@ -233,7 +221,6 @@ srv_g_events_term_id <- function(id, 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 ) @@ -259,6 +246,47 @@ srv_g_events_term_id <- function(id, 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 + ) + font_size <- decorate_output$font_size + pws <- decorate_output$pws + + 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) + ) + ) + }) + + 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(anl_selectors$arm_var(), { arm_selector <- anl_selectors$arm_var() @@ -270,7 +298,11 @@ srv_g_events_term_id <- function(id, arm_data <- data()[[arm_dataset]] choices <- levels(arm_data[[arm_var_name]]) - trt_index <- if (length(choices) == 1L) 1L else 2L + if (length(choices) == 1) { + trt_index <- 1 + } else { + trt_index <- 2 + } updateSelectInput( session, @@ -288,61 +320,17 @@ 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] - ) - ) - }, - 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" - ) + 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]] @@ -350,56 +338,79 @@ 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"]] - 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]], + 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." - ), - need( - !isTRUE(input$arm_trt == input$arm_ref), - "Control and Treatment must be different." - ) + ) + 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, + 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( + # 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_linter. + + q2 <- within( qenv, - code = bquote( + { plot <- osprey::g_events_term_id( - term = ANL[[.(term_var_name)]], + 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()), + arm = ANL[[arm_var_name]], + arm_N = table(parent_dataname[[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, + parent_dataname = as.name(parent_dataname), + 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"]]) 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/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..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()) } @@ -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/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/README.md b/README.md index b8208351..d3d3042a 100644 --- a/README.md +++ b/README.md @@ -35,9 +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) 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, diff --git a/man/tm_g_events_term_id.Rd b/man/tm_g_events_term_id.Rd index e3af6ce8..e7c0428a 100644 --- a/man/tm_g_events_term_id.Rd +++ b/man/tm_g_events_term_id.Rd @@ -1,30 +1,15 @@ % 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, + parent_dataname, + term_var, + arm_var, fontsize = c(5, 3, 7), plot_height = c(600L, 200L, 2000L), plot_width = NULL, @@ -35,12 +20,22 @@ 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{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} -\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{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 +50,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. } \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 +87,18 @@ 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", + parent_dataname = "ADSL", + term_var = variables( + choices = c( + "AEDECOD", "AETERM", + "AEHLT", "AELLT", "AEBODSYS" + ), + selected = "AEDECOD" ), - arm_var = teal.picks::picks( - teal.picks::datasets("ADSL"), - teal.picks::variables( - choices = teal.picks::is_categorical(min.len = 2), - selected = "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..399db05b 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 @@ -14,22 +14,16 @@ 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", + 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 0d4376e8..1b57385a 100644 --- a/tests/testthat/test-tm_g_events_term_id.R +++ b/tests/testthat/test-tm_g_events_term_id.R @@ -3,6 +3,8 @@ 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( @@ -23,7 +25,7 @@ testthat::test_that("tm_g_events_term_id builds a teal module with picks encodin testthat::expect_equal(mod$datanames, c("ADAE", "ADSL")) }) -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 +44,15 @@ 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", + parent_dataname = "ADSL", 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)) })