Skip to content
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

rename qenv #733

Merged
merged 21 commits into from
Sep 22, 2022
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ Collate:
'modules_debugging.R'
'reporter_previewer_module.R'
'show_rcode_modal.R'
'tdata.R'
'teal.R'
'utils.R'
'validations.R'
Expand Down
9 changes: 9 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Generated by roxygen2: do not edit by hand

S3method(get_code,tdata)
S3method(get_join_keys,default)
S3method(get_join_keys,tdata)
S3method(get_metadata,default)
S3method(get_metadata,tdata)
S3method(is_arg_used,"function")
S3method(is_arg_used,default)
S3method(is_arg_used,teal_module)
Expand All @@ -20,17 +25,21 @@ export(.log)
export(bookmarkableShinyApp)
export(default_filter)
export(example_module)
export(get_join_keys)
export(get_metadata)
export(get_rcode)
export(get_rcode_srv)
export(get_rcode_ui)
export(init)
export(log_app_usage)
export(module)
export(modules)
export(new_tdata)
export(reporter_previewer_module)
export(root_modules)
export(show_rcode_modal)
export(srv_teal_with_splash)
export(tdata2env)
export(ui_teal_with_splash)
export(validate_has_data)
export(validate_has_elements)
Expand Down
2 changes: 1 addition & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
### Enhancements

* Added option to choose which variables can be filtered in the filter panel by using the `filterable` attributes for the per-dataset lists in the `filter` argument of `init`.
* `teal_module` having `data` argument in its arguments will receive a list of reactive filter data with `"code"` and `"join_keys"` attributes.
* `teal_module` having `data` argument in its arguments of type `tdata`. Modules receive this object (a list of reactive filtered datasets with `"code"` and `"join_keys"` attributes).
* Updated the internals of `module_teal` to reflect changes in `teal.slice`.
* `teal_module` having `filter_panel_api` argument in its arguments will receive a `FilterPanelAPI` object.

Expand Down
1 change: 1 addition & 0 deletions R/example_module.R
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ example_module <- function(label = "example teal module") {
module(
label,
server = function(id, data) {
checkmate::assert_class(data, "tdata")
moduleServer(id, function(input, output, session) {
output$text <- renderPrint(data[[input$dataname]]())
})
Expand Down
17 changes: 10 additions & 7 deletions R/module_nested_tabs.R
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,9 @@ srv_nested_tabs.teal_module <- function(id, datasets, modules, reporter) {
reactive(modules)
}

#' Convert `FilteredData` to reactive list of data
#' Convert `FilteredData` to reactive list of datasets of the `tdata` type.
#'
#' Converts `FilteredData` object to list of data containing datasets needed for specific module.
#' Converts `FilteredData` object to `tdata` object containing datasets needed for a specific module.
#' Please note that if module needs dataset which has a parent, then parent will be also returned.
#'
#' @param module (`teal_module`) module where needed filters are taken from
Expand All @@ -255,10 +255,13 @@ srv_nested_tabs.teal_module <- function(id, datasets, modules, reporter) {
}
)

# code from previous stages
attr(data, "code") <- get_datasets_code(datanames, datasets)
metadata <- lapply(datanames, datasets$get_metadata)
names(metadata) <- datanames

# join_keys
attr(data, "join_keys") <- datasets$get_join_keys()
data
new_tdata(
data,
reactive(get_datasets_code(datanames, datasets)),
datasets$get_join_keys(),
metadata
)
}
18 changes: 8 additions & 10 deletions R/modules_debugging.R
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
#' and avoids session restarts!
#'
#' @param label `character` label of module
#' @param active_datanames `character vector` datanames shown in filter panel;
#' can be `"all"` to mean all available datasets
#' @keywords internal
#'
#' @examples
Expand All @@ -34,19 +32,19 @@
#' ),
#' header = "Simple teal app"
#' )
#' \dontrun{
#' shinyApp(app$ui, app$server)
#' if (interactive()) {
#' shinyApp(app$ui, app$server)
#' }
filter_calls_module <- function(label = "Filter Calls Module", active_datanames = "all") { # nolint
filter_calls_module <- function(label = "Filter Calls Module") { # nolint
checkmate::assert_string(label)
checkmate::check_character(active_datanames, min.len = 1, any.missing = FALSE)

module(
label = label,
server = function(input, output, session, datasets) {
server = function(input, output, session, data) {
checkmate::assert_class(data, "tdata")

output$filter_calls <- renderText({
active_datanames <- datasets$handle_active_datanames(active_datanames)
teal.slice::get_filter_expr(datasets, datanames = active_datanames)
get_code(data)
})
},
ui = function(id, ...) {
Expand All @@ -56,6 +54,6 @@ filter_calls_module <- function(label = "Filter Calls Module", active_datanames
verbatimTextOutput(ns("filter_calls"))
)
},
filters = active_datanames
filters = "all"
)
}
131 changes: 131 additions & 0 deletions R/tdata.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#' Create a new `tdata` object which contains
#'
#' - `data` a `reactive` list of data.frames (or `MultiAssayExperiment`) with attributes
#' i) `code` (`reactive`) containing code used to generate the data
#' and ii) join_keys (`JoinKeys`) containing the relationships between
#' the data iii) metadata (`named list`) containing any metadata associated with
#' the data frames
#'
#' @param data A `named list` of `data.frames` (or `MultiAssayExperiment`)
#' which optionally can be `reactive`.
#' Inside this object all of these items will be made `reactive`.
#' @param code A `character` (or `reactive` which evaluates to a `character`) containing
#' the code used to generate the data. This should be `reactive` if the code is changing
#' during a reactive context (e.g. if filtering changes the code). Inside this
#' object `code` will be made reactive
#' @param join_keys A `teal.data::JoinKeys` object containing relationships between the
#' datasets.
#' @param metadata A `named list` each element contains a list of metadata about the named data.frame
#' Each element of these list should be atomic and length one.
#' @return A `tdata` object
#' @examples
#'
#' data <- new_tdata(
#' data = list(iris = iris, mtcars = reactive(mtcars), dd = data.frame(x = 1:10)),
#' code = "iris <- iris
#' mtcars <- mtcars
#' dd <- data.frame(x = 1:10)",
#' metadata = list(dd = list(author = "NEST"), iris = list(version = 1))
#' )
#'
#' # Extract a data.frame
#' isolate(data[["iris"]]())
#'
#' @export
Copy link
Contributor

Choose a reason for hiding this comment

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

some tests for these functions will be nice.
Please add roxygen2 docs as are exported.

tdata2env could give bad error outside the reactive context, should we use isolate or add some validation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yup all coming 👍

new_tdata <- function(data, code = "", join_keys = NULL, metadata = NULL) {
checkmate::assert_list(
data,
any.missing = FALSE, names = "unique",
types = c("data.frame", "reactive", "MultiAssayExperiment")
)
checkmate::assert_class(join_keys, "JoinKeys", null.ok = TRUE)
checkmate::assert_multi_class(code, c("character", "reactive"))
nikolas-burkoff marked this conversation as resolved.
Show resolved Hide resolved

checkmate::assert_list(metadata, names = "unique", null.ok = TRUE)
checkmate::assert_subset(names(metadata), names(data))
for (m in metadata) teal.data::validate_metadata(m)

if (is.reactive(code)) {
isolate(checkmate::assert_class(code(), "character"))
}

#create reactive data.frames
for (x in names(data)) {
if (!is.reactive(data[[x]])) {
data[[x]] <- do.call(reactive, list(as.name(x)), envir = list2env(data[x]))
} else {
isolate(checkmate::assert_multi_class(data[[x]](), c("data.frame", "MultiAssayExperiment")))
}
}

# set attributes
attr(data, "code") <- if (is.reactive(code)) code else reactive(code)
attr(data, "join_keys") <- join_keys
attr(data, "metadata") <- metadata

# set class
class(data) <- c("tdata", class(data))
data
}

#' Function to convert a `tdata` object to an `environment`
#' Any `reactives` inside `tdata` are first evaluated
#' @param `data` a `tdata` object
#' @return an `environment`
#' @examples
#'
#' data <- new_tdata(
#' data = list(iris = iris, mtcars = reactive(mtcars)),
#' code = "iris <- iris
#' mtcars = mtcars"
#' )
#'
#' my_env <- isolate(tdata2env(data))
#'
#' @export
tdata2env <- function(data) { # nolint
checkmate::assert_class(data, "tdata")
list2env(lapply(data, function(x) if (is.reactive(x)) x() else x))
}

#' @export
get_code.tdata <- function(data) {
# note teal.data which teal depends on defines the get_code method
attr(data, "code")()
}

#' @export
get_join_keys <- function(data) {
UseMethod("get_join_keys", data)
}


#' @export
get_join_keys.tdata <- function(data) {
attr(data, "join_keys")
}

#' @export
get_join_keys.default <- function(data) {
stop("get_join_keys function not implemented for this object")
}

#' @export
get_metadata <- function(data, dataname) {
checkmate::assert_string(dataname)
UseMethod("get_metadata", data)
}

#' @export
get_metadata.tdata <- function(data, dataname) {
metadata <- attr(data, "metadata")
if (is.null(metadata)) {
return(NULL)
}
metadata[[dataname]]
}

#' @export
get_metadata.default <- function(data, dataname) {
stop("get_metadata function not implemented for this object")
}
4 changes: 2 additions & 2 deletions man/dot-datasets_to_data.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 3 additions & 6 deletions man/filter_calls_module.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 50 additions & 0 deletions man/new_tdata.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions man/tdata2env.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tests/testthat/test-module_nested_tabs.R
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ testthat::test_that(".datasets_to_data returns required attributes", {

# code
testthat::expect_equal(
attr(data, "code")[1],
isolate(attr(data, "code")()[1]),
"d1 <- data.frame(id = 1:5, pk = c(2, 3, 2, 1, 4), val = 1:5)\nd2 <- data.frame(id = 1:5, value = 1:5)\n\n"
)
})
Expand Down
Loading