-
-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce the new DDL #957
Conversation
This comment was marked as resolved.
This comment was marked as resolved.
I don't prefer this approach over #955 because here we introduce a But as @pawelru mentioned in here #955 (comment)
we can have #955 as basic specification of |
This comment was marked as outdated.
This comment was marked as outdated.
Is it possible to have an implementation where we do not pass a I would also consider adding masking as part of the teal_data creation step. This way, the masking is not part of the UI/Server logic. And one can theoretically mask without creating UI and just pass the masking secrets from env variable and just pass it as code passed along with `teal_data`ui <- function(id) {
ns <- NS(id)
tagList(
textInput(ns("username"), label = "Username"),
passwordInput(ns("password"), label = "Password"),
actionButton(ns("submit"), label = "Submit")
)
}
server <- function(id, data) {
env_mask <- list(
username = quote(askpass::askpass()),
password = quote(askpass::askpass())
)
moduleServer(id, function(input, output, session) {
eventReactive(input$submit, {
masked_data <- data |> eval_and_mask(env = reactiveValuesToList(input), env_mask = env_mask)
})
})
}
app <- init(
data = delayed_data(
ui = ui,
server = server,
data = teal_data(
iris = iris,
code = quote({
iris <- iris
open_conn(username = username, password = password)
ADSL <- scda::synthetic_cdisc_data("latest")$adsl
ADTTE <- scda::synthetic_cdisc_data("latest")$adtte
})
)
),
modules = modules(
teal.modules.general::tm_data_table(label = "yolo")
),
filter = teal_slices(
teal_slice(dataname = "ADSL", varname = "AGE", selected = c(18, 60)),
teal_slice(dataname = "inexisting", varname = "AGE")
)
)
runApp(app) |
Now, yes, just specify codeoptions(teal.log_level = "TRACE", teal.show_js_log = TRUE)
library(scda)
pkgload::load_all("teal")
ui <- function(id) {
ns <- NS(id)
tagList(
textInput(ns("username"), label = "Username"),
passwordInput(ns("password"), label = "Password"),
actionButton(ns("submit"), label = "Submit")
)
}
server <- function(id) {
code <- quote({
cat("\nusername: ", username, "\npassword: ", password) # mock connection function
ADSL <- scda::synthetic_cdisc_data("latest")$adsl
ADTTE <- scda::synthetic_cdisc_data("latest")$adtte
})
env_mask <- list(
username = quote(askpass::askpass()),
password = quote(askpass::askpass())
)
moduleServer(id, function(input, output, session) {
eventReactive(input$submit, {
masked_data <- teal_data() |> eval_and_mask(code = code, env = reactiveValuesToList(input), env_mask = env_mask)
})
})
}
app <- init(
data = list(ui = ui, server = server),
modules = modules(
teal.modules.general::tm_data_table(label = "yolo")
),
filter = teal_slices(
teal_slice(dataname = "ADSL", varname = "AGE", selected = c(18, 60)),
teal_slice(dataname = "inexisting", varname = "AGE")
)
)
runApp(app)
|
Here is basic example with app using app codeoptions(teal.log_level = "TRACE", teal.show_js_log = TRUE)
library(scda)
pkgload::load_all("teal")
ui <- function(id) {
ns <- NS(id)
tagList(
textInput(ns("username"), label = "Username"),
passwordInput(ns("password"), label = "Password"),
actionButton(ns("submit"), label = "Submit")
)
}
server <- function(id) {
code <- quote({
cat("\nusername: ", username, "\npassword: ", password) # mock connection function
ADSL <- scda::synthetic_cdisc_data("latest")$adsl
ADTTE <- scda::synthetic_cdisc_data("latest")$adtte
})
env_mask <- list(
username = quote(askpass::askpass()),
password = quote(askpass::askpass())
)
moduleServer(id, function(input, output, session) {
eventReactive(input$submit, {
masked_data <- teal_data() |> eval_and_mask(code = code, env = reactiveValuesToList(input), env_mask = env_mask)
})
})
}
app <- init(
data = list(ui = ui, server = server),
modules = modules(
teal.modules.general::tm_data_table(label = "yolo")
),
filter = teal_slices(
teal_slice(dataname = "ADSL", varname = "AGE", selected = c(18, 60)),
teal_slice(dataname = "inexisting", varname = "AGE")
)
)
runApp(app)
|
I think there is an agreement that I see three ways out:
|
R/data-ddl-utils.R
Outdated
|
||
#' Convenience wrapper for ddl | ||
#' @export # todo: do we want to export this? | ||
ddl <- function(code, input_mask, ui, server) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be used in this way
app code
options(teal.log_level = "TRACE", teal.show_js_log = TRUE)
library(scda)
pkgload::load_all("teal")
ui <- function(id) {
ns <- NS(id)
tagList(
textInput(ns("username"), label = "Username"),
passwordInput(ns("password"), label = "Password"),
actionButton(ns("submit"), label = "Submit")
)
}
server <- function(id, code, input_mask) {
moduleServer(id, function(input, output, session) {
eventReactive(input$submit, {
masked_data <- teal_data() |> eval_and_mask(code = code, input = input, input_mask = input_mask)
})
})
}
data <- ddl(
code = quote({
open_conn(username = username, password = password)
ADSL <- scda::synthetic_cdisc_data("latest")$adsl
ADTTE <- scda::synthetic_cdisc_data("latest")$adtte
}),
input_mask = list(
username = quote(askpass::askpass()),
password = quote(askpass::askpass())
),
ui = ui,
server = server
)
app <- init(
data = data,
modules = modules(
teal.modules.general::tm_data_table(label = "yolo")
),
filter = teal_slices(
teal_slice(dataname = "ADSL", varname = "AGE", selected = c(18, 60)),
teal_slice(dataname = "inexisting", varname = "AGE")
)
)
runApp(app)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly, I think the same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can export delayed_data (name to decide) and expose "delayed data loading" in the documentation as an extension of delayed_data
my preference would be exporting delayed_data
as it might be more helpful to understand the class and concept itself to user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure why I can not see my ques above so pasting here again,
Question: Do we require the function ddl()? It only utilize
delayed_data
with an additional argument . Perhaps it would be more suitable as additonal example in documentation or in the vignette?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my preference would be exporting delayed_data as it might be more helpful to understand the class and concept itself to user.
I have the same opinion. Besides, delayed_data
might have much wider application
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't the fan of teal_transform
which is now delayed_data
. But I guess if this solves the purpose and has the most benefits I think we should expose. From the other hand DDL sounds like a very specific use-case, so I would be fine with this as well
We stay with list(ui, server) and we export only eval_and_mask(). ddl case will be exposed only in the vignette as a special case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think exposing makes it easier to understand as there is server_formals
, extra_args
, server_args
, extra_formals
. If app developer understand that much of R and Shiny, would he be able to craft his own tool for his needs, instead of using teal? Even though I vote to expose
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is server_formals, extra_args, server_args, extra_formals
extra_args
and extra_formals
are just helpful to print relevant error message. There are server
formals and ...
which need to be matched. We need to throw meaningful message to alert that they don't match ;]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think dark smog clouds has overcome my brain today :)
#' `R6` object as returned by [teal.data::cdisc_data()], [teal.data::teal_data()], | ||
#' [teal.data::cdisc_dataset()], [teal.data::dataset()], [teal.data::dataset_connector()] or | ||
#' [teal.data::cdisc_dataset_connector()] or a single `data.frame` or a `MultiAssayExperiment` | ||
#' [teal.data::cdisc_dataset_connector()] or [teal::teal_data_module()] or a single `data.frame` or | ||
#' a `MultiAssayExperiment` | ||
#' or a list of the previous objects or function returning a named list. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to add:
or teal_data_module
object created by teal_data_module()
(the latter with link)
# check teal_modules against datanames | ||
if (inherits(modules, "teal_modules")) { | ||
sapply(modules$children, function(module) recursive_check_datanames(module, datanames = datanames)) | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just for safety -> add: else if inherits modules "teal_module" ... else stop(...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added assert on modules
to be teal_modules
- it is enough as modules can't contain anything else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. I am done for this round. Please ping me after you finish pushing corrections.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is good. Thank you. Let's take hashing discussion elsewhere not to block everything that depends on this.
Part of #937
Closes #895
Another variation for
ddl
. Key changes:teal::init
additionally acceptslist(ui = , server = )
. Both only withid
argument.delayed_data()
function createslist(ui=, server=)
and allows to callserver
function with additional arguments.delayed_data
accepts additional arguments in...
and they are used inserver()
evaluation. These...
must be named and matchingserver
formals.eval_and_mask(code, input, input_mask)
function to support evaluation-with-masking. We need to think about naming of a function and its arguments.eval_and_mask
is used in the similar way aseval_code
andwithin
I decided to not useinput$xyz
for masking butxyz
instead. Please share your feelings about this.App code