Skip to content

Prototype ddl + tdata - #140

Closed
gogonzo wants to merge 19 commits into
mainfrom
prototype2
Closed

Prototype ddl + tdata#140
gogonzo wants to merge 19 commits into
mainfrom
prototype2

Conversation

@gogonzo

@gogonzo gogonzo commented Mar 15, 2023

Copy link
Copy Markdown
Contributor

This branch presents prototype of the new tdata object being a replacement to the TealData and friends. Look at the removed and added number of lines to see why ;)

This branch two independent issues are addressed.

  1. Easier way of specifying connectors
  2. Unifying data management across whole teal process

1. The DDL

New DDL object structure is a simple object containing:

  • code: for example "adsl <- adsl <- synthetic_cdisc_data({ arg })$adsl"
  • offline_args: named list where each element replaces { arg } in the RETURNED code.
  • ui: shiny ui object containing inputs which correspond to the { arg } element. In this case it suppose to be a textInput(id = "latest"). These inputs replace relevant { arg } in the EVALUATED code.
  • server: shiny server function which evaluates the code and return data. Server should:
    • evaluate the code with args taken from the input
    • pass evaluated env and code to the postprocess_fun
  • postprocess_fun: is the function which takes environment and code and creates a tdata object which is then passed further to teal.

It might seem that responsibilities of the server are diffficult, they are not. If you look at the username_password_server the code has only one simple eventReactive call, thanks to the ddl_run which wraps everything together and return what has been specified in the postprocess_fun. ddl_run automatically matches code, input and offline_args and executes postprocess_fun.

eventReactive(input$submit, {
      ddl_run(
        offline_args = offline_args, 
        code = code,
        postprocess_fun = postprocess_fun, 
        input = input
      )
})

custom example

Try simple example below. code and relevant ui are specified to evaluate the code. postprocess_fun returns simple list with data and code (normally we would return tdata). Example app utilizing ddl object just calls ui and server and displays server output (as ddl resolves code independently).

x <- ddl(
  code = '
  ADSL <- scda::synthetic_cdisc_data({ version })$adsl
  ADTTE <- scda::synthetic_cdisc_data({ version })$adtte
  ADRS <- scda::synthetic_cdisc_data({ version })$adrs
  ',
  
  offline_args = list(version = "i want to return this version in the code"),
  
  ui = function(id) {
    ns <- NS(id)
    tagList(
      textInput(ns("version"), label = "SCDA version", value = "latest"),
      actionButton(ns("submit"), label = "Submit")
    )
  },

  postprocess_fun = function(env_list, code) {
    list(data = env_list, code = code)
  }
)

app <- shinyApp(
  ui = fluidPage(
    fluidRow(
      column(3, h1("User Inputs"), x$ui(id = "custom_ui")),
      column(9, 
        h1("R code"), 
        verbatimTextOutput("code"),
        h1("R data"), 
        verbatimTextOutput("data")
      )
    )
  ),
  server = function(input, output, session) {
    loaded_data <- x$server(id = "custom_ui", x$offline_args, x$code, x$postprocess_fun)
    output$code <- renderPrint(cat(loaded_data()$code))
    output$data <- renderPrint(loaded_data()$data)
  }
)

shiny::runApp(app)

2. Standardize data flow in teal

In this prototype tdata is an S4 class inheriting from qenv. tdata has extra join_keys and datanames slots. Object is mutable - one can use eval_code function and it's reproducible as qenv. This object can be included in the teal_module modules and it could replace a qenv object.

tdata manipulation
library(teal.code)
library(teal.data)
library(scda)
adsl <- synthetic_cdisc_data("latest")$adsl
adtte <- synthetic_cdisc_data("latest")$adtte
tdata_obj <- cdisc_data(
  data = list(
    adsl = adsl,
    adtte = adtte
  ),
  code = '
  adsl <- synthetic_cdisc_data("latest")$adsl
  adtte <- synthetic_cdisc_data("latest")$adtte
  '
)
tdata_obj2 <- tdata_obj |> eval_code(quote(adsl <- filter(adsl, ARMCD == "ARM A")))
tdata_obj2[["adsl"]]
example custom ddl connection
x <- ddl(
  # code to be run when app user presses submit
  code = "
    conn <- open_dummy_conn(username = {username}, password = {password})
    my_data <- get_dummy_data(conn)
    close_dummy_conn()
  ",

  # arguments used for show R code
  offline_args = username_password_args(),

  # ui they wish to use for the loading data
  ui = username_password_ui,

  server = username_password_server,

  postprocess_fun = function(env_list, code) {
    cdisc_data(data = env_list, code = code)
  }
)

app <- shinyApp(
  ui = fluidPage(
    fluidRow(
      column(3, h1("User Inputs"), x$ui(id = "custom_ui")),
      column(9, h1("R code"), verbatimTextOutput("output"))
    )
  ),
  server = function(input, output, session) {
    loaded_data <- x$server(id = "custom_ui", x$offline_args, x$code, x$postprocess_fun)
    output$output <- renderPrint({
      req(loaded_data())
      loaded_data()
    })
  }
)

shiny::runApp(app)
example scda connector
x <- ddl(
  # code to be run when app user presses submit
  code = '
  ADSL <- scda::synthetic_cdisc_data({ version })$adsl
  ADTTE <- scda::synthetic_cdisc_data({ version })$adtte
  ADRS <- scda::synthetic_cdisc_data({ version })$adrs
  ',

  # ui they wish to use for the loading data
  ui = function(id) {
    ns <- NS(id)
    tagList(
      textInput(ns("version"), label = "SCDA version", value = "latest"),
      actionButton(ns("submit"), label = "Submit")
    )
  },

  postprocess_fun = function(env_list, code) {
    cdisc_data(data = env_list, code = code)
  }
)
app <- shinyApp(
  ui = fluidPage(
    fluidRow(
      column(3, h1("User Inputs"), x$ui(id = "custom_ui")),
      column(9, h1("R code"), verbatimTextOutput("output"))
    )
  ),
  server = function(input, output, session) {
    loaded_data <- x$server(id = "custom_ui", x$offline_args, x$code, x$postprocess_fun)
    output$output <- renderPrint({
      req(loaded_data())
      loaded_data()
    })
  }
)

shiny::runApp(app)

mhallal1 and others added 18 commits February 24, 2023 17:35
prototype
adsl <- synthetic_cdisc_data("latest")$adsl
adtte <- synthetic_cdisc_data("latest")$adtte
jk <- default_cdisc_join_keys(c("adsl", "adtte"))

tdata_obj <- new_tdata2(
  data = list(adsl = adsl, adtte = adtte),
  code = '
  adsl <- synthetic_cdisc_data("latest")$adsl
  adtte <- synthetic_cdisc_data("latest")$adtte
  ',
  jk 
)


tdata_obj2 <- eval_code(tdata_obj, quote(new <- data.frame(a = 1)))
tdata_obj2[["new"]]
tdata_obj2[["adsl"]]
get_code(tdata_obj2)
some fixes
up
tdata to inherit from qenv
remove unnecessary
remove tests
yup
@gogonzo gogonzo added this to the Simplify TealData milestone Mar 15, 2023
more docs
Comment thread R/tdata-constructors.r
Comment on lines +4 to +13
#' @export
setClass(
Class = "tdata",
contains = "qenv",
slots = c(join_keys = "JoinKeys", datanames = "character"),
prototype = list(
join_keys = join_keys(),
datanames = character(0)
)
)

@gogonzo gogonzo Jul 5, 2023

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ruckip imagine non-ddl cdisc_data could be just an extension of qenv. This class transports data, code and join_keys down to the modules.

@gogonzo

gogonzo commented Aug 15, 2023

Copy link
Copy Markdown
Contributor Author

Closing in favour of #161

@gogonzo gogonzo closed this Aug 15, 2023
@gogonzo
gogonzo deleted the prototype2 branch August 15, 2023 07:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants