From e71d9fa3814871518b3dccbc902cf0bac61f6cdc Mon Sep 17 00:00:00 2001 From: Aleksander Chlebowski Date: Mon, 27 Nov 2023 16:12:45 +0100 Subject: [PATCH 1/4] change vignette authorship --- vignettes/creating-custom-modules.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/creating-custom-modules.Rmd b/vignettes/creating-custom-modules.Rmd index dbaa62b1e4..5be9051450 100644 --- a/vignettes/creating-custom-modules.Rmd +++ b/vignettes/creating-custom-modules.Rmd @@ -1,6 +1,6 @@ --- title: "Creating Custom Modules" -author: "Nikolas Burkoff" +author: "NEST CoreDev" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Creating Custom Modules} From 18620f7db5cf47d2cea60a7be73e79a06ecbdd2b Mon Sep 17 00:00:00 2001 From: Aleksander Chlebowski Date: Mon, 27 Nov 2023 16:13:35 +0100 Subject: [PATCH 2/4] fix line breaks --- vignettes/creating-custom-modules.Rmd | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/vignettes/creating-custom-modules.Rmd b/vignettes/creating-custom-modules.Rmd index 5be9051450..93f5c55cda 100644 --- a/vignettes/creating-custom-modules.Rmd +++ b/vignettes/creating-custom-modules.Rmd @@ -10,7 +10,8 @@ vignette: > ## Introduction -The `teal` framework provides a large number of analysis modules to be incorporated into `teal` applications. However, it is also possible to create your own modules using the `module` function. +The `teal` framework provides a large number of analysis modules to be incorporated into `teal` applications. +However, it is also possible to create your own modules using the `module` function. Here is an implementation of a simple module: @@ -44,9 +45,11 @@ which can be added into `teal` apps using `example_module(label = "Label for tab ### UI function -This function contains the UI required for the module. It should be a function with at least the arguments `id`. It can also contain the argument `data` for access to the application data. See the server section below for more details. +This function contains the UI required for the module. It should be a function with at least the arguments `id`. +It can also contain the argument `data` for access to the application data. See the server section below for more details. -The UI function can contain standard UI components alongside additional widgets provided by the `teal.widgets` package. In the example above we are using the `standard_layout` function of `teal.widgets` which generates a layout +The UI function can contain standard UI components alongside additional widgets provided by the `teal.widgets` package. +In the example above we are using the `standard_layout` function of `teal.widgets` which generates a layout including an encoding panel on the left and main output covering the rest of the module's UI. ### Server function @@ -199,7 +202,11 @@ if (interactive()) { ## `shiny` input cycle -When `teal` modules are run inside the `init` the initial shiny input cycle is empty for each of them. In practice, this means that some inputs might be initialized with `NULL` value, unnecessary triggering some observers. A developer has to be aware of this situation as often it will require `shiny::req` or `ignoreInit` argument in observers or `reactive` expressions. This side effect is caused by the `shiny::insertUI` function. We are aware of this inconvenience and have already started to look for a solution. +When `teal` modules are run inside the `init` the initial shiny input cycle is empty for each of them. +In practice, this means that some inputs might be initialized with `NULL` value, unnecessary triggering some observers. +A developer has to be aware of this situation as often it will require `shiny::req` or `ignoreInit` argument in observers or `reactive` expressions. +This side effect is caused by the `shiny::insertUI` function. +We are aware of this inconvenience and have already started to look for a solution. ## Adding reporting to a module Refer to `vignette("adding_support_for_reporting")` to read about adding support for reporting in your `teal` module. From 0ea753c36b9064b28ff13d388e971cb5d62bd6d5 Mon Sep 17 00:00:00 2001 From: Aleksander Chlebowski Date: Mon, 27 Nov 2023 16:46:17 +0100 Subject: [PATCH 3/4] fix example app --- vignettes/creating-custom-modules.Rmd | 105 +++++++++++--------------- 1 file changed, 45 insertions(+), 60 deletions(-) diff --git a/vignettes/creating-custom-modules.Rmd b/vignettes/creating-custom-modules.Rmd index 93f5c55cda..7044b61fe1 100644 --- a/vignettes/creating-custom-modules.Rmd +++ b/vignettes/creating-custom-modules.Rmd @@ -22,16 +22,17 @@ example_module <- function(label = "example teal module") { module( label, server = function(id, data) { - checkmate::assert_class(data, "tdata") + checkmate::assert_class(data, "reactive") + checkmate::assert_class(isolate(data()), "teal_data") moduleServer(id, function(input, output, session) { - output$text <- renderPrint(data[[input$dataname]]()) + output$text <- renderPrint(data()[[input$dataname]]) }) }, ui = function(id, data) { ns <- NS(id) teal.widgets::standard_layout( output = verbatimTextOutput(ns("text")), - encoding = selectInput(ns("dataname"), "Choose a dataset", choices = names(data)) + encoding = selectInput(ns("dataname"), "Choose a dataset", choices = datanames(data)) ) }, datanames = "all" @@ -87,18 +88,14 @@ See the package and function documentation for further details. library(teal) # ui function for the module -# histogram_var is a teal.transform::data_extract_spec object -# specifying which columns of which datasets users can choose -ui_histogram_example <- function(id, histogram_var) { +# allows for selecting dataset and one of its numeric variables +ui_histogram_example <- function(id) { ns <- NS(id) teal.widgets::standard_layout( output = plotOutput(ns("plot")), encoding = div( - teal.transform::data_extract_ui( - id = ns("histogram_var"), - label = "Variable", - data_extract_spec = histogram_var - ) + uiOutput(ns("datasets")), + uiOutput(ns("numerics")) ), # we have a show R code button to show the code needed # to generate the histogram @@ -107,45 +104,49 @@ ui_histogram_example <- function(id, histogram_var) { } # server function for the module -# histogram_var is a teal.transform::data_extract_spec object -# specifying which columns of which datasets users can choose -srv_histogram_example <- function(id, data, histogram_var) { - checkmate::assert_class(data, "tdata") +# presents datasets and numeric variables for selection +# displays a histogram of the selected variable +srv_histogram_example <- function(id, data) { + checkmate::assert_class(data, "reactive") + checkmate::assert_class(isolate(data()), "teal_data") + moduleServer(id, function(input, output, session) { - # get the selected dataset and column from the UI - extracted <- teal.transform::data_extract_srv( - id = "histogram_var", - datasets = data, - data_extract_spec = histogram_var, - join_keys = teal.data::join_keys(data) - ) + ns <- session$ns - dataname <- reactive(extracted()$dataname) - selected <- reactive(extracted()$select) + # present dataset and variable choices + # each selection stored in separate reactive expression + output$datasets <- renderUI({ + selectInput(ns("datasets"), "select dataset", datanames(data())) + }) + dataset <- reactive(input$datasets) + output$numerics <- renderUI({ + req(dataset()) + nums <- vapply(data()[[dataset()]], is.numeric, logical(1L)) + selectInput(ns("numerics"), "select numeric variable", names(nums[nums])) + }) + selected <- reactive(input$numerics) - # the reactive which adds the code to plot the histogram into the qenv + # add plot code plot_code_q <- reactive({ - validate(need(length(selected) == 1, "Please select a variable")) - - # take the filtered data from the data object and add it into the qenv environment - teal.code::new_qenv(tdata2env(data), code = get_code_tdata(data)) %>% - teal.code::eval_code( - substitute( - expr = p <- hist(dataname[, selected]), - env = list( - dataname = as.name(dataname()), - selected = selected() - ) - ) - ) + validate(need(length(dataset()) == 1L, "Please select a dataset")) + validate(need(length(selected()) == 1L, "Please select a variable")) + req(selected() %in% names(data()[[dataset()]])) + + # evaluate plotting expression within data + # inject input values into plotting expression + within( + data(), + p <- hist(dataset[, selected], las = 1), + dataset = as.name(dataset()), selected = selected() + ) }) - # shiny component to view + # view plot output$plot <- renderPlot({ plot_code_q()[["p"]] }) - # Show the R code when user clicks 'Show R Code' button + # code upon clicking 'Show R Code' button teal.widgets::verbatim_popup_srv( id = "rcode", verbatim_content = reactive(teal.code::get_code(plot_code_q())), @@ -154,17 +155,12 @@ srv_histogram_example <- function(id, data, histogram_var) { }) } -# the function which creates the teal module for users -tm_histogram_example <- function(label, histogram_var) { - checkmate::assert_character(label) - checkmate::assert_class(histogram_var, "data_extract_spec") - +# function that creates module instance to use in `teal` app +tm_histogram_example <- function(label) { module( label = label, server = srv_histogram_example, ui = ui_histogram_example, - ui_args = list(histogram_var = histogram_var), - server_args = list(histogram_var = histogram_var), datanames = "all" ) } @@ -179,19 +175,8 @@ An example `teal` application using this module is shown below: library(teal) app <- init( - data = teal_data( - IRIS = iris, - code = "IRIS <- iris" - ), - modules = tm_histogram_example( - label = "Simple Module", - histogram_var = data_extract_spec( - dataname = "IRIS", - select = select_spec( - choices = c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width") - ) - ) - ), + data = teal_data(IRIS = iris, NPK = npk), + modules = tm_histogram_example(label = "Histogram Module"), header = "Simple app with custom histogram module" ) From eaac2f76b0cc97cd661058634a2bf22d0c272317 Mon Sep 17 00:00:00 2001 From: Aleksander Chlebowski Date: Mon, 27 Nov 2023 16:46:47 +0100 Subject: [PATCH 4/4] fix more line breaks --- vignettes/creating-custom-modules.Rmd | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/vignettes/creating-custom-modules.Rmd b/vignettes/creating-custom-modules.Rmd index 7044b61fe1..aa20118191 100644 --- a/vignettes/creating-custom-modules.Rmd +++ b/vignettes/creating-custom-modules.Rmd @@ -46,8 +46,10 @@ which can be added into `teal` apps using `example_module(label = "Label for tab ### UI function -This function contains the UI required for the module. It should be a function with at least the arguments `id`. -It can also contain the argument `data` for access to the application data. See the server section below for more details. +This function contains the UI required for the module. +It should be a function with at least the arguments `id`. +It can also contain the argument `data` for access to the application data. +See the server section below for more details. The UI function can contain standard UI components alongside additional widgets provided by the `teal.widgets` package. In the example above we are using the `standard_layout` function of `teal.widgets` which generates a layout @@ -70,7 +72,8 @@ function(id, ``` -When used inside a `teal` application called with `init`, the `data` argument is a named list of reactive `data.frame`s containing the data after having been filtered through the filter panel. It is of the `tdata` type and can be created using the `new_tdata` function. +When used inside a `teal` application called with `init`, the `data` argument is a named list of reactive `data.frame`s containing the data after having been filtered through the filter panel. +It is of the `tdata` type and can be created using the `new_tdata` function. ## A More Complicated Example